Coherent4.2.10/i386/lib/intr_disp.s
/ $Header: $
.unixorder
/ Assembly-language code to perform low-level interrupt dispatching.
/ The entry points defined here are referred to by the code elsewhere that
/ defines the initial IDT contents.
/ $Log: $
/
.include "struct.inc"
.include "intr.inc"
.include "ddi.inc"
.include "select.inc"
.globl return_to_user / external
.globl return_from_interrupt / external
.globl trap_level / external
.text
/ Generic interrupt entry-point handler. This duplicates some of the major
/ functionality of "tsave", so ensure that we stay in synch somehow.
/
/ Here, we create fairly standard saved context frame, even though it wastes
/ some time.
context_save:
pusha
push %ds / Save current state
push %es
push %fs
push %gs
/ Now we can play with the registers, fetch the trap structure
/ address from the stack frame. We store the previous contents of
/ the global vector mask in a stack frame slot that is unused for us.
movw $SEL_386_KD, %dx / Map %ds to kernel data
movw %dx, %ds
movl _err(%esp), %ebx / trap information pointer
movl cpu+dc_base_mask, %eax / get old mask
movl %eax, _err(%esp) / save old mask
orl _int_mask(%ebx), %eax
movl %eax, cpu+dc_base_mask / set new mask
/ We interleave some other setup in with writing the new mask and
/ sending EOI's to the interrupt controllers so we don't need to get
/ into the I/O recovery delay games. The ideal choice is a segment
/ register maniplation, since they take so long.
outb $PICM / set new master mask
movw %dx, %es / map %es to kernel data
movb %ah, %al
outb $SPICM / set new slave mask
/ Now we have written the masks, we need to send EOI's to the
/ interrupt controllers to allow the non-masked interrupts to proceed.
/ Again, we thread some extra setup through here to avoid having to
/ play the I/O recovery-delay game.
movb $EOI_COMMAND, %al
cmpl $0, _trapno (%esp)
je ?master_eoi
outb $SPICC / Slave PIC EOI
?master_eoi:
xor %ebp, %ebp / Halt stack backtraces
subl %edx, %edx / set fs to NULL, since we
movw %dx, %fs / have no user context.
outb $PICC / Master PIC EOI
/ Now we have done pretty much all the setup that is possible, we
/ increment the global DDI/DKI flag that says we are in an interrupt
/ context, and increment our flag that says we are in the kernel
/ before globally enabling interrupts in the CPU.
incb cpu+dc_int_level / in an interrupt
sti
incl _int_count(%ebx) / count interrupt
pushl %ebx / point at interrupt info
pushl _int_arg(%ebx) / arg for user function
icall _int_func(%ebx) / call the function
addl $8, %esp / discard arguments
/ Now we can restore the PIC mask settings. We disable interrupts
/ while we do this, because even while we don't care about this being
/ interrupted we have to avoid infinite stack growth. From here to the
/ final interrupt return has to be disabled, with the possible
/ exception of the code to process deferred functions and whatnot
/ (which cannot be re-entered due to the protocol for managing the
/ dc_int_level/dc_user_level flag).
movl _err(%esp), %eax / load saved mask
cli
outb $PICM / restore master mask
movl %eax, cpu+dc_base_mask / restore base mask
cmpb $2, cpu+dc_int_level / what int level are we at?
mov %ah, %al
outb $SPICM / restore slave mask
jae ?skip_defer / if >= 2, don't do defer
/ There are two kinds of deferred routine; DDI/DKI and Coherent. The
/ DDI/DKI semantics don't specify when the deferred routines are run.
/ The Coherent routines historically run just before return to user
/ mode, but it is not clear whether this is important.
cmpb $0, cpu+dc_ipl / if ipl > 0, don't do defer.
jne ?skip_defer
sti / can run this enabled
call return_from_interrupt
cli
?skip_defer:
decb cpu+dc_int_level
/ Now we test to see whether we came from user mode (or the idle
/ process, which tests the same), and if so we run the "stand" routine
/ to check for rescheduling.
call trap_level
testl %eax, %eax / were we in user mode?
jne ?skip_dispatch
sti
movw $SEL_386_UD, %dx / now we *do* have context
movw %dx, %fs
call return_to_user
cli
?skip_dispatch:
pop %gs / restore saved context
pop %fs
pop %es
pop %ds
popa
add $8, %esp / forget err, trapno
iret / Done.
INTR .macro int_no
/ .globl __interrupt_\int_no
/__interrupt_\int_no:
.globl dev\int_no
dev\int_no:
push $ints + int_no * SIZE..intr_t
.if int_no < 8
pushl $0
.else
pushl $1
.endi
jmp context_save
.endm
INTR 0 / periodic timer
INTR 1 / keyboard
/ for chaining PIC B
INTR 3 / COM2
INTR 4 / COM1
INTR 5 /
INTR 6 / floppy disk controller
INTR 7 / parallel port
INTR 8 / RTC
INTR 9 / original IRQ 2
INTR 10
INTR 11
INTR 12
INTR 13
INTR 14 / AT hard-disk
INTR 15