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

4.104.114.84.95
Last change on this file since a4a5624 was f05b2ac, checked in by Ralf Corsepius <ralf.corsepius@…>, on 04/21/04 at 16:01:48

Remove duplicate white lines.

  • Property mode set to 100644
File size: 8.1 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 <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    rtems_raw_irq_hdl           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    * ((unsigned int*) &hdl) = (idt_entry_tbl[index].low_offsets_bits |
57                              (idt_entry_tbl[index].high_offsets_bits << 16));
58    return 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    unsigned int                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    _CPU_ISR_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    _CPU_ISR_Enable(level);
93    return 1;
94}
95
96void _CPU_ISR_install_vector (unsigned vector,
97                              void* hdl,
98                              void** oldHdl)
99{
100    interrupt_gate_descriptor*  idt_entry_tbl;
101    unsigned                    limit;
102    interrupt_gate_descriptor   new;
103    unsigned int                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    _CPU_ISR_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    _CPU_ISR_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    unsigned int                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    _CPU_ISR_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    _CPU_ISR_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    unsigned                    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    _CPU_ISR_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    _CPU_ISR_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.