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/Makefile.in | 64 ++++++ c/src/lib/libcpu/i386/cpu.c | 265 ++++++++++++++++++++++ c/src/lib/libcpu/i386/cpu.h | 365 +++++++++++++++++++++++++++++++ c/src/lib/libcpu/i386/cpu_asm.S | 117 ++++++++++ c/src/lib/libcpu/i386/idt.c | 265 ++++++++++++++++++++++ c/src/lib/libcpu/i386/wrapup/Makefile.in | 50 +++++ 6 files changed, 1126 insertions(+) create mode 100644 c/src/lib/libcpu/i386/Makefile.in create mode 100644 c/src/lib/libcpu/i386/cpu.c create mode 100644 c/src/lib/libcpu/i386/cpu.h create mode 100644 c/src/lib/libcpu/i386/cpu_asm.S create mode 100644 c/src/lib/libcpu/i386/idt.c create mode 100644 c/src/lib/libcpu/i386/wrapup/Makefile.in (limited to 'c/src/lib/libcpu') diff --git a/c/src/lib/libcpu/i386/Makefile.in b/c/src/lib/libcpu/i386/Makefile.in new file mode 100644 index 0000000000..8a1c6f0951 --- /dev/null +++ b/c/src/lib/libcpu/i386/Makefile.in @@ -0,0 +1,64 @@ +# +# $Id$ +# + +@SET_MAKE@ +srcdir = @srcdir@ +VPATH = @srcdir@ +RTEMS_ROOT = @top_srcdir@ +PROJECT_ROOT = @PROJECT_ROOT@ + +PGM=${ARCH}/libcpu.rel + +# C source names, if any, go here -- minus the .c +C_PIECES=cpu +C_FILES=$(C_PIECES:%=%.c) +C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) + +H_FILES=$(srcdir)/cpu.h + +# Assembly source names, if any, go here -- minus the .s +S_PIECES=cpu_asm +S_FILES=$(S_PIECES:%=%.S) +S_O_FILES=$(S_FILES:%.S=${ARCH}/%.o) + +SRCS=$(C_FILES) $(H_FILES) +OBJS=$(C_O_FILES) $(S_O_FILES) + +include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg +include $(RTEMS_ROOT)/make/leaf.cfg + +# +# (OPTIONAL) Add local stuff here using += +# + +DEFINES += +CPPFLAGS += +CFLAGS += + +LD_PATHS += +LD_LIBS += +LDFLAGS += + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +${PGM}: ${SRCS} ${OBJS} + $(make-rel) + +preinstall : + $(MKDIR) $(PROJECT_INCLUDE)/libcpu + $(INSTALL) -m 444 ${H_FILES} $(PROJECT_INCLUDE)/libcpu + +all: ${ARCH} $(SRCS) preinstall $(OBJ) $(PGM) + cd wrapup; $(MAKE) + +# the .rel file built here will be put into libcpu.a by ../wrapup/Makefile +install: all 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; +} diff --git a/c/src/lib/libcpu/i386/cpu.h b/c/src/lib/libcpu/i386/cpu.h new file mode 100644 index 0000000000..cded552740 --- /dev/null +++ b/c/src/lib/libcpu/i386/cpu.h @@ -0,0 +1,365 @@ +/* + * cpu.h - This file contains definitions for data structure related + * to Intel system programming. More 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$ + */ + +#ifndef _i386_CPU_H +#define _i386_CPU_H + +#ifndef ASM + +/* + * Interrupt Level Macros + */ + +#define i386_disable_interrupts( _level ) \ + { \ + _level = 0; /* avoids warnings */ \ + asm volatile ( "pushf ; \ + cli ; \ + pop %0" \ + : "=r" ((_level)) : "0" ((_level)) \ + ); \ + } + +#define i386_enable_interrupts( _level ) \ + { \ + asm volatile ( "push %0 ; \ + popf" \ + : "=r" ((_level)) : "0" ((_level)) \ + ); \ + } + +#define i386_flash_interrupts( _level ) \ + { \ + asm volatile ( "push %0 ; \ + popf ; \ + cli" \ + : "=r" ((_level)) : "0" ((_level)) \ + ); \ + } + +#define i386_get_interrupt_level( _level ) \ + do { \ + register unsigned32 _eflags = 0; \ + \ + asm volatile ( "pushf ; \ + pop %0" \ + : "=r" ((_eflags)) : "0" ((_eflags)) \ + ); \ + \ + _level = (_eflags & 0x0200) ? 0 : 1; \ + } while (0) + +#define _CPU_ISR_Disable( _level ) i386_disable_interrupts( _level ) +#define _CPU_ISR_Enable( _level ) i386_enable_interrupts( _level ) + +/* + * Segment Access Routines + * + * NOTE: Unfortunately, these are still static inlines even when the + * "macro" implementation of the generic code is used. + */ + +static inline unsigned short i386_get_cs() +{ + register unsigned short segment = 0; + + asm volatile ( "movw %%cs,%0" : "=r" (segment) : "0" (segment) ); + + return segment; +} + +static inline unsigned short i386_get_ds() +{ + register unsigned short segment = 0; + + asm volatile ( "movw %%ds,%0" : "=r" (segment) : "0" (segment) ); + + return segment; +} + +static inline unsigned short i386_get_es() +{ + register unsigned short segment = 0; + + asm volatile ( "movw %%es,%0" : "=r" (segment) : "0" (segment) ); + + return segment; +} + +static inline unsigned short i386_get_ss() +{ + register unsigned short segment = 0; + + asm volatile ( "movw %%ss,%0" : "=r" (segment) : "0" (segment) ); + + return segment; +} + +static inline unsigned short i386_get_fs() +{ + register unsigned short segment = 0; + + asm volatile ( "movw %%fs,%0" : "=r" (segment) : "0" (segment) ); + + return segment; +} + +static inline unsigned short i386_get_gs() +{ + register unsigned short segment = 0; + + asm volatile ( "movw %%gs,%0" : "=r" (segment) : "0" (segment) ); + + return segment; +} + +/* + * IO Port Access Routines + */ + +#define i386_outport_byte( _port, _value ) \ + { register unsigned short __port = _port; \ + register unsigned char __value = _value; \ + \ + asm volatile ( "outb %0,%1" : "=a" (__value), "=d" (__port) \ + : "0" (__value), "1" (__port) \ + ); \ + } + +#define i386_outport_word( _port, _value ) \ + { register unsigned short __port = _port; \ + register unsigned short __value = _value; \ + \ + asm volatile ( "outw %0,%1" : "=a" (__value), "=d" (__port) \ + : "0" (__value), "1" (__port) \ + ); \ + } + +#define i386_outport_long( _port, _value ) \ + { register unsigned short __port = _port; \ + register unsigned int __value = _value; \ + \ + asm volatile ( "outl %0,%1" : "=a" (__value), "=d" (__port) \ + : "0" (__value), "1" (__port) \ + ); \ + } + +#define i386_inport_byte( _port, _value ) \ + { register unsigned short __port = _port; \ + register unsigned char __value = 0; \ + \ + asm volatile ( "inb %1,%0" : "=a" (__value), "=d" (__port) \ + : "0" (__value), "1" (__port) \ + ); \ + _value = __value; \ + } + +#define i386_inport_word( _port, _value ) \ + { register unsigned short __port = _port; \ + register unsigned short __value = 0; \ + \ + asm volatile ( "inw %1,%0" : "=a" (__value), "=d" (__port) \ + : "0" (__value), "1" (__port) \ + ); \ + _value = __value; \ + } + +#define i386_inport_long( _port, _value ) \ + { register unsigned short __port = _port; \ + register unsigned int __value = 0; \ + \ + asm volatile ( "inl %1,%0" : "=a" (__value), "=d" (__port) \ + : "0" (__value), "1" (__port) \ + ); \ + _value = __value; \ + } + +/* + * Type definition for raw interrupts. + */ + +typedef unsigned char rtems_vector_offset; + +struct __rtems_raw_irq_connect_data__; + +typedef void (*rtems_raw_irq_hdl) (void); +typedef void (*rtems_raw_irq_enable) (const struct __rtems_raw_irq_connect_data__*); +typedef void (*rtems_raw_irq_disable) (const struct __rtems_raw_irq_connect_data__*); +typedef int (*rtems_raw_irq_is_enabled) (const struct __rtems_raw_irq_connect_data__*); + +typedef struct __rtems_raw_irq_connect_data__{ + /* + * IDT vector offset (IRQ line + PC386_IRQ_VECTOR_BASE) + */ + rtems_vector_offset idtIndex; + /* + * IDT raw handler. See comment on handler properties below in function prototype. + */ + rtems_raw_irq_hdl hdl; + /* + * function for enabling raw interrupts. In order to be consistent + * with the fact that the raw connexion can defined in the + * libcpu library, this library should have no knowledge of + * board specific hardware to manage interrupts and thus the + * "on" routine must enable the irq both at device and PIC level. + * + */ + rtems_raw_irq_enable on; + /* + * function for disabling raw interrupts. In order to be consistent + * with the fact that the raw connexion can defined in the + * libcpu library, this library should have no knowledge of + * board specific hardware to manage interrupts and thus the + * "on" routine must disable the irq both at device and PIC level. + * + */ + rtems_raw_irq_disable off; + /* + * function enabling to know what interrupt may currently occur + */ + rtems_raw_irq_is_enabled isOn; +}rtems_raw_irq_connect_data; + +typedef struct { + /* + * size of all the table fields (*Tbl) described below. + */ + unsigned int idtSize; + /* + * Default handler used when disconnecting interrupts. + */ + rtems_raw_irq_connect_data defaultRawEntry; + /* + * Table containing initials/current value. + */ + rtems_raw_irq_connect_data* rawIrqHdlTbl; +}rtems_raw_irq_global_settings; + +/* + * See page 14.9 Figure 14-2. + * + */ +typedef struct { + unsigned int low_offsets_bits : 16; + unsigned int segment_selector : 16; + unsigned int fixed_value_bits : 8; + unsigned int gate_type : 5; + unsigned int privilege : 2; + unsigned int present : 1; + unsigned int high_offsets_bits: 16; +}interrupt_gate_descriptor; + + +/* + * C callable function enabling to create a interrupt_gate_descriptor + */ +void create_interrupt_gate_descriptor (interrupt_gate_descriptor*, rtems_raw_irq_hdl); + +/* + * C callable function enabling to get handler currently connected to a vector + * + */ +rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset); + +/* + * C callable function enabling to get easilly usable info from + * the actual value of IDT register. + */ +extern void i386_get_info_from_IDTR (interrupt_gate_descriptor** table, + unsigned* limit); +/* + * C callable function enabling to change the value of IDT register. Must be called + * with interrupts masked at processor level!!!. + */ +extern void i386_set_IDTR (interrupt_gate_descriptor* table, + unsigned limit); + +/* + * C callable function enabling to set up one raw idt entry + */ +extern int i386_set_idt_entry (const rtems_raw_irq_connect_data*); + +/* + * C callable function enabling to get one current raw idt entry + */ +extern int i386_get_current_idt_entry (rtems_raw_irq_connect_data*); + +/* + * C callable function enabling to remove one current raw idt entry + */ +extern int i386_delete_idt_entry (const rtems_raw_irq_connect_data*); + +/* + * C callable function enabling to init idt. + * + * CAUTION : this function assumes that the IDTR register + * has been already set. + */ +extern int i386_init_idt (rtems_raw_irq_global_settings* config); + +/* + * C callable function enabling to get actual idt configuration + */ +extern int i386_get_idt_config (rtems_raw_irq_global_settings** config); + + +/* + * See page 11.12 Figure 11-8. + * + */ + +typedef struct { + unsigned int limit_15_0 : 16; + unsigned int base_address_15_0 : 16; + unsigned int base_address_23_16 : 8; + unsigned int type : 4; + unsigned int descriptor_type : 1; + unsigned int privilege : 2; + unsigned int present : 1; + unsigned int limit_19_16 : 4; + unsigned int available : 1; + unsigned int fixed_value_bits : 1; + unsigned int operation_size : 1; + unsigned int granularity : 1; + unsigned int base_address_31_24 : 8; +}segment_descriptors; + +/* + * C callable function enabling to get easilly usable info from + * the actual value of GDT register. + */ +extern void i386_get_info_from_GDTR (segment_descriptors** table, + unsigned* limit); +/* + * C callable function enabling to change the value of GDT register. Must be called + * with interrupts masked at processor level!!!. + */ +extern void i386_set_GDTR (segment_descriptors*, + unsigned limit); + +/* + * C callable function enabling to set up one raw interrupt handler + */ +extern int i386_set_gdt_entry (unsigned short segment_selector, unsigned base, + unsigned limit); + +# endif /* ASM */ + +#endif diff --git a/c/src/lib/libcpu/i386/cpu_asm.S b/c/src/lib/libcpu/i386/cpu_asm.S new file mode 100644 index 0000000000..38dc7318ae --- /dev/null +++ b/c/src/lib/libcpu/i386/cpu_asm.S @@ -0,0 +1,117 @@ +/* cpu_asm.S + * + * This file contains all assembly code for the Intel i386 IDT + * manipulation. + * + * COPYRIGHT (c) 1998 valette@crf.canon.fr + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ + +#include + +BEGIN_CODE +/* + * C callable function enabling to get easilly usable info from + * the actual value of IDT register. + * +extern void i386_get_info_from_IDTR (interrupt_gate_descriptor** table, + unsigned* limit); + */ +PUBLIC (i386_get_info_from_IDTR) +PUBLIC (i386_set_IDTR) +PUBLIC (i386_get_info_from_GDTR) +PUBLIC (i386_set_GDTR) + +SYM (i386_get_info_from_IDTR): + movl 4(esp), ecx /* get location where table address */ + /* must be stored */ + movl 8(esp), edx /* get location table size must be stored */ + + subl $6, esp /* let room to prepare 48 bit IDTR */ + + sidt (esp) /* get 48 bit IDTR value */ + + movl 2(esp), eax /* get base */ + movl eax, (ecx) + + movzwl (esp), eax /* get limit */ + movl eax, (edx) + + addl $6, esp /* restore %esp */ + ret + +/* + * C callable function enabling to change the value of IDT register. Must be called + * with inmterrupt masked at processor level!!!. + * +extern void i386_set_IDTR (interrupt_gate_descriptor* table, + unsigned limit); + */ +SYM (i386_set_IDTR): + + leal 4(esp), edx /* load in edx address of input */ + /* parameter "table" */ + + movl (edx), eax /* load base into eax */ + movl 4(edx), ecx /* load limit into ecx */ + + movw cx, (edx) /* prepare 48 bit pointer */ + movl eax, 2(edx) + + lidt (edx) + + ret +/* + * + * C callable function enabling to get easilly usable info from + * the actual value of GDT register. +extern void i386_get_info_from_GDTR (segment_descriptors** table, + unsigned* limit); + */ + +SYM (i386_get_info_from_GDTR): + movl 4(esp), ecx /* get location where table address */ + /* must be stored */ + movl 8(esp), edx /* get location table size must be stored */ + + subl $6, esp /* let room to prepare 48 bit GDTR */ + + sgdt (esp) /* get 48 bit GDTR value */ + + movl 2(esp), eax /* get base */ + movl eax, (ecx) + + movzwl (esp), eax /* get limit */ + movl eax, (edx) + + addl $6, esp /* restore %esp */ + ret + +/* + * C callable function enabling to change the value of GDT register. + * Must be called with interrupts masked at processor level!!!. + * extern void i386_set_GDTR (segment_descriptors*, unsigned limit); + */ +SYM (i386_set_GDTR): + + leal 4(esp), edx /* load in edx address of input */ + /* parameter "table" */ + + movl (edx), eax /* load base into eax */ + movl 4(edx), ecx /* load limit into ecx */ + + movw cx, (edx) /* prepare 48 bit pointer */ + movl eax, 2(edx) + + lgdt (edx) + + ret + +END_CODE + +END diff --git a/c/src/lib/libcpu/i386/idt.c b/c/src/lib/libcpu/i386/idt.c new file mode 100644 index 0000000000..8873de3811 --- /dev/null +++ b/c/src/lib/libcpu/i386/idt.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; +} diff --git a/c/src/lib/libcpu/i386/wrapup/Makefile.in b/c/src/lib/libcpu/i386/wrapup/Makefile.in new file mode 100644 index 0000000000..aa05a7fae7 --- /dev/null +++ b/c/src/lib/libcpu/i386/wrapup/Makefile.in @@ -0,0 +1,50 @@ +# +# $Id$ +# + +@SET_MAKE@ +srcdir = @srcdir@ +VPATH = @srcdir@ +RTEMS_ROOT = @top_srcdir@ +PROJECT_ROOT = @PROJECT_ROOT@ + +BSP_PIECES=startup clock console timer +GENERIC_PIECES= + +# bummer; have to use $foreach since % pattern subst rules only replace 1x +OBJS=../$(ARCH)/libcpu.rel +LIB=$(ARCH)/libcpu.a + +include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg +include $(RTEMS_ROOT)/make/lib.cfg + +# +# (OPTIONAL) Add local stuff here using += +# + +DEFINES += +CPPFLAGS += +CFLAGS += + +LD_PATHS += +LD_LIBS += +LDFLAGS += + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +$(LIB): ${OBJS} + $(make-library) + +all: ${ARCH} $(SRCS) $(LIB) + $(INSTALL_VARIANT) -m 644 $(LIB) ${PROJECT_RELEASE}/lib +# we create here a directory specific to the PC386 BSP to store the BootImage +# files so they can be easily found + mkdir -p ${PROJECT_RELEASE}/BootImgs -- cgit v1.2.3