Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:41

0001 /*
0002  * cpu.c  - This file contains implementation of C function to
0003  *          instantiate IDT entries. More detailled information can be found
0004  *      on Intel site and more precisely in the following book :
0005  *
0006  *      Pentium Processor family
0007  *      Developper's Manual
0008  *
0009  *      Volume 3 : Architecture and Programming Manual
0010  *
0011  * Copyright (C) 1998  Eric Valette (valette@crf.canon.fr)
0012  *                     Canon Centre Recherche France.
0013  *
0014  *  The license and distribution terms for this file may be
0015  *  found in the file LICENSE in this distribution or at
0016  *  http://www.rtems.org/license/LICENSE.
0017  */
0018 
0019 #include <rtems/score/cpu.h>
0020 #include <bsp/irq.h>
0021 #include <bsp/tblsizes.h>
0022 
0023 /*
0024  * This locking is not enough if IDT is changed at runtime
0025  * and entry can be changed for vector which is enabled
0026  * at change time. But such use is broken anyway.
0027  * Protect code only against concurrent changes.
0028  * Even that is probably unnecessary if different
0029  * entries are changed concurrently.
0030  */
0031 RTEMS_INTERRUPT_LOCK_DEFINE( static, rtems_idt_access_lock, "rtems_idt_access_lock" );
0032 
0033 static rtems_raw_irq_connect_data*  raw_irq_table;
0034 static rtems_raw_irq_connect_data   default_raw_irq_entry;
0035 static interrupt_gate_descriptor    default_idt_entry;
0036 static rtems_raw_irq_global_settings*   local_settings;
0037 
0038 void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry,
0039                        rtems_raw_irq_hdl hdl)
0040 {
0041     idtEntry->low_offsets_bits  = (((unsigned) hdl) & 0xffff);
0042     idtEntry->segment_selector  = i386_get_cs();
0043     idtEntry->fixed_value_bits  = 0;
0044     idtEntry->gate_type     = 0xe;
0045     idtEntry->privilege     = 0;
0046     idtEntry->present       = 1;
0047     idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff);
0048 }
0049 
0050 rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index)
0051 {
0052     uint32_t                    hdl;
0053     interrupt_gate_descriptor*  idt_entry_tbl;
0054     unsigned                    limit;
0055 
0056     i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
0057 
0058     /* Convert limit into number of entries */
0059     limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
0060 
0061     if(index >= limit) {
0062         return 0;
0063     }
0064 
0065     hdl = (idt_entry_tbl[index].low_offsets_bits |
0066           (idt_entry_tbl[index].high_offsets_bits << 16));
0067     return (rtems_raw_irq_hdl) hdl;
0068 }
0069 
0070 int i386_set_idt_entry  (const rtems_raw_irq_connect_data* irq)
0071 {
0072     interrupt_gate_descriptor*  idt_entry_tbl;
0073     unsigned            limit;
0074     rtems_interrupt_lock_context lock_context;
0075 
0076     i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
0077 
0078     /* Convert limit into number of entries */
0079     limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
0080 
0081     if (irq->idtIndex >= limit) {
0082       return 0;
0083     }
0084     /*
0085      * Check if default handler is actually connected. If not issue an error.
0086      * You must first get the current handler via i386_get_current_idt_entry
0087      * and then disconnect it using i386_delete_idt_entry.
0088      * RATIONALE : to always have the same transition by forcing the user
0089      * to get the previous handler before accepting to disconnect.
0090      */
0091     if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
0092       return 0;
0093     }
0094 
0095     rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
0096 
0097     raw_irq_table [irq->idtIndex] = *irq;
0098     create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
0099     if (irq->on)
0100       irq->on(irq);
0101 
0102     rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
0103     return 1;
0104 }
0105 
0106 void _CPU_ISR_install_vector (uint32_t vector,
0107                   CPU_ISR_handler hdl,
0108                   CPU_ISR_handler * oldHdl)
0109 {
0110     interrupt_gate_descriptor*  idt_entry_tbl;
0111     unsigned            limit;
0112     interrupt_gate_descriptor   new;
0113     rtems_interrupt_lock_context lock_context;
0114 
0115     i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
0116 
0117     /* Convert limit into number of entries */
0118     limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
0119 
0120     if (vector >= limit) {
0121       return;
0122     }
0123     rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
0124     *oldHdl = (CPU_ISR_handler) (idt_entry_tbl[vector].low_offsets_bits |
0125     (idt_entry_tbl[vector].high_offsets_bits << 16));
0126 
0127     create_interrupt_gate_descriptor(&new,  hdl);
0128     idt_entry_tbl[vector] = new;
0129 
0130     rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
0131 }
0132 
0133 int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
0134 {
0135     interrupt_gate_descriptor*  idt_entry_tbl;
0136     unsigned            limit;
0137 
0138     i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
0139 
0140     /* Convert limit into number of entries */
0141     limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
0142 
0143     if (irq->idtIndex >= limit) {
0144       return 0;
0145     }
0146     raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
0147 
0148     *irq = raw_irq_table [irq->idtIndex];
0149 
0150     return 1;
0151 }
0152 
0153 int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
0154 {
0155     interrupt_gate_descriptor*  idt_entry_tbl;
0156     unsigned            limit;
0157     rtems_interrupt_lock_context lock_context;
0158 
0159     i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
0160 
0161     /* Convert limit into number of entries */
0162     limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
0163 
0164     if (irq->idtIndex >= limit) {
0165       return 0;
0166     }
0167     /*
0168      * Check if handler passed is actually connected. If not issue an error.
0169      * You must first get the current handler via i386_get_current_idt_entry
0170      * and then disconnect it using i386_delete_idt_entry.
0171      * RATIONALE : to always have the same transition by forcing the user
0172      * to get the previous handler before accepting to disconnect.
0173      */
0174     if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
0175       return 0;
0176     }
0177     rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
0178 
0179     idt_entry_tbl[irq->idtIndex] = default_idt_entry;
0180 
0181     if (irq->off)
0182       irq->off(irq);
0183 
0184     raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
0185     raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
0186 
0187     rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
0188 
0189     return 1;
0190 }
0191 
0192 /*
0193  * Caution this function assumes the IDTR has been already set.
0194  */
0195 int i386_init_idt (rtems_raw_irq_global_settings* config)
0196 {
0197     unsigned            limit;
0198     unsigned            i;
0199     rtems_interrupt_lock_context lock_context;
0200     interrupt_gate_descriptor*  idt_entry_tbl;
0201 
0202     i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
0203 
0204     /* Convert limit into number of entries */
0205     limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
0206 
0207     if (config->idtSize != limit) {
0208       return 0;
0209     }
0210     /*
0211      * store various accelarators
0212      */
0213     raw_irq_table       = config->rawIrqHdlTbl;
0214     local_settings      = config;
0215     default_raw_irq_entry   = config->defaultRawEntry;
0216 
0217     rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
0218 
0219     create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
0220 
0221     for (i=0; i < limit; i++) {
0222       interrupt_gate_descriptor new;
0223       create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
0224       idt_entry_tbl[i] = new;
0225       if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
0226     raw_irq_table[i].on(&raw_irq_table[i]);
0227       }
0228       else {
0229     raw_irq_table[i].off(&raw_irq_table[i]);
0230       }
0231     }
0232     rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
0233 
0234     return 1;
0235 }
0236 
0237 int i386_get_idt_config (rtems_raw_irq_global_settings** config)
0238 {
0239   *config = local_settings;
0240   return 1;
0241 }
0242 
0243 uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
0244                              segment_descriptors* sd)
0245 {
0246     uint16_t                gdt_limit;
0247     uint16_t                tmp_segment = 0;
0248     segment_descriptors*    gdt_entry_tbl;
0249     uint8_t                 present;
0250 
0251     i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
0252 
0253     if (segment_selector_index >= (gdt_limit+1)/8) {
0254       /* index to GDT table out of bounds */
0255       return 0;
0256     }
0257     if (segment_selector_index == 0) {
0258       /* index 0 is not usable */
0259       return 0;
0260     }
0261 
0262     /* put prepared descriptor into the GDT */
0263     present = sd->present;
0264     sd->present = 0;
0265     gdt_entry_tbl[segment_selector_index].present = 0;
0266     RTEMS_COMPILER_MEMORY_BARRIER();
0267     gdt_entry_tbl[segment_selector_index] = *sd;
0268     RTEMS_COMPILER_MEMORY_BARRIER();
0269     gdt_entry_tbl[segment_selector_index].present = present;
0270     sd->present = present;
0271     /*
0272      * Now, reload all segment registers so that the possible changes takes effect.
0273      */
0274     __asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
0275                   "movw %%es,%0 ; movw %0,%%es\n\t"
0276                   "movw %%fs,%0 ; movw %0,%%fs\n\t"
0277                   "movw %%gs,%0 ; movw %0,%%gs\n\t"
0278                   "movw %%ss,%0 ; movw %0,%%ss"
0279                    : "=r" (tmp_segment)
0280                    : "0"  (tmp_segment)
0281                  );
0282     return 1;
0283 }
0284 
0285 void i386_fill_segment_desc_base(uint32_t base,
0286                                  segment_descriptors* sd)
0287 {
0288     sd->base_address_15_0  = base & 0xffff;
0289     sd->base_address_23_16 = (base >> 16) & 0xff;
0290     sd->base_address_31_24 = (base >> 24) & 0xff;
0291 }
0292 
0293 void i386_fill_segment_desc_limit(uint32_t limit,
0294                                   segment_descriptors* sd)
0295 {
0296     sd->granularity = 0;
0297     if (limit > 65535) {
0298       sd->granularity = 1;
0299       limit /= 4096;
0300     }
0301     sd->limit_15_0  = limit & 0xffff;
0302     sd->limit_19_16 = (limit >> 16) & 0xf;
0303 }
0304 
0305 /*
0306  * Caution this function assumes the GDTR has been already set.
0307  */
0308 uint32_t i386_set_gdt_entry (uint16_t segment_selector_index, uint32_t base,
0309                              uint32_t limit)
0310 {
0311     segment_descriptors     gdt_entry;
0312     memset(&gdt_entry, 0, sizeof(gdt_entry));
0313 
0314     i386_fill_segment_desc_limit(limit, &gdt_entry);
0315     i386_fill_segment_desc_base(base, &gdt_entry);
0316     /*
0317      * set up descriptor type (this may well becomes a parameter if needed)
0318      */
0319     gdt_entry.type              = 2;    /* Data R/W */
0320     gdt_entry.descriptor_type   = 1;    /* Code or Data */
0321     gdt_entry.privilege         = 0;    /* ring 0 */
0322     gdt_entry.present           = 1;    /* not present */
0323 
0324     /*
0325      * Now, reload all segment registers so the limit takes effect.
0326      */
0327     return i386_raw_gdt_entry(segment_selector_index, &gdt_entry);
0328 }
0329 
0330 uint16_t i386_next_empty_gdt_entry ()
0331 {
0332     uint16_t                gdt_limit;
0333     segment_descriptors*    gdt_entry_tbl;
0334     /* initial amount of filled descriptors */
0335     static uint16_t         segment_selector_index = NUM_SYSTEM_GDT_DESCRIPTORS - 1;
0336 
0337     segment_selector_index += 1;
0338     i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
0339     if (segment_selector_index >= (gdt_limit+1)/8) {
0340       return 0;
0341     }
0342     return segment_selector_index;
0343 }
0344 
0345 uint16_t i386_cpy_gdt_entry(uint16_t segment_selector_index,
0346                             segment_descriptors* struct_to_fill)
0347 {
0348     uint16_t                gdt_limit;
0349     segment_descriptors*    gdt_entry_tbl;
0350 
0351     i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
0352 
0353     if (segment_selector_index >= (gdt_limit+1)/8) {
0354       return 0;
0355     }
0356 
0357     *struct_to_fill = gdt_entry_tbl[segment_selector_index];
0358     return segment_selector_index;
0359 }
0360 
0361 segment_descriptors* i386_get_gdt_entry(uint16_t segment_selector_index)
0362 {
0363     uint16_t                gdt_limit;
0364     segment_descriptors*    gdt_entry_tbl;
0365 
0366     i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
0367 
0368     if (segment_selector_index >= (gdt_limit+1)/8) {
0369       return 0;
0370     }
0371     return &gdt_entry_tbl[segment_selector_index];
0372 }
0373 
0374 uint32_t i386_limit_gdt_entry(segment_descriptors* gdt_entry)
0375 {
0376     uint32_t lim = (gdt_entry->limit_15_0 + (gdt_entry->limit_19_16<<16));
0377     if (gdt_entry->granularity) {
0378       return lim*4096+4095;
0379     }
0380     return lim;
0381 }