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

4.104.114.84.95
Last change on this file since c83c325 was c83c325, checked in by Joel Sherrill <joel.sherrill@…>, on Sep 12, 2007 at 3:16:02 PM

2007-09-12 Joel Sherrill <joel.sherrill@…>

PR 1257/bsps

  • shared/irq/idt.c, shared/irq/irq.c, shared/irq/irq_init.c: Code outside of cpukit should use the public API for rtems_interrupt_disable/rtems_interrupt_enable. By bypassing the public API and directly accessing _CPU_ISR_Disable and _CPU_ISR_Enable, they were bypassing the compiler memory barrier directive which could lead to problems. This patch also changes the type of the variable passed into these routines and addresses minor style issues.
  • Property mode set to 100644
File size: 8.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 found in the file LICENSE in this distribution or at
16 *  http://www.rtems.com/license/LICENSE.
17 *
18 * $Id$
19 */
20
21#include <libcpu/cpu.h>
22#include <bsp/irq.h>
23
24static rtems_raw_irq_connect_data*      raw_irq_table;
25static rtems_raw_irq_connect_data       default_raw_irq_entry;
26static interrupt_gate_descriptor        default_idt_entry;
27static rtems_raw_irq_global_settings*   local_settings;
28
29void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry,
30                                       rtems_raw_irq_hdl hdl)
31{
32    idtEntry->low_offsets_bits  = (((unsigned) hdl) & 0xffff);
33    idtEntry->segment_selector  = i386_get_cs();
34    idtEntry->fixed_value_bits  = 0;
35    idtEntry->gate_type         = 0xe;
36    idtEntry->privilege         = 0;
37    idtEntry->present           = 1;
38    idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff);
39}
40
41rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index)
42{
43    uint32_t                    hdl;
44    interrupt_gate_descriptor*  idt_entry_tbl;
45    unsigned                    limit;
46
47    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
48
49    /* Convert limit into number of entries */
50    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
51
52    if(index >= limit) {
53        return 0;
54    }
55
56    hdl = (idt_entry_tbl[index].low_offsets_bits |
57          (idt_entry_tbl[index].high_offsets_bits << 16));
58    return (rtems_raw_irq_hdl) hdl;
59}
60
61int i386_set_idt_entry  (const rtems_raw_irq_connect_data* irq)
62{
63    interrupt_gate_descriptor*  idt_entry_tbl;
64    unsigned                    limit;
65    rtems_interrupt_level       level;
66
67    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
68
69    /* Convert limit into number of entries */
70    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
71
72    if (irq->idtIndex >= limit) {
73      return 0;
74    }
75    /*
76     * Check if default handler is actually connected. If not issue an error.
77     * You must first get the current handler via i386_get_current_idt_entry
78     * and then disconnect it using i386_delete_idt_entry.
79     * RATIONALE : to always have the same transition by forcing the user
80     * to get the previous handler before accepting to disconnect.
81     */
82    if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
83      return 0;
84    }
85
86    rtems_interrupt_disable(level);
87
88    raw_irq_table [irq->idtIndex] = *irq;
89    create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
90    irq->on(irq);
91
92    rtems_interrupt_enable(level);
93    return 1;
94}
95
96void _CPU_ISR_install_vector (uint32_t vector,
97                              proc_ptr hdl,
98                              proc_ptr * oldHdl)
99{
100    interrupt_gate_descriptor*  idt_entry_tbl;
101    unsigned                    limit;
102    interrupt_gate_descriptor   new;
103    rtems_interrupt_level       level;
104
105    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
106
107    /* Convert limit into number of entries */
108    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
109
110    if (vector >= limit) {
111      return;
112    }
113    rtems_interrupt_disable(level);
114    * ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits |
115        (idt_entry_tbl[vector].high_offsets_bits << 16);
116
117    create_interrupt_gate_descriptor(&new,  hdl);
118    idt_entry_tbl[vector] = new;
119
120    rtems_interrupt_enable(level);
121}
122
123int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
124{
125    interrupt_gate_descriptor*  idt_entry_tbl;
126    unsigned                    limit;
127
128    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
129
130    /* Convert limit into number of entries */
131    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
132
133    if (irq->idtIndex >= limit) {
134      return 0;
135    }
136    raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
137
138    *irq = raw_irq_table [irq->idtIndex];
139
140    return 1;
141}
142
143int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
144{
145    interrupt_gate_descriptor*  idt_entry_tbl;
146    unsigned                    limit;
147    rtems_interrupt_level       level;
148
149    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
150
151    /* Convert limit into number of entries */
152    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
153
154    if (irq->idtIndex >= limit) {
155      return 0;
156    }
157    /*
158     * Check if handler passed is actually connected. If not issue an error.
159     * You must first get the current handler via i386_get_current_idt_entry
160     * and then disconnect it using i386_delete_idt_entry.
161     * RATIONALE : to always have the same transition by forcing the user
162     * to get the previous handler before accepting to disconnect.
163     */
164    if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
165      return 0;
166    }
167    rtems_interrupt_disable(level);
168
169    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
170
171    irq->off(irq);
172
173    raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
174    raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
175
176    rtems_interrupt_enable(level);
177
178    return 1;
179}
180
181/*
182 * Caution this function assumes the IDTR has been already set.
183 */
184int i386_init_idt (rtems_raw_irq_global_settings* config)
185{
186    unsigned                    limit;
187    unsigned                    i;
188    rtems_interrupt_level       level;
189    interrupt_gate_descriptor*  idt_entry_tbl;
190
191    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
192
193    /* Convert limit into number of entries */
194    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
195
196    if (config->idtSize != limit) {
197      return 0;
198    }
199    /*
200     * store various accelarators
201     */
202    raw_irq_table               = config->rawIrqHdlTbl;
203    local_settings              = config;
204    default_raw_irq_entry       = config->defaultRawEntry;
205
206    rtems_interrupt_disable(level);
207
208    create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
209
210    for (i=0; i < limit; i++) {
211      interrupt_gate_descriptor new;
212      create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
213      idt_entry_tbl[i] = new;
214      if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
215        raw_irq_table[i].on(&raw_irq_table[i]);
216      }
217      else {
218        raw_irq_table[i].off(&raw_irq_table[i]);
219      }
220    }
221    rtems_interrupt_enable(level);
222
223    return 1;
224}
225
226int i386_get_idt_config (rtems_raw_irq_global_settings** config)
227{
228  *config = local_settings;
229  return 1;
230}
231
232/*
233 * Caution this function assumes the GDTR has been already set.
234 */
235int i386_set_gdt_entry (unsigned short segment_selector, unsigned base,
236                        unsigned limit)
237{
238    unsigned                    gdt_limit;
239    unsigned short              tmp_segment = 0;
240    unsigned int                limit_adjusted;
241    segment_descriptors*        gdt_entry_tbl;
242
243    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
244
245    if (segment_selector > limit) {
246      return 0;
247    }
248    /*
249     * set up limit first
250     */
251    limit_adjusted = limit;
252    if ( limit > 4095 ) {
253      gdt_entry_tbl[segment_selector].granularity = 1;
254      limit_adjusted /= 4096;
255    }
256    gdt_entry_tbl[segment_selector].limit_15_0  = limit_adjusted & 0xffff;
257    gdt_entry_tbl[segment_selector].limit_19_16 = (limit_adjusted >> 16) & 0xf;
258    /*
259     * set up base
260     */
261    gdt_entry_tbl[segment_selector].base_address_15_0  = base & 0xffff;
262    gdt_entry_tbl[segment_selector].base_address_23_16 = (base >> 16) & 0xff;
263    gdt_entry_tbl[segment_selector].base_address_31_24 = (base >> 24) & 0xff;
264    /*
265     * set up descriptor type (this may well becomes a parameter if needed)
266     */
267    gdt_entry_tbl[segment_selector].type                = 2;    /* Data R/W */
268    gdt_entry_tbl[segment_selector].descriptor_type     = 1;    /* Code or Data */
269    gdt_entry_tbl[segment_selector].privilege           = 0;    /* ring 0 */
270    gdt_entry_tbl[segment_selector].present             = 1;    /* not present */
271
272    /*
273     * Now, reload all segment registers so the limit takes effect.
274     */
275
276    asm volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
277                  "movw %%es,%0 ; movw %0,%%es\n\t"
278                  "movw %%fs,%0 ; movw %0,%%fs\n\t"
279                  "movw %%gs,%0 ; movw %0,%%gs\n\t"
280                  "movw %%ss,%0 ; movw %0,%%ss"
281                   : "=r" (tmp_segment)
282                   : "0"  (tmp_segment)
283                  );
284
285    return 1;
286}
Note: See TracBrowser for help on using the repository browser.