source: rtems/c/src/lib/libbsp/i386/shared/irq/idt.c @ 7b0c74ff

5
Last change on this file since 7b0c74ff was 7b0c74ff, checked in by Sebastian Huber <sebastian.huber@…>, on 06/09/17 at 13:42:36

i386: Support thread-local storage (TLS)

Update #2468.

  • Property mode set to 100644
File size: 11.4 KB
RevLine 
[67a2288]1/*
2 * cpu.c  - This file contains implementation of C function to
[aa9eb940]3 *          instantiate IDT entries. More detailled information can be found
[67a2288]4 *          on Intel site and more precisely in the following book :
5 *
[aa9eb940]6 *              Pentium Processor family
[67a2288]7 *              Developper's Manual
8 *
9 *              Volume 3 : Architecture and Programming Manual
10 *
11 * Copyright (C) 1998  Eric Valette (valette@crf.canon.fr)
12 *                     Canon Centre Recherche France.
13 *
14 *  The license and distribution terms for this file may be
[8c41855]15 *  found in the file LICENSE in this distribution or at
[c499856]16 *  http://www.rtems.org/license/LICENSE.
[67a2288]17 */
18
[328bd35]19#include <rtems/score/cpu.h>
[529cebf0]20#include <bsp/irq.h>
[7b0c74ff]21#include <bsp/tblsizes.h>
[67a2288]22
[6b54dcb]23/*
24 * This locking is not enough if IDT is changed at runtime
25 * and entry can be changed for vector which is enabled
26 * at change time. But such use is broken anyway.
27 * Protect code only against concurrent changes.
28 * Even that is probably unnecessary if different
29 * entries are changed concurrently.
30 */
31RTEMS_INTERRUPT_LOCK_DEFINE( static, rtems_idt_access_lock, "rtems_idt_access_lock" );
32
[67a2288]33static rtems_raw_irq_connect_data*      raw_irq_table;
34static rtems_raw_irq_connect_data       default_raw_irq_entry;
35static interrupt_gate_descriptor        default_idt_entry;
36static rtems_raw_irq_global_settings*   local_settings;
37
38void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry,
39                                       rtems_raw_irq_hdl hdl)
40{
41    idtEntry->low_offsets_bits  = (((unsigned) hdl) & 0xffff);
[6128a4a]42    idtEntry->segment_selector  = i386_get_cs();
[67a2288]43    idtEntry->fixed_value_bits  = 0;
[6128a4a]44    idtEntry->gate_type         = 0xe;
[67a2288]45    idtEntry->privilege         = 0;
46    idtEntry->present           = 1;
47    idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff);
48}
49
50rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index)
51{
[a4d3eb0d]52    uint32_t                    hdl;
[67a2288]53    interrupt_gate_descriptor*  idt_entry_tbl;
[34e7be2]54    unsigned                    limit;
[67a2288]55
56    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
[6128a4a]57
[97d7b068]58    /* Convert limit into number of entries */
59    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
[6128a4a]60
[97d7b068]61    if(index >= limit) {
62        return 0;
63    }
64
[a4d3eb0d]65    hdl = (idt_entry_tbl[index].low_offsets_bits |
66          (idt_entry_tbl[index].high_offsets_bits << 16));
67    return (rtems_raw_irq_hdl) hdl;
[67a2288]68}
69
70int i386_set_idt_entry  (const rtems_raw_irq_connect_data* irq)
71{
72    interrupt_gate_descriptor*  idt_entry_tbl;
73    unsigned                    limit;
[6b54dcb]74    rtems_interrupt_lock_context lock_context;
[67a2288]75
76    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
77
[97d7b068]78    /* Convert limit into number of entries */
79    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
80
81    if (irq->idtIndex >= limit) {
[67a2288]82      return 0;
83    }
84    /*
85     * Check if default handler is actually connected. If not issue an error.
86     * You must first get the current handler via i386_get_current_idt_entry
87     * and then disconnect it using i386_delete_idt_entry.
88     * RATIONALE : to always have the same transition by forcing the user
89     * to get the previous handler before accepting to disconnect.
90     */
91    if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
92      return 0;
93    }
[97d7b068]94
[6b54dcb]95    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[6128a4a]96
[67a2288]97    raw_irq_table [irq->idtIndex] = *irq;
98    create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
[069ed6c5]99    if (irq->on)
100      irq->on(irq);
[6128a4a]101
[6b54dcb]102    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[67a2288]103    return 1;
104}
105
[4d90b98e]106void _CPU_ISR_install_vector (uint32_t vector,
107                              proc_ptr hdl,
108                              proc_ptr * oldHdl)
[67a2288]109{
110    interrupt_gate_descriptor*  idt_entry_tbl;
111    unsigned                    limit;
112    interrupt_gate_descriptor   new;
[6b54dcb]113    rtems_interrupt_lock_context lock_context;
[6128a4a]114
[67a2288]115    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
116
[97d7b068]117    /* Convert limit into number of entries */
118    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
119
120    if (vector >= limit) {
[67a2288]121      return;
122    }
[6b54dcb]123    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[67a2288]124    * ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits |
125        (idt_entry_tbl[vector].high_offsets_bits << 16);
126
127    create_interrupt_gate_descriptor(&new,  hdl);
128    idt_entry_tbl[vector] = new;
129
[6b54dcb]130    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[67a2288]131}
[6128a4a]132
[67a2288]133int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
134{
135    interrupt_gate_descriptor*  idt_entry_tbl;
136    unsigned                    limit;
137
138    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
139
[97d7b068]140    /* Convert limit into number of entries */
141    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
142
143    if (irq->idtIndex >= limit) {
144      return 0;
[67a2288]145    }
146    raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
[6128a4a]147
[67a2288]148    *irq = raw_irq_table [irq->idtIndex];
[6128a4a]149
[97d7b068]150    return 1;
[67a2288]151}
152
153int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
154{
155    interrupt_gate_descriptor*  idt_entry_tbl;
156    unsigned                    limit;
[6b54dcb]157    rtems_interrupt_lock_context lock_context;
[6128a4a]158
[67a2288]159    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
160
[97d7b068]161    /* Convert limit into number of entries */
162    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
163
164    if (irq->idtIndex >= limit) {
165      return 0;
[67a2288]166    }
[97d7b068]167    /*
[67a2288]168     * Check if handler passed is actually connected. If not issue an error.
169     * You must first get the current handler via i386_get_current_idt_entry
170     * and then disconnect it using i386_delete_idt_entry.
171     * RATIONALE : to always have the same transition by forcing the user
172     * to get the previous handler before accepting to disconnect.
173     */
174    if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
[97d7b068]175      return 0;
[67a2288]176    }
[6b54dcb]177    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[67a2288]178
179    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
180
[069ed6c5]181    if (irq->off)
182      irq->off(irq);
[6128a4a]183
[67a2288]184    raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
[97d7b068]185    raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
[67a2288]186
[6b54dcb]187    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[6128a4a]188
[97d7b068]189    return 1;
[67a2288]190}
191
192/*
193 * Caution this function assumes the IDTR has been already set.
194 */
195int i386_init_idt (rtems_raw_irq_global_settings* config)
196{
197    unsigned                    limit;
198    unsigned                    i;
[6b54dcb]199    rtems_interrupt_lock_context lock_context;
[67a2288]200    interrupt_gate_descriptor*  idt_entry_tbl;
[6128a4a]201
[67a2288]202    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
203
[97d7b068]204    /* Convert limit into number of entries */
205    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
[6128a4a]206
[67a2288]207    if (config->idtSize != limit) {
[97d7b068]208      return 0;
[67a2288]209    }
210    /*
211     * store various accelarators
212     */
213    raw_irq_table               = config->rawIrqHdlTbl;
214    local_settings              = config;
215    default_raw_irq_entry       = config->defaultRawEntry;
216
[6b54dcb]217    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[67a2288]218
219    create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
220
221    for (i=0; i < limit; i++) {
222      interrupt_gate_descriptor new;
223      create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
224      idt_entry_tbl[i] = new;
225      if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
226        raw_irq_table[i].on(&raw_irq_table[i]);
227      }
228      else {
229        raw_irq_table[i].off(&raw_irq_table[i]);
230      }
231    }
[6b54dcb]232    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[67a2288]233
[97d7b068]234    return 1;
[67a2288]235}
236
237int i386_get_idt_config (rtems_raw_irq_global_settings** config)
238{
239  *config = local_settings;
[97d7b068]240  return 1;
[67a2288]241}
242
[74d2d940]243uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
244                             segment_descriptors* sd)
[67a2288]245{
[74d2d940]246    uint16_t                gdt_limit;
247    uint16_t                tmp_segment = 0;
248    segment_descriptors*    gdt_entry_tbl;
249    uint8_t                 present;
[67a2288]250
251    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
252
[74d2d940]253    if (segment_selector_index >= (gdt_limit+1)/8) {
254      /* index to GDT table out of bounds */
[97d7b068]255      return 0;
[67a2288]256    }
[74d2d940]257    if (segment_selector_index == 0) {
258      /* index 0 is not usable */
259      return 0;
[67a2288]260    }
261
[74d2d940]262    /* put prepared descriptor into the GDT */
263    present = sd->present;
264    sd->present = 0;
265    gdt_entry_tbl[segment_selector_index].present = 0;
266    RTEMS_COMPILER_MEMORY_BARRIER();
267    gdt_entry_tbl[segment_selector_index] = *sd;
268    RTEMS_COMPILER_MEMORY_BARRIER();
269    gdt_entry_tbl[segment_selector_index].present = present;
270    sd->present = present;
[67a2288]271    /*
[74d2d940]272     * Now, reload all segment registers so that the possible changes takes effect.
[67a2288]273     */
[00882105]274    __asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
[55b3616]275                  "movw %%es,%0 ; movw %0,%%es\n\t"
276                  "movw %%fs,%0 ; movw %0,%%fs\n\t"
277                  "movw %%gs,%0 ; movw %0,%%gs\n\t"
278                  "movw %%ss,%0 ; movw %0,%%ss"
[67a2288]279                   : "=r" (tmp_segment)
280                   : "0"  (tmp_segment)
[74d2d940]281                 );
[97d7b068]282    return 1;
[67a2288]283}
[74d2d940]284
285void i386_fill_segment_desc_base(uint32_t base,
286                                 segment_descriptors* sd)
287{
288    sd->base_address_15_0  = base & 0xffff;
289    sd->base_address_23_16 = (base >> 16) & 0xff;
290    sd->base_address_31_24 = (base >> 24) & 0xff;
291}
292
293void i386_fill_segment_desc_limit(uint32_t limit,
294                                  segment_descriptors* sd)
295{
296    sd->granularity = 0;
297    if (limit > 65535) {
298      sd->granularity = 1;
299      limit /= 4096;
300    }
301    sd->limit_15_0  = limit & 0xffff;
302    sd->limit_19_16 = (limit >> 16) & 0xf;
303}
304
305/*
306 * Caution this function assumes the GDTR has been already set.
307 */
308uint32_t i386_set_gdt_entry (uint16_t segment_selector_index, uint32_t base,
309                             uint32_t limit)
310{
311    segment_descriptors     gdt_entry;
312    memset(&gdt_entry, 0, sizeof(gdt_entry));
313
314    i386_fill_segment_desc_limit(limit, &gdt_entry);
315    i386_fill_segment_desc_base(base, &gdt_entry);
316    /*
317     * set up descriptor type (this may well becomes a parameter if needed)
318     */
319    gdt_entry.type              = 2;    /* Data R/W */
320    gdt_entry.descriptor_type   = 1;    /* Code or Data */
321    gdt_entry.privilege         = 0;    /* ring 0 */
322    gdt_entry.present           = 1;    /* not present */
323
324    /*
325     * Now, reload all segment registers so the limit takes effect.
326     */
327    return i386_raw_gdt_entry(segment_selector_index, &gdt_entry);
328}
329
330uint16_t i386_next_empty_gdt_entry ()
331{
332    uint16_t                gdt_limit;
333    segment_descriptors*    gdt_entry_tbl;
334    /* initial amount of filled descriptors */
[7b0c74ff]335    static uint16_t         segment_selector_index = NUM_SYSTEM_GDT_DESCRIPTORS - 1;
[74d2d940]336
337    segment_selector_index += 1;
338    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
339    if (segment_selector_index >= (gdt_limit+1)/8) {
340      return 0;
341    }
342    return segment_selector_index;
343}
344
345uint16_t i386_cpy_gdt_entry(uint16_t segment_selector_index,
346                            segment_descriptors* struct_to_fill)
347{
348    uint16_t                gdt_limit;
349    segment_descriptors*    gdt_entry_tbl;
350
351    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
352
353    if (segment_selector_index >= (gdt_limit+1)/8) {
354      return 0;
355    }
356
357    *struct_to_fill = gdt_entry_tbl[segment_selector_index];
358    return segment_selector_index;
359}
360
361segment_descriptors* i386_get_gdt_entry(uint16_t segment_selector_index)
362{
363    uint16_t                gdt_limit;
364    segment_descriptors*    gdt_entry_tbl;
365
366    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
367
368    if (segment_selector_index >= (gdt_limit+1)/8) {
369      return 0;
370    }
371    return &gdt_entry_tbl[segment_selector_index];
372}
373
374uint32_t i386_limit_gdt_entry(segment_descriptors* gdt_entry)
375{
376    uint32_t lim = (gdt_entry->limit_15_0 + (gdt_entry->limit_19_16<<16));
377    if (gdt_entry->granularity) {
378      return lim*4096+4095;
379    }
380    return lim;
381}
Note: See TracBrowser for help on using the repository browser.