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

5
Last change on this file since 6b54dcb was 6b54dcb, checked in by Pavel Pisa <pisa@…>, on 10/12/16 at 07:40:41

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

  • Property mode set to 100644
File size: 11.3 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>
[67a2288]21
[6b54dcb]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
[67a2288]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);
[6128a4a]41    idtEntry->segment_selector  = i386_get_cs();
[67a2288]42    idtEntry->fixed_value_bits  = 0;
[6128a4a]43    idtEntry->gate_type         = 0xe;
[67a2288]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{
[a4d3eb0d]51    uint32_t                    hdl;
[67a2288]52    interrupt_gate_descriptor*  idt_entry_tbl;
[34e7be2]53    unsigned                    limit;
[67a2288]54
55    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
[6128a4a]56
[97d7b068]57    /* Convert limit into number of entries */
58    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
[6128a4a]59
[97d7b068]60    if(index >= limit) {
61        return 0;
62    }
63
[a4d3eb0d]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;
[67a2288]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;
[6b54dcb]73    rtems_interrupt_lock_context lock_context;
[67a2288]74
75    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
76
[97d7b068]77    /* Convert limit into number of entries */
78    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
79
80    if (irq->idtIndex >= limit) {
[67a2288]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    }
[97d7b068]93
[6b54dcb]94    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[6128a4a]95
[67a2288]96    raw_irq_table [irq->idtIndex] = *irq;
97    create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
[069ed6c5]98    if (irq->on)
99      irq->on(irq);
[6128a4a]100
[6b54dcb]101    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[67a2288]102    return 1;
103}
104
[4d90b98e]105void _CPU_ISR_install_vector (uint32_t vector,
106                              proc_ptr hdl,
107                              proc_ptr * oldHdl)
[67a2288]108{
109    interrupt_gate_descriptor*  idt_entry_tbl;
110    unsigned                    limit;
111    interrupt_gate_descriptor   new;
[6b54dcb]112    rtems_interrupt_lock_context lock_context;
[6128a4a]113
[67a2288]114    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
115
[97d7b068]116    /* Convert limit into number of entries */
117    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
118
119    if (vector >= limit) {
[67a2288]120      return;
121    }
[6b54dcb]122    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[67a2288]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
[6b54dcb]129    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[67a2288]130}
[6128a4a]131
[67a2288]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
[97d7b068]139    /* Convert limit into number of entries */
140    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
141
142    if (irq->idtIndex >= limit) {
143      return 0;
[67a2288]144    }
145    raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
[6128a4a]146
[67a2288]147    *irq = raw_irq_table [irq->idtIndex];
[6128a4a]148
[97d7b068]149    return 1;
[67a2288]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;
[6b54dcb]156    rtems_interrupt_lock_context lock_context;
[6128a4a]157
[67a2288]158    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
159
[97d7b068]160    /* Convert limit into number of entries */
161    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
162
163    if (irq->idtIndex >= limit) {
164      return 0;
[67a2288]165    }
[97d7b068]166    /*
[67a2288]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){
[97d7b068]174      return 0;
[67a2288]175    }
[6b54dcb]176    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[67a2288]177
178    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
179
[069ed6c5]180    if (irq->off)
181      irq->off(irq);
[6128a4a]182
[67a2288]183    raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
[97d7b068]184    raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
[67a2288]185
[6b54dcb]186    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[6128a4a]187
[97d7b068]188    return 1;
[67a2288]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;
[6b54dcb]198    rtems_interrupt_lock_context lock_context;
[67a2288]199    interrupt_gate_descriptor*  idt_entry_tbl;
[6128a4a]200
[67a2288]201    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
202
[97d7b068]203    /* Convert limit into number of entries */
204    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
[6128a4a]205
[67a2288]206    if (config->idtSize != limit) {
[97d7b068]207      return 0;
[67a2288]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
[6b54dcb]216    rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
[67a2288]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    }
[6b54dcb]231    rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
[67a2288]232
[97d7b068]233    return 1;
[67a2288]234}
235
236int i386_get_idt_config (rtems_raw_irq_global_settings** config)
237{
238  *config = local_settings;
[97d7b068]239  return 1;
[67a2288]240}
241
[74d2d940]242uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
243                             segment_descriptors* sd)
[67a2288]244{
[74d2d940]245    uint16_t                gdt_limit;
246    uint16_t                tmp_segment = 0;
247    segment_descriptors*    gdt_entry_tbl;
248    uint8_t                 present;
[67a2288]249
250    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
251
[74d2d940]252    if (segment_selector_index >= (gdt_limit+1)/8) {
253      /* index to GDT table out of bounds */
[97d7b068]254      return 0;
[67a2288]255    }
[74d2d940]256    if (segment_selector_index == 0) {
257      /* index 0 is not usable */
258      return 0;
[67a2288]259    }
260
[74d2d940]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;
[67a2288]270    /*
[74d2d940]271     * Now, reload all segment registers so that the possible changes takes effect.
[67a2288]272     */
[00882105]273    __asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
[55b3616]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"
[67a2288]278                   : "=r" (tmp_segment)
279                   : "0"  (tmp_segment)
[74d2d940]280                 );
[97d7b068]281    return 1;
[67a2288]282}
[74d2d940]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.