/* irq.c * * This file contains the implementation of the function described in irq.h * * Copyright (C) 1998 valette@crf.canon.fr * * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be * found in found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * $Id$ */ #include #include #include #include #include #include #ifndef CPU_STACK_ALIGNMENT #error "Missing header? CPU_STACK_ALIGNMENT is not defined here" #endif /* Stack frame we use for intermediate storage */ #define ARG_OFF 0 #define MSK_OFF 4 #define EBX_OFF 8 /* ebx */ #define EBP_OFF 12 /* code restoring ebp/esp relies on */ #define ESP_OFF 16 /* esp being on top of ebp! */ #ifdef __SSE__ /* need to be on 16 byte boundary for SSE, add 12 to do that */ #define FRM_SIZ (20+12+512) #define SSE_OFF 32 #else #define FRM_SIZ 20 #endif BEGIN_CODE SYM (_ISR_Handler): /* * Before this was point is reached the vectors unique * entry point did the following: * * 1. saved scratch registers registers eax edx ecx * 2. put the vector number in ecx. * * 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 */ /* * Establish an aligned stack frame * original sp * saved ebx * saved ebp * saved irq mask * vector arg to C_dispatch_isr <- aligned SP */ movl esp, eax subl $FRM_SIZ, esp andl $ - CPU_STACK_ALIGNMENT, esp movl ebx, EBX_OFF(esp) movl eax, ESP_OFF(esp) movl ebp, EBP_OFF(esp) #ifdef __SSE__ /* NOTE: SSE only is supported if the BSP enables fxsave/fxrstor * to save/restore SSE context! This is so far only implemented * for pc386!. */ /* We save SSE here (on the task stack) because we possibly * call other C-code (besides the ISR, namely _Thread_Dispatch()) */ /* don't wait here; a possible exception condition will eventually be * detected when the task resumes control and executes a FP instruction fwait */ fxsave SSE_OFF(esp) fninit /* clean-slate FPU */ movl $0x1f80, ARG_OFF(esp) /* use ARG_OFF as scratch space */ ldmxcsr ARG_OFF(esp) /* clean-slate MXCSR */ #endif .check_stack_switch: movl esp, ebp /* ebp = previous stack pointer */ #if defined(RTEMS_SMP) && defined(BSP_HAS_SMP) movl $SYM(_Per_CPU_Information_p), ebx call SYM(bsp_smp_processor_id) mov (ebx,eax,4), ebx pushl ecx call SYM(_ISR_SMP_Enter) popl ecx cmpl $0, eax jne .i8259 movl PER_CPU_INTERRUPT_STACK_HIGH(ebx), esp #else movl $SYM(_Per_CPU_Information), ebx /* * Is this the outermost interrupt? * Switch stacks if necessary */ cmpl $0, PER_CPU_ISR_NEST_LEVEL(ebx) jne nested /* No, then continue */ movl PER_CPU_INTERRUPT_STACK_HIGH(ebx), esp /* * We want to insure that the old stack pointer is in ebp * By saving it on every interrupt, all we have to do is * movl ebp->esp near the end of every interrupt. */ nested: incl PER_CPU_ISR_NEST_LEVEL(ebx) /* one nest level deeper */ incl SYM (_Thread_Dispatch_disable_level) /* disable multitasking */ #endif /* * i8259 Management */ .i8259: /* Do not disable any 8259 interrupts if this isn't from one */ cmp ecx, 16 /* is this a PIC IRQ? */ jge .end_of_i8259 /* * acknowledge the interrupt */ movw SYM (i8259s_cache), ax /* save current i8259 interrupt mask */ movl eax, MSK_OFF(esp) /* save in stack frame */ /* * 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 movb $PIC_EOI, al cmpl $7, ecx jbe .master outb $PIC_SLAVE_COMMAND_IO_PORT .master: outb $PIC_MASTER_COMMAND_IO_PORT .end_of_i8259: /* * re-enable interrupts at processor level as the current * interrupt source is now masked via i8259 */ sti /* * ECX is preloaded with the vector number; store as arg * on top of stack. Note that _CPU_Interrupt_stack_high * was adjusted in _CPU_Interrupt_stack_setup() (score/rtems/cpu.h) * to make sure there is space. */ movl ecx, ARG_OFF(esp) /* store vector arg in stack */ call C_dispatch_isr /* * disable interrupts_again */ cli movl ARG_OFF(esp), ecx /* grab vector arg from stack */ /* * Restore stack. This moves back to the task stack * when all interrupts are unnested. */ movl ebp, esp /* * restore the original i8259 masks */ /* Do not touch 8259 interrupts if this isn't from one */ cmp ecx, 16 /* is this a PIC IRQ? */ jge .dont_restore_i8259 movl MSK_OFF(esp), eax movw ax, SYM (i8259s_cache) outb $PIC_MASTER_IMR_IO_PORT movb ah, al outb $PIC_SLAVE_IMR_IO_PORT .dont_restore_i8259: #if defined(RTEMS_SMP) && defined(BSP_HAS_SMP) call SYM(_ISR_SMP_Exit) testl eax, eax je .exit #else decl PER_CPU_ISR_NEST_LEVEL(ebx) /* 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 */ cmpb $0, PER_CPU_DISPATCH_NEEDED(ebx) /* Is task switch necessary? */ jne .schedule /* Yes, then call the scheduler */ jmp .exit /* No, exit */ #endif .schedule: /* * the scratch registers have already been saved and we are already * back on the thread system stack. So we can call _Thread_Dispatch * directly */ call _Thread_Dispatch /* * fall through exit to restore complete contex (scratch registers * eip, CS, Flags). */ .exit: #ifdef __SSE__ fwait fxrstor SSE_OFF(esp) #endif /* restore ebx, ebp and original esp */ addl $EBX_OFF, esp popl ebx popl ebp popl esp /* * 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 (_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) DISTINCT_INTERRUPT_ENTRY(16) /* * routine used to initialize the IDT by default */ PUBLIC (default_raw_idt_handler) PUBLIC (raw_idt_notify) SYM (default_raw_idt_handler): pusha cld mov esp, ebp andl $ - CPU_STACK_ALIGNMENT, esp call raw_idt_notify mov ebp, esp popa iret END_CODE END