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

Last change on this file since 7b0c74ff was 7b0c74ff, checked in by Sebastian Huber <sebastian.huber@…>, on Jun 9, 2017 at 1:42:36 PM

i386: Support thread-local storage (TLS)

Update #2468.

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/*
2 * cpu.c  - This file contains implementation of C function to
3 *          instantiate IDT entries. More detailled information can be found
4 *          on Intel site and more precisely in the following book :
5 *
6 *              Pentium Processor family
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
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#include <rtems/score/cpu.h>
20#include <bsp/irq.h>
21#include <bsp/tblsizes.h>
22
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
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);
42    idtEntry->segment_selector  = i386_get_cs();
43    idtEntry->fixed_value_bits  = 0;
44    idtEntry->gate_type         = 0xe;
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{
52    uint32_t                    hdl;
53    interrupt_gate_descriptor*  idt_entry_tbl;
54    unsigned                    limit;
55
56    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
57
58    /* Convert limit into number of entries */
59    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
60
61    if(index >= limit) {
62        return 0;
63    }
64
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;
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;
74    rtems_interrupt_lock_context lock_context;
75
76    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
77
78    /* Convert limit into number of entries */
79    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
80
81    if (irq->idtIndex >= limit) {
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    }
94
95    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
96
97    raw_irq_table [irq->idtIndex] = *irq;
98    create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
99    if (irq->on)
100      irq->on(irq);
101
102    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
103    return 1;
104}
105
106void _CPU_ISR_install_vector (uint32_t vector,
107                              proc_ptr hdl,
108                              proc_ptr * oldHdl)
109{
110    interrupt_gate_descriptor*  idt_entry_tbl;
111    unsigned                    limit;
112    interrupt_gate_descriptor   new;
113    rtems_interrupt_lock_context lock_context;
114
115    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
116
117    /* Convert limit into number of entries */
118    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
119
120    if (vector >= limit) {
121      return;
122    }
123    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
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
130    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
131}
132
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
140    /* Convert limit into number of entries */
141    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
142
143    if (irq->idtIndex >= limit) {
144      return 0;
145    }
146    raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
147
148    *irq = raw_irq_table [irq->idtIndex];
149
150    return 1;
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;
157    rtems_interrupt_lock_context lock_context;
158
159    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
160
161    /* Convert limit into number of entries */
162    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
163
164    if (irq->idtIndex >= limit) {
165      return 0;
166    }
167    /*
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){
175      return 0;
176    }
177    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
178
179    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
180
181    if (irq->off)
182      irq->off(irq);
183
184    raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
185    raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
186
187    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
188
189    return 1;
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;
199    rtems_interrupt_lock_context lock_context;
200    interrupt_gate_descriptor*  idt_entry_tbl;
201
202    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
203
204    /* Convert limit into number of entries */
205    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
206
207    if (config->idtSize != limit) {
208      return 0;
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
217    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
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    }
232    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
233
234    return 1;
235}
236
237int i386_get_idt_config (rtems_raw_irq_global_settings** config)
238{
239  *config = local_settings;
240  return 1;
241}
242
243uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
244                             segment_descriptors* sd)
245{
246    uint16_t                gdt_limit;
247    uint16_t                tmp_segment = 0;
248    segment_descriptors*    gdt_entry_tbl;
249    uint8_t                 present;
250
251    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
252
253    if (segment_selector_index >= (gdt_limit+1)/8) {
254      /* index to GDT table out of bounds */
255      return 0;
256    }
257    if (segment_selector_index == 0) {
258      /* index 0 is not usable */
259      return 0;
260    }
261
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;
271    /*
272     * Now, reload all segment registers so that the possible changes takes effect.
273     */
274    __asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
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"
279                   : "=r" (tmp_segment)
280                   : "0"  (tmp_segment)
281                 );
282    return 1;
283}
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 */
335    static uint16_t         segment_selector_index = NUM_SYSTEM_GDT_DESCRIPTORS - 1;
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.