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

Last change on this file since 328bd35 was 328bd35, checked in by Joel Sherrill <joel@…>, on Jan 23, 2016 at 7:06:22 PM

i386: refactor libcpu/cpu.h into rtems/score/i386.h

Fixes #2515.

  • Property mode set to 100644
File size: 10.6 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.org/license/LICENSE.
17 */
18
19#include <rtems/score/cpu.h>
20#include <bsp/irq.h>
21
22static rtems_raw_irq_connect_data*      raw_irq_table;
23static rtems_raw_irq_connect_data       default_raw_irq_entry;
24static interrupt_gate_descriptor        default_idt_entry;
25static rtems_raw_irq_global_settings*   local_settings;
26
27void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry,
28                                       rtems_raw_irq_hdl hdl)
29{
30    idtEntry->low_offsets_bits  = (((unsigned) hdl) & 0xffff);
31    idtEntry->segment_selector  = i386_get_cs();
32    idtEntry->fixed_value_bits  = 0;
33    idtEntry->gate_type         = 0xe;
34    idtEntry->privilege         = 0;
35    idtEntry->present           = 1;
36    idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff);
37}
38
39rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index)
40{
41    uint32_t                    hdl;
42    interrupt_gate_descriptor*  idt_entry_tbl;
43    unsigned                    limit;
44
45    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
46
47    /* Convert limit into number of entries */
48    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
49
50    if(index >= limit) {
51        return 0;
52    }
53
54    hdl = (idt_entry_tbl[index].low_offsets_bits |
55          (idt_entry_tbl[index].high_offsets_bits << 16));
56    return (rtems_raw_irq_hdl) hdl;
57}
58
59int i386_set_idt_entry  (const rtems_raw_irq_connect_data* irq)
60{
61    interrupt_gate_descriptor*  idt_entry_tbl;
62    unsigned                    limit;
63    rtems_interrupt_level       level;
64
65    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
66
67    /* Convert limit into number of entries */
68    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
69
70    if (irq->idtIndex >= limit) {
71      return 0;
72    }
73    /*
74     * Check if default handler is actually connected. If not issue an error.
75     * You must first get the current handler via i386_get_current_idt_entry
76     * and then disconnect it using i386_delete_idt_entry.
77     * RATIONALE : to always have the same transition by forcing the user
78     * to get the previous handler before accepting to disconnect.
79     */
80    if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
81      return 0;
82    }
83
84    rtems_interrupt_disable(level);
85
86    raw_irq_table [irq->idtIndex] = *irq;
87    create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
88    if (irq->on)
89      irq->on(irq);
90
91    rtems_interrupt_enable(level);
92    return 1;
93}
94
95void _CPU_ISR_install_vector (uint32_t vector,
96                              proc_ptr hdl,
97                              proc_ptr * oldHdl)
98{
99    interrupt_gate_descriptor*  idt_entry_tbl;
100    unsigned                    limit;
101    interrupt_gate_descriptor   new;
102    rtems_interrupt_level       level;
103
104    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
105
106    /* Convert limit into number of entries */
107    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
108
109    if (vector >= limit) {
110      return;
111    }
112    rtems_interrupt_disable(level);
113    * ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits |
114        (idt_entry_tbl[vector].high_offsets_bits << 16);
115
116    create_interrupt_gate_descriptor(&new,  hdl);
117    idt_entry_tbl[vector] = new;
118
119    rtems_interrupt_enable(level);
120}
121
122int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
123{
124    interrupt_gate_descriptor*  idt_entry_tbl;
125    unsigned                    limit;
126
127    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
128
129    /* Convert limit into number of entries */
130    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
131
132    if (irq->idtIndex >= limit) {
133      return 0;
134    }
135    raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
136
137    *irq = raw_irq_table [irq->idtIndex];
138
139    return 1;
140}
141
142int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
143{
144    interrupt_gate_descriptor*  idt_entry_tbl;
145    unsigned                    limit;
146    rtems_interrupt_level       level;
147
148    i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
149
150    /* Convert limit into number of entries */
151    limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
152
153    if (irq->idtIndex >= limit) {
154      return 0;
155    }
156    /*
157     * Check if handler passed is actually connected. If not issue an error.
158     * You must first get the current handler via i386_get_current_idt_entry
159     * and then disconnect it using i386_delete_idt_entry.
160     * RATIONALE : to always have the same transition by forcing the user
161     * to get the previous handler before accepting to disconnect.
162     */
163    if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
164      return 0;
165    }
166    rtems_interrupt_disable(level);
167
168    idt_entry_tbl[irq->idtIndex] = default_idt_entry;
169
170    if (irq->off)
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
232uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
233                             segment_descriptors* sd)
234{
235    uint16_t                gdt_limit;
236    uint16_t                tmp_segment = 0;
237    segment_descriptors*    gdt_entry_tbl;
238    uint8_t                 present;
239
240    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
241
242    if (segment_selector_index >= (gdt_limit+1)/8) {
243      /* index to GDT table out of bounds */
244      return 0;
245    }
246    if (segment_selector_index == 0) {
247      /* index 0 is not usable */
248      return 0;
249    }
250
251    /* put prepared descriptor into the GDT */
252    present = sd->present;
253    sd->present = 0;
254    gdt_entry_tbl[segment_selector_index].present = 0;
255    RTEMS_COMPILER_MEMORY_BARRIER();
256    gdt_entry_tbl[segment_selector_index] = *sd;
257    RTEMS_COMPILER_MEMORY_BARRIER();
258    gdt_entry_tbl[segment_selector_index].present = present;
259    sd->present = present;
260    /*
261     * Now, reload all segment registers so that the possible changes takes effect.
262     */
263    __asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
264                  "movw %%es,%0 ; movw %0,%%es\n\t"
265                  "movw %%fs,%0 ; movw %0,%%fs\n\t"
266                  "movw %%gs,%0 ; movw %0,%%gs\n\t"
267                  "movw %%ss,%0 ; movw %0,%%ss"
268                   : "=r" (tmp_segment)
269                   : "0"  (tmp_segment)
270                 );
271    return 1;
272}
273
274void i386_fill_segment_desc_base(uint32_t base,
275                                 segment_descriptors* sd)
276{
277    sd->base_address_15_0  = base & 0xffff;
278    sd->base_address_23_16 = (base >> 16) & 0xff;
279    sd->base_address_31_24 = (base >> 24) & 0xff;
280}
281
282void i386_fill_segment_desc_limit(uint32_t limit,
283                                  segment_descriptors* sd)
284{
285    sd->granularity = 0;
286    if (limit > 65535) {
287      sd->granularity = 1;
288      limit /= 4096;
289    }
290    sd->limit_15_0  = limit & 0xffff;
291    sd->limit_19_16 = (limit >> 16) & 0xf;
292}
293
294/*
295 * Caution this function assumes the GDTR has been already set.
296 */
297uint32_t i386_set_gdt_entry (uint16_t segment_selector_index, uint32_t base,
298                             uint32_t limit)
299{
300    segment_descriptors     gdt_entry;
301    memset(&gdt_entry, 0, sizeof(gdt_entry));
302
303    i386_fill_segment_desc_limit(limit, &gdt_entry);
304    i386_fill_segment_desc_base(base, &gdt_entry);
305    /*
306     * set up descriptor type (this may well becomes a parameter if needed)
307     */
308    gdt_entry.type              = 2;    /* Data R/W */
309    gdt_entry.descriptor_type   = 1;    /* Code or Data */
310    gdt_entry.privilege         = 0;    /* ring 0 */
311    gdt_entry.present           = 1;    /* not present */
312
313    /*
314     * Now, reload all segment registers so the limit takes effect.
315     */
316    return i386_raw_gdt_entry(segment_selector_index, &gdt_entry);
317}
318
319uint16_t i386_next_empty_gdt_entry ()
320{
321    uint16_t                gdt_limit;
322    segment_descriptors*    gdt_entry_tbl;
323    /* initial amount of filled descriptors */
324    static uint16_t         segment_selector_index = 2;
325
326    segment_selector_index += 1;
327    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
328    if (segment_selector_index >= (gdt_limit+1)/8) {
329      return 0;
330    }
331    return segment_selector_index;
332}
333
334uint16_t i386_cpy_gdt_entry(uint16_t segment_selector_index,
335                            segment_descriptors* struct_to_fill)
336{
337    uint16_t                gdt_limit;
338    segment_descriptors*    gdt_entry_tbl;
339
340    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
341
342    if (segment_selector_index >= (gdt_limit+1)/8) {
343      return 0;
344    }
345
346    *struct_to_fill = gdt_entry_tbl[segment_selector_index];
347    return segment_selector_index;
348}
349
350segment_descriptors* i386_get_gdt_entry(uint16_t segment_selector_index)
351{
352    uint16_t                gdt_limit;
353    segment_descriptors*    gdt_entry_tbl;
354
355    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
356
357    if (segment_selector_index >= (gdt_limit+1)/8) {
358      return 0;
359    }
360    return &gdt_entry_tbl[segment_selector_index];
361}
362
363uint32_t i386_limit_gdt_entry(segment_descriptors* gdt_entry)
364{
365    uint32_t lim = (gdt_entry->limit_15_0 + (gdt_entry->limit_19_16<<16));
366    if (gdt_entry->granularity) {
367      return lim*4096+4095;
368    }
369    return lim;
370}
Note: See TracBrowser for help on using the repository browser.