source: rtems/c/src/lib/libcpu/i386/idt.c @ b2b4835

4.104.114.84.95
Last change on this file since b2b4835 was aa9eb940, checked in by Joel Sherrill <joel.sherrill@…>, on 07/30/99 at 17:28:11

Fixed typos.

  • Property mode set to 100644
File size: 8.2 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.OARcorp.com/rtems/license.html.
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   
68    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
69
70    /* Convert limit into number of entries */
71    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
72
73    if (irq->idtIndex >= limit) {
74      return 0;
75    }
76    /*
77     * Check if default handler is actually connected. If not issue an error.
78     * You must first get the current handler via i386_get_current_idt_entry
79     * and then disconnect it using i386_delete_idt_entry.
80     * RATIONALE : to always have the same transition by forcing the user
81     * to get the previous handler before accepting to disconnect.
82     */
83    if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
84      return 0;
85    }
86
87    _CPU_ISR_Disable(level);
88   
89    raw_irq_table [irq->idtIndex] = *irq;
90    create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
91    irq->on(irq);
92   
93    _CPU_ISR_Enable(level);
94    return 1;
95}
96
97void _CPU_ISR_install_vector (unsigned vector,
98                              void* hdl,
99                              void** oldHdl)
100{
101    interrupt_gate_descriptor*  idt_entry_tbl;
102    unsigned                    limit;
103    interrupt_gate_descriptor   new;
104    unsigned int                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    _CPU_ISR_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    _CPU_ISR_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    unsigned int                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    _CPU_ISR_Disable(level);
169
170    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
171
172    irq->off(irq);
173   
174    raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
175    raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
176
177    _CPU_ISR_Enable(level);
178   
179    return 1;
180}
181
182/*
183 * Caution this function assumes the IDTR has been already set.
184 */
185int i386_init_idt (rtems_raw_irq_global_settings* config)
186{
187    unsigned                    limit;
188    unsigned                    i;
189    unsigned                    level;
190    interrupt_gate_descriptor*  idt_entry_tbl;
191   
192    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
193
194    /* Convert limit into number of entries */
195    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
196     
197    if (config->idtSize != limit) {
198      return 0;
199    }
200    /*
201     * store various accelarators
202     */
203    raw_irq_table               = config->rawIrqHdlTbl;
204    local_settings              = config;
205    default_raw_irq_entry       = config->defaultRawEntry;
206
207    _CPU_ISR_Disable(level);
208
209    create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
210
211    for (i=0; i < limit; i++) {
212      interrupt_gate_descriptor new;
213      create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
214      idt_entry_tbl[i] = new;
215      if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
216        raw_irq_table[i].on(&raw_irq_table[i]);
217      }
218      else {
219        raw_irq_table[i].off(&raw_irq_table[i]);
220      }
221    }
222    _CPU_ISR_Enable(level);
223
224    return 1;
225}
226
227int i386_get_idt_config (rtems_raw_irq_global_settings** config)
228{
229  *config = local_settings;
230  return 1;
231}
232
233/*
234 * Caution this function assumes the GDTR has been already set.
235 */
236int i386_set_gdt_entry (unsigned short segment_selector, unsigned base,
237                        unsigned limit)
238{
239    unsigned                    gdt_limit;
240    unsigned short              tmp_segment = 0;
241    unsigned int                limit_adjusted;
242    segment_descriptors*        gdt_entry_tbl;
243
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
279                   movw %%es,%0 ; movw %0,%%es
280                   movw %%fs,%0 ; movw %0,%%fs
281                   movw %%gs,%0 ; movw %0,%%gs
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.