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/libbsp/i386/Makefile.in | 2 +- c/src/lib/libbsp/i386/pc386/clock/ckinit.c | 66 ++--- c/src/lib/libbsp/i386/pc386/console/console.c | 46 +-- c/src/lib/libbsp/i386/pc386/console/inch.c | 31 +- c/src/lib/libbsp/i386/pc386/include/Makefile.in | 4 +- c/src/lib/libbsp/i386/pc386/include/bsp.h | 10 +- c/src/lib/libbsp/i386/pc386/start/start.s | 3 +- c/src/lib/libbsp/i386/pc386/startup/Makefile.in | 10 +- c/src/lib/libbsp/i386/pc386/startup/bspstart.c | 8 +- c/src/lib/libbsp/i386/pc386/startup/ldsegs.s | 30 +- c/src/lib/libbsp/i386/pc386/timer/timer.c | 72 +++-- c/src/lib/libbsp/i386/shared/Makefile.in | 16 ++ c/src/lib/libbsp/i386/shared/irq/Makefile.in | 33 +++ c/src/lib/libbsp/i386/shared/irq/idt.c | 265 +++++++++++++++++ c/src/lib/libbsp/i386/shared/irq/irq.c | 358 +++++++++++++++++++++++ c/src/lib/libbsp/i386/shared/irq/irq.h | 260 +++++++++++++++++ c/src/lib/libbsp/i386/shared/irq/irq_asm.h | 30 ++ c/src/lib/libbsp/i386/shared/irq/irq_asm.s | 261 +++++++++++++++++ c/src/lib/libbsp/i386/shared/irq/irq_init.c | 200 +++++++++++++ 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 ++++ c/src/lib/wrapup/Makefile.in | 1 + 26 files changed, 2718 insertions(+), 114 deletions(-) create mode 100644 c/src/lib/libbsp/i386/shared/Makefile.in create mode 100644 c/src/lib/libbsp/i386/shared/irq/Makefile.in create mode 100644 c/src/lib/libbsp/i386/shared/irq/idt.c create mode 100644 c/src/lib/libbsp/i386/shared/irq/irq.c create mode 100644 c/src/lib/libbsp/i386/shared/irq/irq.h create mode 100644 c/src/lib/libbsp/i386/shared/irq/irq_asm.h create mode 100644 c/src/lib/libbsp/i386/shared/irq/irq_asm.s create mode 100644 c/src/lib/libbsp/i386/shared/irq/irq_init.c 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') diff --git a/c/src/lib/libbsp/i386/Makefile.in b/c/src/lib/libbsp/i386/Makefile.in index f049e9b2e8..98bc04bc5d 100644 --- a/c/src/lib/libbsp/i386/Makefile.in +++ b/c/src/lib/libbsp/i386/Makefile.in @@ -12,4 +12,4 @@ include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg include $(RTEMS_ROOT)/make/directory.cfg # Descend into the $(RTEMS_BSP_FAMILY) directory -SUB_DIRS=$(RTEMS_BSP_FAMILY) +SUB_DIRS=shared $(RTEMS_BSP_FAMILY) diff --git a/c/src/lib/libbsp/i386/pc386/clock/ckinit.c b/c/src/lib/libbsp/i386/pc386/clock/ckinit.c index 956d453747..f3687d1e66 100644 --- a/c/src/lib/libbsp/i386/pc386/clock/ckinit.c +++ b/c/src/lib/libbsp/i386/pc386/clock/ckinit.c @@ -38,11 +38,6 @@ #include #include -/*-------------------------------------------------------------------------+ -| Constants -+--------------------------------------------------------------------------*/ -#define CLOCK_IRQ 0x00 /* Clock IRQ. */ - /*-------------------------------------------------------------------------+ | Macros +--------------------------------------------------------------------------*/ @@ -67,13 +62,12 @@ rtems_device_minor_number rtems_clock_minor; /*-------------------------------------------------------------------------+ | Function: clockIsr -| Description: Interrupt Service Routine for clock (08h) interruption. +| Description: Interrupt Service Routine for clock (0h) interruption. | Global Variables: Clock_driver_ticks, Clock_isrs. | Arguments: vector - standard RTEMS argument - see documentation. | Returns: standard return value - see documentation. +--------------------------------------------------------------------------*/ -static rtems_isr -clockIsr(rtems_vector_number vector) +static void clockIsr() { /*-------------------------------------------------------------------------+ | PLEASE NOTE: The following is directly transcribed from the go32 BSP for @@ -98,10 +92,8 @@ clockIsr(rtems_vector_number vector) else Clock_isrs--; - PC386_ackIrq(vector - PC386_IRQ_VECTOR_BASE); } /* clockIsr */ - /*-------------------------------------------------------------------------+ | Function: Clock_exit | Description: Clock cleanup routine at RTEMS exit. NOTE: This routine is @@ -110,7 +102,7 @@ clockIsr(rtems_vector_number vector) | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ -void Clock_exit(void) +void clockOff(const rtems_irq_connect_data* unused) { if (BSP_Configuration.ticks_per_timeslice) { @@ -129,13 +121,10 @@ void Clock_exit(void) | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ -void -Install_clock(rtems_isr_entry isr) +static void clockOn(const rtems_irq_connect_data* unused) { rtems_unsigned32 microseconds_per_isr; - rtems_status_code status; - #if 0 /* Initialize clock from on-board real time clock. This breaks the */ /* test code which assumes which assumes the application will do it. */ @@ -175,22 +164,25 @@ Install_clock(rtems_isr_entry isr) /* 105/88 approximates TIMER_TICK * 1e-6 */ rtems_unsigned32 count = US_TO_TICK(microseconds_per_isr); - status = PC386_installRtemsIrqHandler(CLOCK_IRQ, isr); - - if (status != RTEMS_SUCCESSFUL) - { - printk("Error installing clock interrupt handler!\n"); - rtems_fatal_error_occurred(status); - } - outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); outport_byte(TIMER_CNTR0, count >> 0 & 0xff); outport_byte(TIMER_CNTR0, count >> 8 & 0xff); } - atexit(Clock_exit); -} /* Install_clock */ +} +int clockIsOn(const rtems_irq_connect_data* unused) +{ + return ((i8259s_cache & 0x1) == 0); +} + +static rtems_irq_connect_data clockIrqData = {PC_386_PERIODIC_TIMER, + clockIsr, + clockOn, + clockOff, + clockIsOn}; + + /*-------------------------------------------------------------------------+ | Clock device driver INITIALIZE entry point. @@ -202,8 +194,11 @@ Clock_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *pargp) { - Install_clock(clockIsr); /* Install the interrupt handler */ - + + if (!pc386_install_rtems_irq_handler (&clockIrqData)) { + printk("Unable to initialize system clock\n"); + rtems_fatal_error_occurred(1); + } /* make major/minor avail to others such as shared memory driver */ rtems_clock_major = major; @@ -212,7 +207,7 @@ Clock_initialize(rtems_device_major_number major, return RTEMS_SUCCESSFUL; } /* Clock_initialize */ - + /*-------------------------------------------------------------------------+ | Console device driver CONTROL entry point +--------------------------------------------------------------------------*/ @@ -231,17 +226,12 @@ Clock_control(rtems_device_major_number major, +-------------------------------------------------------------------------*/ if (args->command == rtems_build_name('I', 'S', 'R', ' ')) - clockIsr(PC386_IRQ_VECTOR_BASE + CLOCK_IRQ); + clockIsr(); else if (args->command == rtems_build_name('N', 'E', 'W', ' ')) { - rtems_status_code status; - - status = PC386_installRtemsIrqHandler(CLOCK_IRQ, clockIsr); - - if (status != RTEMS_SUCCESSFUL) - { + if (!pc386_install_rtems_irq_handler (&clockIrqData)) { printk("Error installing clock interrupt handler!\n"); - rtems_fatal_error_occurred(status); + rtems_fatal_error_occurred(1); } } } @@ -249,6 +239,10 @@ Clock_control(rtems_device_major_number major, return RTEMS_SUCCESSFUL; } /* Clock_control */ +void Clock_exit() +{ + pc386_remove_rtems_irq_handler (&clockIrqData); +} /*-------------------------------------------------------------------------+ | PLEASE NOTE: The following is directly transcribed from the go32 BSP for diff --git a/c/src/lib/libbsp/i386/pc386/console/console.c b/c/src/lib/libbsp/i386/pc386/console/console.c index 29ec37ec56..c8d9a2c4f4 100644 --- a/c/src/lib/libbsp/i386/pc386/console/console.c +++ b/c/src/lib/libbsp/i386/pc386/console/console.c @@ -45,17 +45,20 @@ int PC386ConsolePort = PC386_CONSOLE_PORT_CONSOLE; static int conSetAttr(int minor, const struct termios *); -/*-------------------------------------------------------------------------+ -| Constants -+--------------------------------------------------------------------------*/ -#define KEYBOARD_IRQ 0x01 /* Keyboard IRQ. */ - - /*-------------------------------------------------------------------------+ | External Prototypes +--------------------------------------------------------------------------*/ -extern rtems_isr _IBMPC_keyboard_isr(rtems_vector_number); - /* keyboard (IRQ 0x01) Interrupt Service Routine (defined in 'inch.c') */ +extern void _IBMPC_keyboard_isr(void); +extern void _IBMPC_keyboard_isr_on(const rtems_irq_connect_data*); +extern void _IBMPC_keyboard_isr_off(const rtems_irq_connect_data*); +extern int _IBMPC_keyboard_isr_is_on(const rtems_irq_connect_data*); + +static rtems_irq_connect_data console_isr_data = {PC_386_KEYBOARD, + _IBMPC_keyboard_isr, + _IBMPC_keyboard_isr_on, + _IBMPC_keyboard_isr_off, + _IBMPC_keyboard_isr_is_on}; + extern rtems_boolean _IBMPC_scankey(char *); /* defined in 'inch.c' */ @@ -164,9 +167,9 @@ console_initialize(rtems_device_major_number major, { /* Install keyboard interrupt handler */ - status = PC386_installRtemsIrqHandler(KEYBOARD_IRQ, _IBMPC_keyboard_isr); - - if (status != RTEMS_SUCCESSFUL) + status = pc386_install_rtems_irq_handler(&console_isr_data); + + if (!status) { printk("Error installing keyboard interrupt handler!\n"); rtems_fatal_error_occurred(status); @@ -198,16 +201,18 @@ console_initialize(rtems_device_major_number major, /* Set interrupt handler */ if(PC386ConsolePort == PC386_UART_COM1) { - status = PC386_installRtemsIrqHandler(PC386_UART_COM1_IRQ, - PC386_uart_termios_isr_com1); + console_isr_data.name = PC386_UART_COM1_IRQ; + console_isr_data.hdl = PC386_uart_termios_isr_com1; + } else { assert(PC386ConsolePort == PC386_UART_COM2); - - status = PC386_installRtemsIrqHandler(PC386_UART_COM2_IRQ, - PC386_uart_termios_isr_com2); + console_isr_data.name = PC386_UART_COM2_IRQ; + console_isr_data.hdl = PC386_uart_termios_isr_com2; } + + status =pc386_install_rtems_irq_handler(&console_isr_data); /* * Register the device */ @@ -291,12 +296,15 @@ console_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { + rtems_device_driver res = RTEMS_SUCCESSFUL; + if(PC386ConsolePort != PC386_CONSOLE_PORT_CONSOLE) { - return rtems_termios_close (arg); + res = rtems_termios_close (arg); } - - return RTEMS_SUCCESSFUL; + pc386_remove_rtems_irq_handler (&console_isr_data); + + return res; } /* console_close */ diff --git a/c/src/lib/libbsp/i386/pc386/console/inch.c b/c/src/lib/libbsp/i386/pc386/console/inch.c index 9159e62ab4..39c29f93e0 100644 --- a/c/src/lib/libbsp/i386/pc386/console/inch.c +++ b/c/src/lib/libbsp/i386/pc386/console/inch.c @@ -210,6 +210,17 @@ _IBMPC_scankey(char *outChar) return TRUE; } /* _IBMPC_scankey */ +void _IBMPC_keyboard_isr_on(const rtems_irq_connect_data* unused) +{} +void _IBMPC_keyboard_isr_off(const rtems_irq_connect_data* unused) +{} + +int _IBMPC_keyboard_isr_is_on(const rtems_irq_connect_data* irq) +{ + return pc386_irq_enabled_at_i8259s (irq->name); +} + + /*-------------------------------------------------------------------------+ | Function: _IBMPC_keyboard_isr @@ -218,8 +229,7 @@ _IBMPC_scankey(char *outChar) | Arguments: vector - standard RTEMS argument - see documentation. | Returns: standard return value - see documentation. +--------------------------------------------------------------------------*/ -rtems_isr -_IBMPC_keyboard_isr(rtems_vector_number vector) +void _IBMPC_keyboard_isr() { if (_IBMPC_scankey(&kbd_buffer[kbd_last])) { @@ -231,8 +241,6 @@ _IBMPC_keyboard_isr(rtems_vector_number vector) kbd_last = next; } } - - PC386_ackIrq(vector - PC386_IRQ_VECTOR_BASE); /* Mark interrupt as handled. */ } /* _IBMPC_keyboard_isr */ @@ -281,6 +289,21 @@ _IBMPC_inch(void) return c; } /* _IBMPC_inch */ + + /* + * Routine that can be used before interrupt management is initialized. + */ + +char +debugPollingGetChar(void) +{ + char c; + while (!_IBMPC_scankey(&c)) + continue; + + return c; +} + /*-------------------------------------------------------------------------+ | Function: _IBMPC_inch_sleep | Description: If charcter is ready return it, otherwise sleep until diff --git a/c/src/lib/libbsp/i386/pc386/include/Makefile.in b/c/src/lib/libbsp/i386/pc386/include/Makefile.in index 49e4168eca..10cf41f1a3 100644 --- a/c/src/lib/libbsp/i386/pc386/include/Makefile.in +++ b/c/src/lib/libbsp/i386/pc386/include/Makefile.in @@ -8,8 +8,8 @@ VPATH = @srcdir@ RTEMS_ROOT = @top_srcdir@ PROJECT_ROOT = @PROJECT_ROOT@ -H_FILES = $(srcdir)/bsp.h $(srcdir)/coverhd.h $(srcdir)/irq.h \ - $(srcdir)/crt.h $(srcdir)/pc386uart.h $(srcdir)/pcibios.h +H_FILES = $(srcdir)/bsp.h $(srcdir)/coverhd.h $(srcdir)/crt.h \ + $(srcdir)/pc386uart.h $(srcdir)/pcibios.h # # Equate files are for including from assembly preprocessed by diff --git a/c/src/lib/libbsp/i386/pc386/include/bsp.h b/c/src/lib/libbsp/i386/pc386/include/bsp.h index c7920f385b..1e7a4a6506 100644 --- a/c/src/lib/libbsp/i386/pc386/include/bsp.h +++ b/c/src/lib/libbsp/i386/pc386/include/bsp.h @@ -51,7 +51,8 @@ extern "C" { #include #include #include - +#include + /*-------------------------------------------------------------------------+ | Constants +--------------------------------------------------------------------------*/ @@ -133,8 +134,11 @@ extern "C" { /*-------------------------------------------------------------------------+ | External Variables. +--------------------------------------------------------------------------*/ -extern i386_IDT_slot Interrupt_descriptor_table[]; -extern i386_GDT_slot Global_descriptor_table []; +#define IDT_SIZE 256 +#define GDT_SIZE 3 + +extern interrupt_gate_descriptor Interrupt_descriptor_table[IDT_SIZE]; +extern segment_descriptors Global_descriptor_table [GDT_SIZE]; extern rtems_configuration_table BSP_Configuration; /* User provided BSP configuration table. */ diff --git a/c/src/lib/libbsp/i386/pc386/start/start.s b/c/src/lib/libbsp/i386/pc386/start/start.s index fae56137e5..36fad8153c 100644 --- a/c/src/lib/libbsp/i386/pc386/start/start.s +++ b/c/src/lib/libbsp/i386/pc386/start/start.s @@ -104,7 +104,8 @@ speakl: jmp speakl # and SPIN!!! call printk addl $4, esp - /*call debugPollingGetChar */ + call debugPollingGetChar + #endif /*----------------------------------------------------------------------------+ diff --git a/c/src/lib/libbsp/i386/pc386/startup/Makefile.in b/c/src/lib/libbsp/i386/pc386/startup/Makefile.in index 1c8f298249..a4fe0e4cb2 100644 --- a/c/src/lib/libbsp/i386/pc386/startup/Makefile.in +++ b/c/src/lib/libbsp/i386/pc386/startup/Makefile.in @@ -11,14 +11,14 @@ PROJECT_ROOT = @PROJECT_ROOT@ PGM=${ARCH}/startup.rel # C source names, if any, go here -- minus the .c -C_PIECES=bspclean bsplibc bsppost bspstart exit irq main sbrk +C_PIECES=bspclean bsplibc bsppost bspstart exit irq irq_init main sbrk C_FILES=$(C_PIECES:%=%.c) C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) H_FILES= # Assembly source names, if any, go here -- minus the .s -S_PIECES=ldsegs +S_PIECES=ldsegs irq_asm S_FILES=$(S_PIECES:%=%.s) S_O_FILES=$(S_FILES:%.s=${ARCH}/%.o) @@ -50,6 +50,12 @@ LDFLAGS += CLEAN_ADDITIONS += CLOBBER_ADDITIONS += +IMPORT_SRC=$(srcdir)/../../shared/irq/irq.c \ + $(srcdir)/../../shared/irq/irq_init.c $(srcdir)/../../shared/irq/irq_asm.s + +preinstall: + ${CP} ${IMPORT_SRC} . + ${PGM}: ${SRCS} ${OBJS} $(make-rel) all: ${ARCH} $(SRCS) $(PGM) diff --git a/c/src/lib/libbsp/i386/pc386/startup/bspstart.c b/c/src/lib/libbsp/i386/pc386/startup/bspstart.c index 607e816d6d..744808b3be 100644 --- a/c/src/lib/libbsp/i386/pc386/startup/bspstart.c +++ b/c/src/lib/libbsp/i386/pc386/startup/bspstart.c @@ -65,7 +65,8 @@ char *rtems_progname; /* Program name - from main(). */ extern void _exit(int); /* define in exit.c */ void bsp_libc_init( void *, unsigned32, int ); void bsp_postdriver_hook(void); - +extern void rtems_irq_mngt_init(); + /*-------------------------------------------------------------------------+ | Function: bsp_pretasking_hook | Description: BSP pretasking hook. Called just before drivers are @@ -128,6 +129,11 @@ void bsp_start( void ) console_reserve_resources(&BSP_Configuration); + /* + * Init trems_interrupt_management + */ + rtems_irq_mngt_init(); + /* * The following information is very useful when debugging. */ diff --git a/c/src/lib/libbsp/i386/pc386/startup/ldsegs.s b/c/src/lib/libbsp/i386/pc386/startup/ldsegs.s index f2171575ce..8805128e69 100644 --- a/c/src/lib/libbsp/i386/pc386/startup/ldsegs.s +++ b/c/src/lib/libbsp/i386/pc386/startup/ldsegs.s @@ -45,12 +45,13 @@ /*----------------------------------------------------------------------------+ | CODE section +----------------------------------------------------------------------------*/ - +EXTERN (rtems_i8259_masks) + BEGIN_CODE EXTERN (_establish_stack) EXTERN (Timer_exit) - EXTERN (Clock_exit) + EXTERN (clockOff) .p2align 4 /*----------------------------------------------------------------------------+ @@ -95,25 +96,6 @@ next_step: movw ax, fs movw ax, gs - /* Set default interrupt handler */ - movl $0, ecx - movl $Interrupt_descriptor_table, eax - movl $_default_int_handler, ebx - movl ebx, edx - sarl $16, edx -loop: - movw bx, (eax) - movw $0x8, 2(eax) - movw $0x8e00, 4(eax) - movw dx, 8(eax) - addl $8, eax - addl $1, ecx - cmpl $255, ecx - jle loop - - - - /*---------------------------------------------------------------------+ | Now we have to reprogram the interrupts :-(. We put them right after | the intel-reserved hardware interrupts, at int 0x20-0x2F. There they @@ -157,6 +139,8 @@ loop: outb al, $0x21 /* is cascaded */ call SYM(delay) + movw $0xFFFB, SYM(i8259s_cache) /* set up same values in cache */ + jmp SYM (_establish_stack) # return to the bsp entry code /*-------------------------------------------------------------------------+ @@ -185,10 +169,6 @@ SYM (_return_to_monitor): +--------------------------------------------------------------------------*/ .p2align 4 - PUBLIC (_default_int_handler) -SYM (_default_int_handler): - iret - /*---------------------------------------------------------------------------+ | GDT itself +--------------------------------------------------------------------------*/ diff --git a/c/src/lib/libbsp/i386/pc386/timer/timer.c b/c/src/lib/libbsp/i386/pc386/timer/timer.c index ea7b5c5219..650c6a5a5e 100644 --- a/c/src/lib/libbsp/i386/pc386/timer/timer.c +++ b/c/src/lib/libbsp/i386/pc386/timer/timer.c @@ -43,7 +43,6 @@ /*-------------------------------------------------------------------------+ | Constants +--------------------------------------------------------------------------*/ -#define TIMER_IRQ 0x00 /* Timer IRQ. */ #define AVG_OVERHEAD 0 /* 0.1 microseconds to start/stop timer. */ #define LEAST_VALID 1 /* Don't trust a value lower than this. */ @@ -53,6 +52,13 @@ volatile rtems_unsigned32 Ttimer_val; rtems_boolean Timer_driver_Find_average_overhead = TRUE; +/*-------------------------------------------------------------------------+ +| External Prototypes ++--------------------------------------------------------------------------*/ +extern void timerisr(); + /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */ +extern int clockIsOn(const rtems_irq_connect_data*); + /*-------------------------------------------------------------------------+ | Pentium optimized timer handling. +--------------------------------------------------------------------------*/ @@ -86,7 +92,6 @@ rdtsc(void) void Timer_exit(void) { - PC386_disableIrq(TIMER_IRQ); } /* Timer_exit */ @@ -107,8 +112,6 @@ Timer_initialize(void) First = FALSE; atexit(Timer_exit); /* Try not to hose the system at exit. */ - PC386_enableIrq(TIMER_IRQ); - /* Disable the programmable timer so ticks don't interfere. */ } Ttimer_val = rdtsc(); /* read starting time */ } /* Timer_initialize */ @@ -143,11 +146,48 @@ Read_timer(void) +--------------------------------------------------------------------------*/ #define US_PER_ISR 250 /* Number of micro-seconds per timer interruption */ + /*-------------------------------------------------------------------------+ -| External Prototypes +| Function: Timer_exit +| Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is +| not really necessary, since there will be a reset at exit. +| Global Variables: None. +| Arguments: None. +| Returns: Nothing. +--------------------------------------------------------------------------*/ -extern rtems_isr timerisr(rtems_vector_number); - /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */ +void +timerOff(const rtems_raw_irq_connect_data* used) +{ + /* + * disable interrrupt at i8259 level + */ + pc386_irq_disable_at_i8259s(used->idtIndex - PC386_IRQ_VECTOR_BASE); + /* reset timer mode to standard (DOS) value */ + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); + outport_byte(TIMER_CNTR0, 0); + outport_byte(TIMER_CNTR0, 0); +} /* Timer_exit */ + + +void timerOn(const rtems_raw_irq_connect_data* used) +{ + /* load timer for US_PER_ISR microsecond period */ + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); + outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff); + outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff); + /* + * disable interrrupt at i8259 level + */ + pc386_irq_enable_at_i8259s(used->idtIndex - PC386_IRQ_VECTOR_BASE); +} + +static rtems_raw_irq_connect_data timer_raw_irq_data = { + PC_386_PERIODIC_TIMER + PC386_IRQ_VECTOR_BASE, + timerisr, + timerOn, + timerOff, + clockIsOn +}; /*-------------------------------------------------------------------------+ | Function: Timer_exit @@ -160,13 +200,9 @@ extern rtems_isr timerisr(rtems_vector_number); void Timer_exit(void) { - /* reset timer mode to standard (DOS) value */ - outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); - outport_byte(TIMER_CNTR0, 0); - outport_byte(TIMER_CNTR0, 0); + i386_delete_idt_entry (&timer_raw_irq_data); } /* Timer_exit */ - /*-------------------------------------------------------------------------+ | Function: Timer_initialize | Description: Timer initialization routine. @@ -184,14 +220,10 @@ Timer_initialize(void) First = FALSE; atexit(Timer_exit); /* Try not to hose the system at exit. */ - - /* install a raw interrupt handler for timer */ - PC386_installRawIrqHandler(TIMER_IRQ, timerisr); - - /* load timer for US_PER_ISR microsecond period */ - outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); - outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff); - outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff); + if (!i386_set_idt_entry (&timer_raw_irq_data)) { + printk("raw handler connexion failed\n"); + rtems_fatal_error_occurred(1); + } } /* wait for ISR to be called at least once */ Ttimer_val = 0; diff --git a/c/src/lib/libbsp/i386/shared/Makefile.in b/c/src/lib/libbsp/i386/shared/Makefile.in new file mode 100644 index 0000000000..feab84d468 --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/Makefile.in @@ -0,0 +1,16 @@ +# +# $Id$ +# + +@SET_MAKE@ +srcdir = @srcdir@ +VPATH = @srcdir@ +RTEMS_ROOT = @top_srcdir@ +PROJECT_ROOT = @PROJECT_ROOT@ + +include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg +include $(RTEMS_ROOT)/make/directory.cfg + +# Descend into the $(RTEMS_BSP_FAMILY) directory +SUB_DIRS=irq + diff --git a/c/src/lib/libbsp/i386/shared/irq/Makefile.in b/c/src/lib/libbsp/i386/shared/irq/Makefile.in new file mode 100644 index 0000000000..098852cfa0 --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/Makefile.in @@ -0,0 +1,33 @@ +# +# $Id$ +# + +@SET_MAKE@ +srcdir = @srcdir@ +VPATH = @srcdir@ +RTEMS_ROOT = @top_srcdir@ +PROJECT_ROOT = @PROJECT_ROOT@ + +H_FILES = $(srcdir)/irq.h $(srcdir)/irq_asm.h + +# +# Equate files are for including from assembly preprocessed by +# gm4 or gasp. No examples are provided except for those for +# other CPUs. The best way to generate them would be to +# provide a program which generates the constants used based +# on the C equivalents. +# + +EQ_FILES = + +SRCS=$(H_FILES) $(EQ_FILES) + +include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg +include $(RTEMS_ROOT)/make/leaf.cfg + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +preinstall all: $(SRCS) + $(INSTALL) -m 444 $(H_FILES) $(PROJECT_INCLUDE) + $(INSTALL) -m 444 $(EQ_FILES) $(PROJECT_INCLUDE) diff --git a/c/src/lib/libbsp/i386/shared/irq/idt.c b/c/src/lib/libbsp/i386/shared/irq/idt.c new file mode 100644 index 0000000000..8873de3811 --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/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/libbsp/i386/shared/irq/irq.c b/c/src/lib/libbsp/i386/shared/irq/irq.c new file mode 100644 index 0000000000..026f4953d7 --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/irq.c @@ -0,0 +1,358 @@ +/* irq.c + * + * This file contains the implementation of the function described in irq.h + * + * CopyRight (C) 1998 valette@crf.canon.fr + * + * 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 + +/* + * pointer to the mask representing the additionnal irq vectors + * that must be disabled when a particular entry is activated. + * They will be dynamically computed from teh prioruty table given + * in pc386_rtems_irq_mngt_set(); + * CAUTION : this table is accessed directly by interrupt routine + * prologue. + */ +rtems_i8259_masks irq_mask_or_tbl[PC_386_IRQ_LINES_NUMBER]; + +/* + * Copy of data given via initial pc386_rtems_irq_mngt_set() for + * the sake of efficiency. + * CAUTION : this table is accessed directly by interrupt routine + * prologue. + */ +rtems_irq_hdl current_irq[PC_386_IRQ_LINES_NUMBER]; +/* + * default handler connected on each irq after bsp initialization + */ +static rtems_irq_connect_data default_rtems_entry; + +/* + * location used to store initial tables used for interrupt + * management. + */ +static rtems_irq_global_settings* internal_config; +static rtems_irq_connect_data* rtems_hdl_tbl; +/*-------------------------------------------------------------------------+ +| Cache for 1st and 2nd PIC IRQ line's status (enabled or disabled) register. ++--------------------------------------------------------------------------*/ +/* + * lower byte is interrupt mask on the master PIC. + * while upper bits are interrupt on the slave PIC. + * This cache is initialized in ldseg.s + */ +rtems_i8259_masks i8259s_cache; + +/*-------------------------------------------------------------------------+ +| Function: PC386_irq_disable_at_i8259s +| Description: Mask IRQ line in appropriate PIC chip. +| Global Variables: i8259s_cache +| Arguments: vector_offset - number of IRQ line to mask. +| Returns: Nothing. ++--------------------------------------------------------------------------*/ +int pc386_irq_disable_at_i8259s (const rtems_irq_symbolic_name irqLine) +{ + unsigned short mask; + unsigned int level; + + if ( ((int)irqLine < PC_386_LOWEST_OFFSET) || + ((int)irqLine > PC_386_MAX_OFFSET ) + ) + return 1; + + _CPU_ISR_Disable(level); + + mask = 1 << irqLine; + i8259s_cache |= mask; + + if (irqLine < 8) + { + outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); + } + else + { + outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) > 8)); + } + _CPU_ISR_Enable (level); + + return 0; +} + +/*-------------------------------------------------------------------------+ +| Function: pc386_irq_enable_at_i8259s +| Description: Unmask IRQ line in appropriate PIC chip. +| Global Variables: i8259s_cache +| Arguments: irqLine - number of IRQ line to mask. +| Returns: Nothing. ++--------------------------------------------------------------------------*/ +int pc386_irq_enable_at_i8259s (const rtems_irq_symbolic_name irqLine) +{ + unsigned short mask; + unsigned int level; + + if ( ((int)irqLine < PC_386_LOWEST_OFFSET) || + ((int)irqLine > PC_386_MAX_OFFSET ) + ) + return 1; + + _CPU_ISR_Disable(level); + + mask = ~(1 << irqLine); + i8259s_cache &= mask; + + if (irqLine < 8) + { + outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); + } + else + { + outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) > 8)); + } + _CPU_ISR_Enable (level); + + return 0; +} /* mask_irq */ + +int pc386_irq_enabled_at_i8259s (const rtems_irq_symbolic_name irqLine) +{ + unsigned short mask; + + if ( ((int)irqLine < PC_386_LOWEST_OFFSET) || + ((int)irqLine > PC_386_MAX_OFFSET ) + ) + return 1; + + mask = (1 << irqLine); + return (~(i8259s_cache & mask)); +} + + +/*-------------------------------------------------------------------------+ +| Function: pc386_irq_ack_at_i8259s +| Description: Signal generic End Of Interrupt (EOI) to appropriate PIC. +| Global Variables: None. +| Arguments: irqLine - number of IRQ line to acknowledge. +| Returns: Nothing. ++--------------------------------------------------------------------------*/ +int pc386_irq_ack_at_i8259s (const rtems_irq_symbolic_name irqLine) +{ + if ( ((int)irqLine < PC_386_LOWEST_OFFSET) || + ((int)irqLine > PC_386_MAX_OFFSET ) + ) + return 1; + + if (irqLine >= 8) { + outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI); + } + outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI); + + return 0; + +} /* ackIRQ */ + +/* + * ------------------------ RTEMS Irq helper functions ---------------- + */ + +/* + * Caution : this function assumes the variable "internal_config" + * is already set and that the tables it contains are still valid + * and accessible. + */ +static void compute_i8259_masks_from_prio () +{ + unsigned int i; + unsigned int j; + /* + * Always mask at least current interrupt to prevent re-entrance + */ + for (i=0; i < internal_config->irqNb; i++) { + * ((unsigned short*) &irq_mask_or_tbl[i]) = (1 << i); + for (j = 0; j < internal_config->irqNb; j++) { + /* + * Mask interrupts at i8259 level that have a lower priority + */ + if (internal_config->irqPrioTbl [i] > internal_config->irqPrioTbl [j]) { + * ((unsigned short*) &irq_mask_or_tbl[i]) |= (1 << j); + } + } + } +} + +/* + * Caution : this function assumes the variable "internal_config" + * is already set and that the tables it contains are still valid + * and accessible. + */ +static void make_copy_of_handlers () +{ + int i; + for (i=0; i < internal_config->irqNb; i++) { + current_irq [i] = internal_config->irqHdlTbl[i].hdl; + } +} + + +/* + * This function check that the value given for the irq line + * is valid. + */ + +static int isValidInterrupt(int irq) +{ + if ( (irq < PC_386_LOWEST_OFFSET) || (irq > PC_386_MAX_OFFSET)) + return 0; + return 1; +} + +/* + * ------------------------ RTEMS Single Irq Handler Mngt Routines ---------------- + */ + +int pc386_install_rtems_irq_handler (const rtems_irq_connect_data* irq) +{ + unsigned int level; + + if (!isValidInterrupt(irq->name)) { + 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 (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) { + return 0; + } + _CPU_ISR_Disable(level); + + /* + * store the data provided by user + */ + rtems_hdl_tbl[irq->name] = *irq; + /* + * update table used directly by rtems interrupt prologue + */ + current_irq [irq->name] = irq->hdl; + /* + * Enable interrupt at PIC level + */ + pc386_irq_enable_at_i8259s (irq->name); + /* + * Enable interrupt on device + */ + irq->on(irq); + + _CPU_ISR_Enable(level); + + return 1; +} + + +int pc386_get_current_rtems_irq_handler (rtems_irq_connect_data* irq) +{ + if (!isValidInterrupt(irq->name)) { + return 0; + } + *irq = rtems_hdl_tbl[irq->name]; + return 1; +} + +int pc386_remove_rtems_irq_handler (const rtems_irq_connect_data* irq) +{ + unsigned int level; + + if (!isValidInterrupt(irq->name)) { + 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 (rtems_hdl_tbl[irq->name].hdl != irq->hdl) { + return 0; + } + _CPU_ISR_Disable(level); + + /* + * disable interrupt at PIC level + */ + pc386_irq_disable_at_i8259s (irq->name); + + /* + * Disable interrupt on device + */ + irq->off(irq); + + /* + * restore the default irq value + */ + rtems_hdl_tbl[irq->name] = default_rtems_entry; + + current_irq[irq->name] = default_rtems_entry.hdl; + + _CPU_ISR_Enable(level); + + return 1; +} + +/* + * ------------------------ RTEMS Global Irq Handler Mngt Routines ---------------- + */ + +int pc386_rtems_irq_mngt_set(rtems_irq_global_settings* config) +{ + int i; + unsigned int level; + /* + * Store various code accelerators + */ + internal_config = config; + default_rtems_entry = config->defaultEntry; + rtems_hdl_tbl = config->irqHdlTbl; + + _CPU_ISR_Disable(level); + /* + * set up internal tables used by rtems interrupt prologue + */ + compute_i8259_masks_from_prio (); + make_copy_of_handlers (); + + for (i=0; i < internal_config->irqNb; i++) { + if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) { + pc386_irq_enable_at_i8259s (i); + rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); + } + else { + rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); + pc386_irq_disable_at_i8259s (i); + } + } + /* + * must disable slave pic anyway + */ + pc386_irq_enable_at_i8259s (2); + _CPU_ISR_Enable(level); + return 1; +} + +int pc386_rtems_irq_mngt_get(rtems_irq_global_settings** config) +{ + *config = internal_config; + return 0; +} diff --git a/c/src/lib/libbsp/i386/shared/irq/irq.h b/c/src/lib/libbsp/i386/shared/irq/irq.h new file mode 100644 index 0000000000..3fba2db09e --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/irq.h @@ -0,0 +1,260 @@ +/* irq.h + * + * This include file describe the data structure and the functions implemented + * by rtems to write interrupt handlers. + * + * CopyRight (C) 1998 valette@crf.canon.fr + * + * This code is heavilly inspired by the public specification of STREAM V2 + * that can be found at : + * + * by following + * the STREAM API Specification Document link. + * + * 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 _IRQ_H_ +#define _IRQ_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Include some preprocessor value also used by assember code + */ + +#include +#include +/*-------------------------------------------------------------------------+ +| Constants ++--------------------------------------------------------------------------*/ + +typedef enum { + /* Base vector for our IRQ handlers. */ + PC386_IRQ_VECTOR_BASE = PC386_ASM_IRQ_VECTOR_BASE, + PC_386_IRQ_LINES_NUMBER = 16, + PC_386_LOWEST_OFFSET = 0, + PC_386_MAX_OFFSET = PC_386_IRQ_LINES_NUMBER - 1, + /* + * Interrupt offset in comparison to PC386_ASM_IRQ_VECTOR_BASE + * NB : 1) Interrupt vector number in IDT = offset + PC386_ASM_IRQ_VECTOR_BASE + * 2) The same name should be defined on all architecture + * so that handler connexion can be unchanged. + */ + PC_386_PERIODIC_TIMER = 0, + + PC_386_KEYBOARD = 1, + + PC386_UART_COM2_IRQ = 3, + + PC386_UART_COM1_IRQ = 4, + + PC_386_RT_TIMER1 = 8 + +}rtems_irq_symbolic_name; + + + + +/* + * Type definition for RTEMS managed interrupts + */ +typedef unsigned char rtems_irq_prio; +typedef unsigned short rtems_i8259_masks; + +extern rtems_i8259_masks i8259s_cache; + +struct __rtems_irq_connect_data__; /* forward declaratiuon */ + +typedef void (*rtems_irq_hdl) (void); +typedef void (*rtems_irq_enable) (const struct __rtems_irq_connect_data__*); +typedef void (*rtems_irq_disable) (const struct __rtems_irq_connect_data__*); +typedef int (*rtems_irq_is_enabled) (const struct __rtems_irq_connect_data__*); + +typedef struct __rtems_irq_connect_data__ { + /* + * IRQ line + */ + rtems_irq_symbolic_name name; + /* + * handler. See comment on handler properties below in function prototype. + */ + rtems_irq_hdl hdl; + /* + * function for enabling interrupts at device level (ONLY!). + * The BSP code will automatically enable it at i8259s level. + * RATIONALE : anyway such code has to exist in current driver code. + * It is usually called immediately AFTER connecting the interrupt handler. + * RTEMS may well need such a function when restoring normal interrupt + * processing after a debug session. + * + */ + rtems_irq_enable on; + /* + * function for disabling interrupts at device level (ONLY!). + * The code will disable it at i8259s level. RATIONALE : anyway + * such code has to exist for clean shutdown. It is usually called + * BEFORE disconnecting the interrupt. RTEMS may well need such + * a function when disabling normal interrupt processing for + * a debug session. May well be a NOP function. + */ + rtems_irq_disable off; + /* + * function enabling to know what interrupt may currently occur + * if someone manipulates the i8259s interrupt mask without care... + */ + rtems_irq_is_enabled isOn; +}rtems_irq_connect_data; + +typedef struct { + /* + * size of all the table fields (*Tbl) described below. + */ + unsigned int irqNb; + /* + * Default handler used when disconnecting interrupts. + */ + rtems_irq_connect_data defaultEntry; + /* + * Table containing initials/current value. + */ + rtems_irq_connect_data* irqHdlTbl; + /* + * actual value of PC386_IRQ_VECTOR_BASE... + */ + rtems_irq_symbolic_name irqBase; + /* + * software priorities associated with interrupts. + * if irqPrio [i] > intrPrio [j] it means that + * interrupt handler hdl connected for interrupt name i + * will not be interrupted by the handler connected for interrupt j + * The interrupt source will be physically masked at i8259 level. + */ + rtems_irq_prio* irqPrioTbl; +}rtems_irq_global_settings; + + + + +/*-------------------------------------------------------------------------+ +| Function Prototypes. ++--------------------------------------------------------------------------*/ +/* + * ------------------------ Intel 8259 (or emulation) Mngt Routines ------- + */ + +/* + * function to disable a particular irq at 8259 level. After calling + * this function, even if the device asserts the interrupt line it will + * not be propagated further to the processor + */ +int pc386_irq_disable_at_i8259s (const rtems_irq_symbolic_name irqLine); +/* + * function to enable a particular irq at 8259 level. After calling + * this function, if the device asserts the interrupt line it will + * be propagated further to the processor + */ +int pc386_irq_enable_at_i8259s (const rtems_irq_symbolic_name irqLine); +/* + * function to acknoledge a particular irq at 8259 level. After calling + * this function, if a device asserts an enabled interrupt line it will + * be propagated further to the processor. Mainly usefull for people + * writting raw handlers as this is automagically done for rtems managed + * handlers. + */ +int pc386_irq_ack_at_i8259s (const rtems_irq_symbolic_name irqLine); +/* + * function to check if a particular irq is enabled at 8259 level. After calling + */ +int pc386_irq_enabled_at_i8259s (const rtems_irq_symbolic_name irqLine); +/* + * ------------------------ RTEMS Single Irq Handler Mngt Routines ---------------- + */ +/* + * function to connect a particular irq handler. This hanlder will NOT be called + * directly as the result of the corresponding interrupt. Instead, a RTEMS + * irq prologue will be called that will : + * + * 1) save the C scratch registers, + * 2) switch to a interrupt stack if the interrupt is not nested, + * 3) store the current i8259s' interrupt masks + * 4) modify them to disable the current interrupt at 8259 level (and may + * be others depending on software priorities) + * 5) aknowledge the i8259s', + * 6) demask the processor, + * 7) call the application handler + * + * As a result the hdl function provided + * + * a) can perfectly be written is C, + * b) may also well directly call the part of the RTEMS API that can be used + * from interrupt level, + * c) It only responsible for handling the jobs that need to be done at + * the device level including (aknowledging/re-enabling the interrupt at device, + * level, getting the data,...) + * + * When returning from the function, the following will be performed by + * the RTEMS irq epilogue : + * + * 1) masks the interrupts again, + * 2) restore the original i8259s' interrupt masks + * 3) switch back on the orinal stack if needed, + * 4) perform rescheduling when necessary, + * 5) restore the C scratch registers... + * 6) restore initial execution flow + * + */ +int pc386_install_rtems_irq_handler (const rtems_irq_connect_data*); +/* + * function to get the current RTEMS irq handler for ptr->name. It enables to + * define hanlder chain... + */ +int pc386_get_current_rtems_irq_handler (rtems_irq_connect_data* ptr); +/* + * function to get disconnect the RTEMS irq handler for ptr->name. + * This function checks that the value given is the current one for safety reason. + * The user can use the previous function to get it. + */ +int pc386_remove_rtems_irq_handler (const rtems_irq_connect_data*); + +/* + * ------------------------ RTEMS Global Irq Handler Mngt Routines ---------------- + */ +/* + * (Re) Initialize the RTEMS interrupt management. + * + * The result of calling this function will be the same as if each individual + * handler (config->irqHdlTbl[i].hdl) different from "config->defaultEntry.hdl" + * has been individualy connected via + * pc386_install_rtems_irq_handler(&config->irqHdlTbl[i]) + * And each handler currently equal to config->defaultEntry.hdl + * has been previously disconnected via + * pc386_remove_rtems_irq_handler (&config->irqHdlTbl[i]) + * + * This is to say that all information given will be used and not just + * only the space. + * + * CAUTION : the various table address contained in config will be used + * directly by the interrupt mangement code in order to save + * data size so they must stay valid after the call => they should + * not be modified or declared on a stack. + */ + +int pc386_rtems_irq_mngt_set(rtems_irq_global_settings* config); +/* + * (Re) get info on current RTEMS interrupt management. + */ +int pc386_rtems_irq_mngt_get(rtems_irq_global_settings**); + +#ifdef __cplusplus +} +#endif + +#endif /* _IRQ_H_ */ +/* end of include file */ diff --git a/c/src/lib/libbsp/i386/shared/irq/irq_asm.h b/c/src/lib/libbsp/i386/shared/irq/irq_asm.h new file mode 100644 index 0000000000..bf6bb17e8e --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/irq_asm.h @@ -0,0 +1,30 @@ +/* irq_asm.h + * + * This include file has defines to represent some contant used + * to program and manage the Intel 8259 interrupt controller + * + * + * COPYRIGHT (c) 1998 valette@crf.canon.fr + * + * 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 __IRQ_ASM_H__ +#define __IRQ_ASM_H__ + +#define PC386_ASM_IRQ_VECTOR_BASE 0x20 + /* PIC's command and mask registers */ +#define PIC_MASTER_COMMAND_IO_PORT 0x20 /* Master PIC command register */ +#define PIC_SLAVE_COMMAND_IO_PORT 0xa0 /* Slave PIC command register */ +#define PIC_MASTER_IMR_IO_PORT 0x21 /* Master PIC Interrupt Mask Register */ +#define PIC_SLAVE_IMR_IO_PORT 0xa1 /* Slave PIC Interrupt Mask Register */ + + /* Command for specific EOI (End Of Interrupt): Interrupt acknowledge */ +#define PIC_EOSI 0x60 /* End of Specific Interrupt (EOSI) */ +#define PIC_EOI 0x20 /* Generic End of Interrupt (EOI) */ + + +#endif diff --git a/c/src/lib/libbsp/i386/shared/irq/irq_asm.s b/c/src/lib/libbsp/i386/shared/irq/irq_asm.s new file mode 100644 index 0000000000..7765a9dd22 --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/irq_asm.s @@ -0,0 +1,261 @@ +/* irq.c + * + * This file contains the implementation of the function described in irq.h + * + * CopyRight (C) 1998 valette@crf.canon.fr + * + * 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 "asm.h" +#include + +.set SAVED_REGS , 32 # space consumed by saved regs +.set EIP_OFFSET , SAVED_REGS # offset of tasks eip +.set CS_OFFSET , EIP_OFFSET+4 # offset of tasks code segment +.set EFLAGS_OFFSET , CS_OFFSET+4 # offset of tasks eflags + + +/*PAGE + * void _new_ISR_Displatch() + * + * Entry point from the outermost interrupt service routine exit. + * The current stack is the supervisor mode stack. + */ + + PUBLIC (_new_ISR_Displatch) +SYM (_New_ISR_Displatch): + + call SYM (_Thread_Dispatch) # invoke Dispatcher + + /* + * BEGINNING OF DE-ESTABLISH SEGMENTS + * + * NOTE: Make sure there is code here if code is added to + * load the segment registers. + * + */ + + /***** DE-ESTABLISH SEGMENTS CODE GOES HERE ****/ + + /* + * END OF DE-ESTABLISH SEGMENTS + */ + + popa # restore general registers + iret # return to interrupted thread + + +SYM (_New_ISR_Handler): + /* + * Before this was point is reached the vectors unique + * entry point did the following: + * + * 1. saved sctach registers registers eax edx ecx" + * 2. put the vector number in ecx. + * + * BEGINNING OF ESTABLISH SEGMENTS + * + * WARNING: If an interrupt can occur when the segments are + * not correct, then this is where we should establish + * the segments. In addition to establishing the + * segments, it may be necessary to establish a stack + * in the current data area on the outermost interrupt. + * + * NOTE: If the previous values of the segment registers are + * pushed, do not forget to adjust SAVED_REGS. + * + * NOTE: Make sure the exit code which restores these + * when this type of code is needed. + */ + + /***** ESTABLISH SEGMENTS CODE GOES HERE ******/ + + /* + * END OF ESTABLISH SEGMENTS + */ + + /* + * Now switch stacks if necessary + */ + + movw SYM (i8259s_cache), ax # move current i8259 interrupt mask in ax + pushl eax # push it on the stack + /* + * compute the new PIC mask: + * + * = | irq_mask_or_tbl[] + */ + movw SYM (irq_mask_or_tbl) (,ecx,2), dx + orw dx, ax + /* + * Install new computed value on the i8259 and update cache + * accordingly + */ + movw ax, SYM (i8259s_cache) + outb $PIC_MASTER_IMR_IO_PORT + movb ah, al + outb $PIC_SLAVE_IMR_IO_PORT + + /* + * acknowledge the interrupt + * + */ + movb $PIC_EOI, al + cmpl $7, ecx + jbe .master + outb $PIC_SLAVE_COMMAND_IO_PORT +.master: + outb $PIC_MASTER_COMMAND_IO_PORT + +.check_stack_switch: + pushl ebp + movl esp, ebp # ebp = previous stack pointer + cmpl $0, SYM (_ISR_Nest_level) # is this the outermost interrupt? + jne nested # No, then continue + movl SYM (_CPU_Interrupt_stack_high), esp + + /* + * We want to insure that the old stack pointer is on the + * stack we will be on at the end of the ISR when we restore it. + * By saving it on every interrupt, all we have to do is pop it + * near the end of every interrupt. + */ + +nested: + incl SYM (_ISR_Nest_level) # one nest level deeper + incl SYM (_Thread_Dispatch_disable_level) # disable multitasking + /* + * re-enable interrupts at processor level as the current + * interrupt source is now masked via i8259 + */ + sti + + /* + * ECX is preloaded with the vector number but it is a scratch register + * so we must save it again. + */ + + pushl ecx # push vector number + mov SYM (current_irq) (,ecx,4),eax + # eax = Users handler + call *eax # invoke user ISR + /* + * disable interrupts_again + */ + cli + popl ecx # ecx = vector number + /* + * restore stack + */ + movl ebp, esp + popl ebp + + /* + * restore the original i8259 masks + */ + popl eax + movw ax, SYM (i8259s_cache) + outb $PIC_MASTER_IMR_IO_PORT + movb ah, al + outb $PIC_SLAVE_IMR_IO_PORT + + + decl SYM (_ISR_Nest_level) # one less ISR nest level + # If interrupts are nested, + # then dispatching is disabled + + decl SYM (_Thread_Dispatch_disable_level) + # unnest multitasking + # Is dispatch disabled + jne .exit # Yes, then exit + + cmpl $0, SYM (_Context_Switch_necessary) + # Is task switch necessary? + jne bframe # Yes, then build stack + + cmpl $0, SYM (_ISR_Signals_to_thread_executing) + # signals sent to Run_thread + # while in interrupt handler? + je .exit # No, exit + +bframe: + movl $0, SYM (_ISR_Signals_to_thread_executing) + /* + * complete code as if a pusha had been executed on entry + */ + pushl ebx + pushl esp + pushl ebp + pushl esi + pushl edi + # push the isf for Isr_dispatch + pushl EFLAGS_OFFSET(esp) # push tasks eflags + push cs # cs of Isr_dispatch + pushl $ SYM (_New_ISR_Displatch)# entry point + iret + +.exit: + /* + * BEGINNING OF DE-ESTABLISH SEGMENTS + * + * NOTE: Make sure there is code here if code is added to + * load the segment registers. + * + */ + + /******* DE-ESTABLISH SEGMENTS CODE GOES HERE ********/ + + /* + * END OF DE-ESTABLISH SEGMENTS + */ + popl edx + popl ecx + popl eax + iret + +#define DISTINCT_INTERRUPT_ENTRY(_vector) \ + .p2align 4 ; \ + PUBLIC (rtems_irq_prologue_ ## _vector ) ; \ +SYM (rtems_irq_prologue_ ## _vector ): \ + pushl eax ; \ + pushl ecx ; \ + pushl edx ; \ + movl $ _vector, ecx ; \ + jmp SYM (_New_ISR_Handler) ; + +DISTINCT_INTERRUPT_ENTRY(0) +DISTINCT_INTERRUPT_ENTRY(1) +DISTINCT_INTERRUPT_ENTRY(2) +DISTINCT_INTERRUPT_ENTRY(3) +DISTINCT_INTERRUPT_ENTRY(4) +DISTINCT_INTERRUPT_ENTRY(5) +DISTINCT_INTERRUPT_ENTRY(6) +DISTINCT_INTERRUPT_ENTRY(7) +DISTINCT_INTERRUPT_ENTRY(8) +DISTINCT_INTERRUPT_ENTRY(9) +DISTINCT_INTERRUPT_ENTRY(10) +DISTINCT_INTERRUPT_ENTRY(11) +DISTINCT_INTERRUPT_ENTRY(12) +DISTINCT_INTERRUPT_ENTRY(13) +DISTINCT_INTERRUPT_ENTRY(14) +DISTINCT_INTERRUPT_ENTRY(15) + + /* + * routine used to initialize the IDT by default + */ + +PUBLIC (default_raw_idt_handler) +PUBLIC (raw_idt_notify) + +SYM (default_raw_idt_handler): + pusha + cld + call raw_idt_notify + popa + iret + diff --git a/c/src/lib/libbsp/i386/shared/irq/irq_init.c b/c/src/lib/libbsp/i386/shared/irq/irq_init.c new file mode 100644 index 0000000000..0910bdef56 --- /dev/null +++ b/c/src/lib/libbsp/i386/shared/irq/irq_init.c @@ -0,0 +1,200 @@ +/* irq_init.c + * + * This file contains the implementation of rtems initialization + * related to interrupt handling. + * + * 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 +#include +#include + +/* + * rtems prologue generated in irq_asm.S + */ +extern void rtems_irq_prologue_0(); +extern void rtems_irq_prologue_1(); +extern void rtems_irq_prologue_2(); +extern void rtems_irq_prologue_3(); +extern void rtems_irq_prologue_4(); +extern void rtems_irq_prologue_5(); +extern void rtems_irq_prologue_6(); +extern void rtems_irq_prologue_7(); +extern void rtems_irq_prologue_8(); +extern void rtems_irq_prologue_9(); +extern void rtems_irq_prologue_10(); +extern void rtems_irq_prologue_11(); +extern void rtems_irq_prologue_12(); +extern void rtems_irq_prologue_13(); +extern void rtems_irq_prologue_14(); +extern void rtems_irq_prologue_15(); +/* + * default idt vector + */ +extern void default_raw_idt_handler(); +/* + * default on/off function + */ +static void nop_func(){} +/* + * default isOn function + */ +static int not_connected() {return 0;} + + +static rtems_raw_irq_connect_data idtHdl[IDT_SIZE]; + +/* + * Table used to store rtems managed interrupt handlers. + * Borrow the table to store raw handler entries at the beginning. + * The table will be reinitialized before the call to pc386_rtems_irq_mngt_set(). + */ +static rtems_irq_connect_data rtemsIrq[PC_386_IRQ_LINES_NUMBER] = { + {0,(rtems_irq_hdl)rtems_irq_prologue_0}, + {0,(rtems_irq_hdl)rtems_irq_prologue_1}, + {0,(rtems_irq_hdl)rtems_irq_prologue_2}, + {0,(rtems_irq_hdl)rtems_irq_prologue_3}, + {0,(rtems_irq_hdl)rtems_irq_prologue_4}, + {0,(rtems_irq_hdl)rtems_irq_prologue_5}, + {0,(rtems_irq_hdl)rtems_irq_prologue_6}, + {0,(rtems_irq_hdl)rtems_irq_prologue_7}, + {0,(rtems_irq_hdl)rtems_irq_prologue_8}, + {0,(rtems_irq_hdl)rtems_irq_prologue_9}, + {0,(rtems_irq_hdl)rtems_irq_prologue_10}, + {0,(rtems_irq_hdl)rtems_irq_prologue_11}, + {0,(rtems_irq_hdl)rtems_irq_prologue_12}, + {0,(rtems_irq_hdl)rtems_irq_prologue_13}, + {0,(rtems_irq_hdl)rtems_irq_prologue_14}, + {0,(rtems_irq_hdl)rtems_irq_prologue_15} +}; + +static rtems_raw_irq_connect_data defaultRawIrq = { + /* vectorIdex, hdl , on , off , isOn */ + 0, default_raw_idt_handler ,nop_func , nop_func, not_connected +}; + +static rtems_irq_connect_data defaultIrq = { + /* vectorIdex, hdl , on , off , isOn */ + 0, nop_func , nop_func , nop_func , not_connected +}; + +static rtems_irq_prio irqPrioTable[PC_386_IRQ_LINES_NUMBER]={ + /* + * actual rpiorities for interrupt : + * 0 means that only current interrupt is masked + * 255 means all other interrupts are masked + * The second entry has a priority of 255 because + * it is the slave pic entry and is should always remain + * unamsked. + */ + 0,0, + 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +static interrupt_gate_descriptor idtEntry; + +static rtems_irq_global_settings initial_config; +static rtems_raw_irq_global_settings raw_initial_config; + +void raw_idt_notify() +{ + printk("raw_idt_notify has been called \n"); +} + +void rtems_irq_mngt_init() +{ + int i; + interrupt_gate_descriptor* idt_entry_tbl; + unsigned limit; + unsigned int level; + + + i386_get_info_from_IDTR (&idt_entry_tbl, &limit); + + _CPU_ISR_Disable(level); + + /* + * Init the complete IDT vector table with defaultRawIrq value + */ + for (i = 0; i < IDT_SIZE; i++) { + idtHdl[i] = defaultRawIrq; + } + raw_initial_config.idtSize = IDT_SIZE; + raw_initial_config.defaultRawEntry = defaultRawIrq; + raw_initial_config.rawIrqHdlTbl = idtHdl; + + if (!i386_init_idt (&raw_initial_config)) { + /* + * put something here that will show the failure... + */ + _IBMPC_initVideo(); + printk("Unable to initialize IDT!!! System locked\n"); + while (1); + } + /* + * Patch the entry that will be used by RTEMS for interrupt management + * with RTEMS prologue. + */ + for (i = 0; i < PC_386_IRQ_LINES_NUMBER; i++) { + create_interrupt_gate_descriptor(&idtEntry,(rtems_raw_irq_hdl) rtemsIrq[i].hdl); + idt_entry_tbl[i + PC386_ASM_IRQ_VECTOR_BASE] = idtEntry; + } + /* + * At this point we have completed the initialization of IDT + * with raw handlers. We must now initialize the higher level + * interrupt management. + */ + /* + * re-init the rtemsIrq table + */ + for (i = 0; i < PC_386_IRQ_LINES_NUMBER; i++) { + rtemsIrq[i] = defaultIrq; + } + /* + * Init initial Interrupt management config + */ + initial_config.irqNb = PC_386_IRQ_LINES_NUMBER; + initial_config.defaultEntry = defaultIrq; + initial_config.irqHdlTbl = rtemsIrq; + initial_config.irqBase = PC386_ASM_IRQ_VECTOR_BASE; + initial_config.irqPrioTbl = irqPrioTable; + if (!pc386_rtems_irq_mngt_set(&initial_config)) { + /* + * put something here that will show the failure... + */ + _IBMPC_initVideo(); + printk("Unable to initialize RTEMS interrupt Management!!! System locked\n"); + while (1); + } + +#define DEBUG +#ifdef DEBUG + { + /* + * following adresses should be the same + */ + unsigned tmp; + + _IBMPC_initVideo(); + + printk("idt_entry_tbl = %x Interrupt_descriptor_table addr = %x\n", + idt_entry_tbl, &Interrupt_descriptor_table); + tmp = (unsigned) get_hdl_from_vector (PC386_ASM_IRQ_VECTOR_BASE + PC_386_PERIODIC_TIMER); + printk("clock isr address from idt = %x should be %x\n", + tmp, (unsigned) rtems_irq_prologue_0); + } + printk("i8259s_cache = %x\n", * (unsigned short*) &i8259s_cache); + debugPollingGetChar(); +#endif + asm volatile ("sti"); +} 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 diff --git a/c/src/lib/wrapup/Makefile.in b/c/src/lib/wrapup/Makefile.in index bef0aa637b..04a12300d5 100644 --- a/c/src/lib/wrapup/Makefile.in +++ b/c/src/lib/wrapup/Makefile.in @@ -16,6 +16,7 @@ include $(RTEMS_ROOT)/make/lib.cfg LIB=$(PROJECT_RELEASE)/lib/librtemsall${LIB_VARIANT}.a SRCS=$(wildcard $(PROJECT_RELEASE)/lib/libbsp$(LIB_VARIANT).a) \ + $(PROJECT_RELEASE)/lib/libcpu$(LIB_VARIANT).a \ $(PROJECT_RELEASE)/lib/librtems$(LIB_VARIANT).a \ $(wildcard $(PROJECT_RELEASE)/lib/libposix$(LIB_VARIANT).a) \ $(wildcard $(PROJECT_RELEASE)/lib/libka9q$(LIB_VARIANT).a) \ -- cgit v1.2.3