source: rtems/c/src/lib/libbsp/i386/shared/irq/idt.c @ 6b54dcb

Last change on this file since 6b54dcb was 6b54dcb, checked in by Pavel Pisa <pisa@…>, on Oct 12, 2016 at 7:40:41 AM

bsps/i386: replace global interrupt disable by SMP build supporting locking.

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