/* 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 <irq_asm.h>
.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:
*
* <new mask> = <old mask> | irq_mask_or_tbl[<intr number aka ecx>]
*/
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