NT4/private/ntos/nthals/halcbus/i386/cbswint.asm
2020-09-30 17:12:29 +02:00

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