From 67a2288991ce3662a588ee83c0bea9c9efae5f1e Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 23 Jul 1998 22:02:34 +0000 Subject: Patch from Eric VALETTE : Here is a enhanced version of my previous patch. This patch enables to potentially share the new interrupt management code for all Intel targets (pc386, go32 and force386) bsp. Note : this patch is complete only for pc386. It still needs to be completed for go32 and force386. I carrefully checked that anything needed is in for force386 (only some function name changes for IDT manipulation and GDT segment manipulation). But anyway I will not be able to test any of theses targets... --- c/src/lib/libcpu/i386/cpu.c | 265 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 c/src/lib/libcpu/i386/cpu.c (limited to 'c/src/lib/libcpu/i386/cpu.c') diff --git a/c/src/lib/libcpu/i386/cpu.c b/c/src/lib/libcpu/i386/cpu.c new file mode 100644 index 0000000000..8873de3811 --- /dev/null +++ b/c/src/lib/libcpu/i386/cpu.c @@ -0,0 +1,265 @@ +/* + * cpu.c - This file contains implementation of C function to + * Instanciate IDT entries. More detailled information can be found + * on Intel site and more precisely in the following book : + * + * Pentium Processor familly + * Developper's Manual + * + * Volume 3 : Architecture and Programming Manual + * + * Copyright (C) 1998 Eric Valette (valette@crf.canon.fr) + * Canon Centre Recherche France. + * + * The license and distribution terms for this file may be + * found in found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ + +#include +#include + +static rtems_raw_irq_connect_data* raw_irq_table; +static rtems_raw_irq_connect_data default_raw_irq_entry; +static interrupt_gate_descriptor default_idt_entry; +static rtems_raw_irq_global_settings* local_settings; + +void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry, + rtems_raw_irq_hdl hdl) +{ + idtEntry->low_offsets_bits = (((unsigned) hdl) & 0xffff); + idtEntry->segment_selector = i386_get_cs(); + idtEntry->fixed_value_bits = 0; + idtEntry->gate_type = 0xe; + idtEntry->privilege = 0; + idtEntry->present = 1; + idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff); +} + +rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index) +{ + rtems_raw_irq_hdl hdl; + interrupt_gate_descriptor* idt_entry_tbl; + unsigned limit; + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + * ((unsigned int*) &hdl) = (idt_entry_tbl[index].low_offsets_bits | + (idt_entry_tbl[index].high_offsets_bits << 16)); + return hdl; +} + +int i386_set_idt_entry (const rtems_raw_irq_connect_data* irq) +{ + interrupt_gate_descriptor* idt_entry_tbl; + unsigned limit; + unsigned int level; + + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + if (irq->idtIndex > limit) { + return 0; + } + /* + * Check if default handler is actually connected. If not issue an error. + * You must first get the current handler via i386_get_current_idt_entry + * and then disconnect it using i386_delete_idt_entry. + * RATIONALE : to always have the same transition by forcing the user + * to get the previous handler before accepting to disconnect. + */ + if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) { + return 0; + } + _CPU_ISR_Disable(level); + + raw_irq_table [irq->idtIndex] = *irq; + create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl); + irq->on(irq); + + _CPU_ISR_Enable(level); + return 1; +} + +void _CPU_ISR_install_vector (unsigned vector, + void* hdl, + void** oldHdl) +{ + interrupt_gate_descriptor* idt_entry_tbl; + unsigned limit; + interrupt_gate_descriptor new; + unsigned int level; + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + if (vector > limit) { + return; + } + _CPU_ISR_Disable(level) + * ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits | + (idt_entry_tbl[vector].high_offsets_bits << 16); + + create_interrupt_gate_descriptor(&new, hdl); + idt_entry_tbl[vector] = new; + + _CPU_ISR_Enable(level); +} + +int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq) +{ + interrupt_gate_descriptor* idt_entry_tbl; + unsigned limit; + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + if (irq->idtIndex > limit) { + return 1; + } + raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex); + + *irq = raw_irq_table [irq->idtIndex]; + + return 0; +} + +int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq) +{ + interrupt_gate_descriptor* idt_entry_tbl; + unsigned limit; + unsigned int level; + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + if (irq->idtIndex > limit) { + return 1; + } + /* + * Check if handler passed is actually connected. If not issue an error. + * You must first get the current handler via i386_get_current_idt_entry + * and then disconnect it using i386_delete_idt_entry. + * RATIONALE : to always have the same transition by forcing the user + * to get the previous handler before accepting to disconnect. + */ + if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){ + return 1; + } + _CPU_ISR_Disable(level); + + idt_entry_tbl[irq->idtIndex] = default_idt_entry; + + irq->off(irq); + + raw_irq_table[irq->idtIndex] = default_raw_irq_entry; + + _CPU_ISR_Enable(level); + + return 0; +} + +/* + * Caution this function assumes the IDTR has been already set. + */ +int i386_init_idt (rtems_raw_irq_global_settings* config) +{ + unsigned limit; + unsigned i; + unsigned level; + interrupt_gate_descriptor* idt_entry_tbl; + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + + if (config->idtSize != limit) { + return 1; + } + /* + * store various accelarators + */ + raw_irq_table = config->rawIrqHdlTbl; + local_settings = config; + default_raw_irq_entry = config->defaultRawEntry; + + _CPU_ISR_Disable(level); + + create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl); + + for (i=0; i < limit; i++) { + interrupt_gate_descriptor new; + create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl); + idt_entry_tbl[i] = new; + if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) { + raw_irq_table[i].on(&raw_irq_table[i]); + } + else { + raw_irq_table[i].off(&raw_irq_table[i]); + } + } + _CPU_ISR_Enable(level); + + return 0; +} + +int i386_get_idt_config (rtems_raw_irq_global_settings** config) +{ + *config = local_settings; + return 0; +} + +/* + * Caution this function assumes the GDTR has been already set. + */ +int i386_set_gdt_entry (unsigned short segment_selector, unsigned base, + unsigned limit) +{ + unsigned gdt_limit; + unsigned short tmp_segment = 0; + unsigned int limit_adjusted; + segment_descriptors* gdt_entry_tbl; + + + i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit); + + if (segment_selector > limit) { + return 1; + } + /* + * set up limit first + */ + limit_adjusted = limit; + if ( limit > 4095 ) { + gdt_entry_tbl[segment_selector].granularity = 1; + limit_adjusted /= 4096; + } + gdt_entry_tbl[segment_selector].limit_15_0 = limit_adjusted & 0xffff; + gdt_entry_tbl[segment_selector].limit_19_16 = (limit_adjusted >> 16) & 0xf; + /* + * set up base + */ + gdt_entry_tbl[segment_selector].base_address_15_0 = base & 0xffff; + gdt_entry_tbl[segment_selector].base_address_23_16 = (base >> 16) & 0xff; + gdt_entry_tbl[segment_selector].base_address_31_24 = (base >> 24) & 0xff; + /* + * set up descriptor type (this may well becomes a parameter if needed) + */ + gdt_entry_tbl[segment_selector].type = 2; /* Data R/W */ + gdt_entry_tbl[segment_selector].descriptor_type = 1; /* Code or Data */ + gdt_entry_tbl[segment_selector].privilege = 0; /* ring 0 */ + gdt_entry_tbl[segment_selector].present = 1; /* not present */ + + /* + * Now, reload all segment registers so the limit takes effect. + */ + + asm volatile( "movw %%ds,%0 ; movw %0,%%ds + movw %%es,%0 ; movw %0,%%es + movw %%fs,%0 ; movw %0,%%fs + movw %%gs,%0 ; movw %0,%%gs + movw %%ss,%0 ; movw %0,%%ss" + : "=r" (tmp_segment) + : "0" (tmp_segment) + ); + + return 0; +} -- cgit v1.2.3