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

4.115
Last change on this file since 8c41855 was 8c41855, checked in by Joel Sherrill <joel.sherrill@…>, on 01/28/11 at 20:35:38

2011-01-28 Joel Sherrill <joel.sherrilL@…>

  • shared/irq/idt.c, shared/irq/irq.c, shared/irq/irq.h, shared/irq/irq_asm.h: Fix typo where license said found in found in.
  • 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 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    if (irq->on)
91      irq->on(irq);
92
93    rtems_interrupt_enable(level);
94    return 1;
95}
96
97void _CPU_ISR_install_vector (uint32_t vector,
98                              proc_ptr hdl,
99                              proc_ptr * oldHdl)
100{
101    interrupt_gate_descriptor*  idt_entry_tbl;
102    unsigned                    limit;
103    interrupt_gate_descriptor   new;
104    rtems_interrupt_level       level;
105
106    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
107
108    /* Convert limit into number of entries */
109    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
110
111    if (vector >= limit) {
112      return;
113    }
114    rtems_interrupt_disable(level);
115    * ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits |
116        (idt_entry_tbl[vector].high_offsets_bits << 16);
117
118    create_interrupt_gate_descriptor(&new,  hdl);
119    idt_entry_tbl[vector] = new;
120
121    rtems_interrupt_enable(level);
122}
123
124int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
125{
126    interrupt_gate_descriptor*  idt_entry_tbl;
127    unsigned                    limit;
128
129    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
130
131    /* Convert limit into number of entries */
132    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
133
134    if (irq->idtIndex >= limit) {
135      return 0;
136    }
137    raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
138
139    *irq = raw_irq_table [irq->idtIndex];
140
141    return 1;
142}
143
144int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
145{
146    interrupt_gate_descriptor*  idt_entry_tbl;
147    unsigned                    limit;
148    rtems_interrupt_level       level;
149
150    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
151
152    /* Convert limit into number of entries */
153    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
154
155    if (irq->idtIndex >= limit) {
156      return 0;
157    }
158    /*
159     * Check if handler passed is actually connected. If not issue an error.
160     * You must first get the current handler via i386_get_current_idt_entry
161     * and then disconnect it using i386_delete_idt_entry.
162     * RATIONALE : to always have the same transition by forcing the user
163     * to get the previous handler before accepting to disconnect.
164     */
165    if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
166      return 0;
167    }
168    rtems_interrupt_disable(level);
169
170    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
171
172    if (irq->off)
173      irq->off(irq);
174
175    raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
176    raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
177
178    rtems_interrupt_enable(level);
179
180    return 1;
181}
182
183/*
184 * Caution this function assumes the IDTR has been already set.
185 */
186int i386_init_idt (rtems_raw_irq_global_settings* config)
187{
188    unsigned                    limit;
189    unsigned                    i;
190    rtems_interrupt_level       level;
191    interrupt_gate_descriptor*  idt_entry_tbl;
192
193    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
194
195    /* Convert limit into number of entries */
196    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
197
198    if (config->idtSize != limit) {
199      return 0;
200    }
201    /*
202     * store various accelarators
203     */
204    raw_irq_table               = config->rawIrqHdlTbl;
205    local_settings              = config;
206    default_raw_irq_entry       = config->defaultRawEntry;
207
208    rtems_interrupt_disable(level);
209
210    create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
211
212    for (i=0; i < limit; i++) {
213      interrupt_gate_descriptor new;
214      create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
215      idt_entry_tbl[i] = new;
216      if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
217        raw_irq_table[i].on(&raw_irq_table[i]);
218      }
219      else {
220        raw_irq_table[i].off(&raw_irq_table[i]);
221      }
222    }
223    rtems_interrupt_enable(level);
224
225    return 1;
226}
227
228int i386_get_idt_config (rtems_raw_irq_global_settings** config)
229{
230  *config = local_settings;
231  return 1;
232}
233
234/*
235 * Caution this function assumes the GDTR has been already set.
236 */
237int i386_set_gdt_entry (unsigned short segment_selector, unsigned base,
238                        unsigned limit)
239{
240    unsigned                    gdt_limit;
241    unsigned short              tmp_segment = 0;
242    unsigned int                limit_adjusted;
243    segment_descriptors*        gdt_entry_tbl;
244
245    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
246
247    if (segment_selector > limit) {
248      return 0;
249    }
250    /*
251     * set up limit first
252     */
253    limit_adjusted = limit;
254    if ( limit > 4095 ) {
255      gdt_entry_tbl[segment_selector].granularity = 1;
256      limit_adjusted /= 4096;
257    }
258    gdt_entry_tbl[segment_selector].limit_15_0  = limit_adjusted & 0xffff;
259    gdt_entry_tbl[segment_selector].limit_19_16 = (limit_adjusted >> 16) & 0xf;
260    /*
261     * set up base
262     */
263    gdt_entry_tbl[segment_selector].base_address_15_0  = base & 0xffff;
264    gdt_entry_tbl[segment_selector].base_address_23_16 = (base >> 16) & 0xff;
265    gdt_entry_tbl[segment_selector].base_address_31_24 = (base >> 24) & 0xff;
266    /*
267     * set up descriptor type (this may well becomes a parameter if needed)
268     */
269    gdt_entry_tbl[segment_selector].type                = 2;    /* Data R/W */
270    gdt_entry_tbl[segment_selector].descriptor_type     = 1;    /* Code or Data */
271    gdt_entry_tbl[segment_selector].privilege           = 0;    /* ring 0 */
272    gdt_entry_tbl[segment_selector].present             = 1;    /* not present */
273
274    /*
275     * Now, reload all segment registers so the limit takes effect.
276     */
277
278    asm volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
279                  "movw %%es,%0 ; movw %0,%%es\n\t"
280                  "movw %%fs,%0 ; movw %0,%%fs\n\t"
281                  "movw %%gs,%0 ; movw %0,%%gs\n\t"
282                  "movw %%ss,%0 ; movw %0,%%ss"
283                   : "=r" (tmp_segment)
284                   : "0"  (tmp_segment)
285                  );
286
287    return 1;
288}
Note: See TracBrowser for help on using the repository browser.