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, ; ; 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, ; Pass Null ExceptionFrame ; Pass TrapFrame to Ipi service rtn stdCall _KiIpiServiceRoutine, ; ; Do interrupt exit processing ; INTERRUPT_EXIT ; will return to caller stdENDP _HalpIpiHandler _TEXT ENDS END