445 lines
11 KiB
NASM
445 lines
11 KiB
NASM
title "Software Interrupts"
|
|
;++
|
|
;
|
|
;Copyright (c) 1992, 1993, 1994 Corollary Inc
|
|
;
|
|
;Module Name:
|
|
;
|
|
; cbswint.asm
|
|
;
|
|
;Abstract:
|
|
;
|
|
; This module implements the HAL software interrupt routines
|
|
; for the MP Corollary implementation under Windows NT.
|
|
;
|
|
;Author:
|
|
;
|
|
; Landy Wang (landy@corollary.com) 26-Mar-1992
|
|
;
|
|
;Environment:
|
|
;
|
|
; Kernel Mode
|
|
;
|
|
;Revision History:
|
|
;
|
|
;--
|
|
|
|
|
|
.386p
|
|
.xlist
|
|
include hal386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include i386\kimacro.inc
|
|
include i386\cbus.inc
|
|
include mac386.inc
|
|
|
|
EXTRNP _HalBeginSystemInterrupt,3
|
|
EXTRNP _HalEndSystemInterrupt,2
|
|
|
|
.list
|
|
|
|
_TEXT SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; FASTCALL
|
|
; HalRequestSoftwareInterrupt (
|
|
; IN KIRQL RequestIrql
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is used to issue a software interrupt to the
|
|
; calling processor. Since this is all done in hardware, the
|
|
; code to implement this is trivial. Our hardware supports
|
|
; sending the interrupt to lowest-in-group processors, which
|
|
; would be useful for a good number of DPCs, for example, but
|
|
; the kernel doesn't currently tell us which kinds of software
|
|
; interrupts need to go to the caller versus which can go to
|
|
; any processor.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (cl) = RequestIrql - Supplies the request IRQL value
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
cPublicFastCall HalRequestSoftwareInterrupt ,1
|
|
|
|
push ecx
|
|
call dword ptr [_CbusRequestSoftwareInterrupt]
|
|
fstRet HalRequestSoftwareInterrupt
|
|
|
|
fstENDP HalRequestSoftwareInterrupt
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalClearSoftwareInterrupt (
|
|
; IN KIRQL RequestIrql
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is used to clear a possible pending software interrupt.
|
|
; Support for this function is optional, and allows the kernel to
|
|
; reduce the number of spurious software interrupts it receives. Since
|
|
; neither the APIC nor the CBC can clear an interrupt once it is sent,
|
|
; this optional function becomes a no-op in the Cbus HAL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (cl) = RequestIrql - Supplies the request IRQL value
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
cPublicFastCall HalClearSoftwareInterrupt ,1
|
|
fstRET HalClearSoftwareInterrupt
|
|
fstENDP HalClearSoftwareInterrupt
|
|
|
|
page ,132
|
|
subttl "Dispatch Interrupt"
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpDispatchInterrupt(
|
|
; VOID
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is the interrupt handler for a software interrupt generated
|
|
; at DISPATCH_LEVEL. Its function is to save the machine state, raise
|
|
; Irql to DISPATCH_LEVEL, dismiss the interrupt, and call the DPC
|
|
; delivery routine.
|
|
;
|
|
; Note that the "software" interrupt has in fact been
|
|
; generated by software, but delivered by hardware - thus, no iret
|
|
; frame needs to be constructed here by software.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST hdpi_a, hdpi_t
|
|
|
|
cPublicProc _HalpDispatchInterrupt ,0
|
|
;
|
|
; Save machine state on trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT hdpi_a, hdpi_t
|
|
|
|
;
|
|
; The only thing we need to save here is the interrupted taskpri.
|
|
; We must EOI the APIC immediately as there is no interrupt source,
|
|
; and if we context switch and exit this thread, then we may never EOI!
|
|
; the above macro will save all our registers, so we don't need to
|
|
; below. Thus, the EOI serves as the HalEndSystemInterrupt.
|
|
;
|
|
|
|
mov eax, DPC_TASKPRI ; mark interrupting vec
|
|
CBUS_EOI eax, ecx ; destroy eax & ecx
|
|
|
|
mov esi, dword ptr PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr
|
|
|
|
push dword ptr [esi] ; save entry taskpri
|
|
if DBG
|
|
cmp dword ptr [esi], 0 ; old irql not zero?
|
|
je short irqlok
|
|
cmp dword ptr [esi], 01fh ; old irql not zero?
|
|
je short irqlok
|
|
int 3
|
|
irqlok:
|
|
endif
|
|
|
|
mov dword ptr [esi], DPC_TASKPRI ; set new h/w taskpri
|
|
sti ; and allow interrupts
|
|
|
|
;
|
|
; Go do Dispatch Interrupt processing - we may context switch away from
|
|
; this thread here, resume the idle thread and either much later (or
|
|
; never) continue onward. so we must EOI _before_ dispatching with
|
|
; this call.
|
|
;
|
|
|
|
stdCall _KiDispatchInterrupt
|
|
|
|
;
|
|
; restore our original IRQL by programming it into the interrupt
|
|
; controller since we will read it out of the interrupt controller
|
|
; on the next KfRaiseIrql(). We must re-read the address of the
|
|
; task priority register for the current processor before setting
|
|
; it because we may be resuming a thread on a different processor
|
|
; from the one that originally entered this routine. For the APIC,
|
|
; this wouldn't matter because the task priority register is at the
|
|
; same physical address for all processors. But for the CBC, each
|
|
; task priority lies in global physical space (at different addresses).
|
|
; since we are at DISPATCH_LEVEL here, we cannot be pre-empted as
|
|
; we do this.
|
|
;
|
|
|
|
ifdef CBC_REV1
|
|
pop eax ; get entry taskpri
|
|
if DBG
|
|
cmp eax, 0 ; old irql not zero?
|
|
je short irqlok2
|
|
cmp eax, 01fh ; old irql not zero?
|
|
je short irqlok2
|
|
int 3
|
|
irqlok2:
|
|
endif
|
|
pushfd
|
|
cli
|
|
mov esi, dword ptr PCR[PcHal.PcrTaskpri]
|
|
mov dword ptr [esi], eax ; restore entry taskpri
|
|
popfd
|
|
else
|
|
mov esi, dword ptr PCR[PcHal.PcrTaskpri]
|
|
pop dword ptr [esi] ; restore entry taskpri
|
|
endif
|
|
|
|
;
|
|
; Call this directly instead of through INTERRUPT_EXIT
|
|
; because the HalEndSystemInterrupt has already been done,
|
|
; and must only be done ONCE per interrupt.
|
|
;
|
|
|
|
cli
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without EOI
|
|
|
|
stdENDP _HalpDispatchInterrupt
|
|
|
|
page ,132
|
|
subttl "APC Interrupt"
|
|
;++
|
|
;
|
|
; HalpApcInterrupt(
|
|
; VOID
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered as the result of a software interrupt generated
|
|
; at APC_LEVEL. Its function is to save the machine state, raise Irql to
|
|
; APC_LEVEL, dismiss the interrupt, and call the APC delivery routine.
|
|
;
|
|
; Note that the "software" interrupt has in fact been
|
|
; generated by software, but delivered by hardware - thus, no iret
|
|
; frame needs to be constructed here by software.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is Disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST hapc_a, hapc_t
|
|
|
|
cPublicProc _HalpApcInterrupt ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
ENTER_INTERRUPT hapc_a, hapc_t
|
|
|
|
; The only thing we need to save here is the interrupted taskpri.
|
|
; We must EOI the APIC immediately as there is no interrupt source,
|
|
; and if we context switch and exit this thread, then we may never EOI!
|
|
; the above macro will save all our registers, so we don't need to
|
|
; below. Thus, the EOI serves as the HalEndSystemInterrupt.
|
|
|
|
mov eax, APC_TASKPRI ; mark interrupting vec
|
|
CBUS_EOI eax, ecx ; destroy eax & ecx
|
|
|
|
mov esi, dword ptr PCR[PcHal.PcrTaskpri] ; get h/w taskpri addr
|
|
|
|
push dword ptr [esi] ; save entry taskpri
|
|
|
|
if DBG
|
|
cmp dword ptr [esi], 0 ; old irql not zero?
|
|
je short @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
|
|
mov dword ptr [esi], APC_TASKPRI ; set new h/w taskpri
|
|
sti ; and allow interrupts
|
|
|
|
; call the APC delivery routine(previous mode,Null exception frame,
|
|
; trap frame)
|
|
|
|
|
|
mov eax, [ebp]+TsSegCs ; get interrupted code's CS
|
|
and eax, MODE_MASK ; extract the mode
|
|
|
|
stdCall _KiDeliverApc, <eax, 0, ebp>
|
|
|
|
;
|
|
; restore our original IRQL by programming it into the interrupt
|
|
; controller since we will read it out of the interrupt controller
|
|
; on the next KfRaiseIrql(). We must re-read the address of the
|
|
; task priority register for the current processor before setting
|
|
; it because we may be resuming a thread on a different processor
|
|
; from the one that originally entered this routine. For the APIC,
|
|
; this wouldn't matter because the task priority register is at the
|
|
; same physical address for all processors. But for the CBC, each
|
|
; task priority lies in global physical space (at different addresses).
|
|
; since we are at APC_LEVEL here, we must protect against a context
|
|
; switch happening between reading the taskpri address and actually
|
|
; setting its value, hence the cli...
|
|
;
|
|
|
|
ifdef CBC_REV1
|
|
cli
|
|
endif
|
|
if DBG
|
|
cmp dword ptr [esp], 0 ; old irql not zero?
|
|
je short @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
mov esi, dword ptr PCR[PcHal.PcrTaskpri]
|
|
pop dword ptr [esi] ; restore entry taskpri
|
|
|
|
if DBG
|
|
cmp dword ptr [esi], 0 ; old irql not zero?
|
|
je short @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
;
|
|
; Call this directly instead of through INTERRUPT_EXIT
|
|
; because the HalEndSystemInterrupt has already been done,
|
|
; and must only be done ONCE per interrupt.
|
|
;
|
|
|
|
cli
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
|
|
|
|
stdENDP _HalpApcInterrupt
|
|
|
|
|
|
page ,132
|
|
subttl "HalRequestIpi"
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalRequestIpi(
|
|
; IN ULONG Mask
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Requests an interprocessor interrupt
|
|
;
|
|
; for Windows NT (and MPX 2.1), we use full distributed
|
|
; interrupt capability, and, thus, we will IGNORE the sswi address
|
|
; that RRD passes us and prioritize IPI as we see fit, given the
|
|
; other devices configured into the system. see the backend Cbus1
|
|
; and Cbus2 handlers for more details.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Mask - Mask of processors to be interrupted
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
cPublicProc _HalRequestIpi ,1
|
|
|
|
jmp dword ptr [_CbusRequestIPI]
|
|
|
|
stdENDP _HalRequestIpi
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpIpiHandler (
|
|
; );
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by inter
|
|
; processor communication. Its function is to call its handler.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None.
|
|
; Interrupt is dismissed
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST Hipi_a, Hipi_t
|
|
|
|
align 4
|
|
|
|
cPublicProc _HalpIpiHandler ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hipi_a, Hipi_t ; (ebp) -> Trap frame
|
|
|
|
;
|
|
; Save interrupting vector and previous IRQL. previous IRQL will
|
|
; be filled in by our call to HalBeginSystemInterrupt, and the
|
|
; stack space for both will be removed via INTERRUPT_EXIT's
|
|
; call to HalEndSystemInterrupt.
|
|
;
|
|
push dword ptr [_CbusIpiVector]
|
|
sub esp, 4 ; space for OldIrql
|
|
|
|
;
|
|
; We now dismiss the interprocessor interrupt
|
|
; (Irql, Vector, stack location of OldIrql)
|
|
|
|
stdCall _HalBeginSystemInterrupt, <IPI_LEVEL, dword ptr [_CbusIpiVector], esp>
|
|
|
|
; Pass Null ExceptionFrame
|
|
; Pass TrapFrame to Ipi service rtn
|
|
|
|
stdCall _KiIpiServiceRoutine, <ebp, 0>
|
|
|
|
;
|
|
; Do interrupt exit processing
|
|
;
|
|
|
|
INTERRUPT_EXIT ; will return to caller
|
|
|
|
stdENDP _HalpIpiHandler
|
|
|
|
|
|
_TEXT ENDS
|
|
END
|