// TITLE("Interrupt Support") //++ // // Copyright (c) 1995 Microsoft Corporation and IBM Corporation // // Module Name: // // xxintsup.s // // Abstract: // // This module implements the PowerPC machine dependent code for // interrupt handling. // // Author: // // Chuck Lenzmeier (chuckl) 18-Feb-1995 // Adapter from C code by Peter L. Johnston (plj@vnet.ibm.com) August 1993 // Adapted from code by David N. Cutler (davec) 1-Apr-1991 // // Environment: // // Kernel mode only, IRQL DISPATCH_LEVEL. // // Revision History: // //-- #include "ksppc.h" .extern __imp_KeLowerIrql .extern __imp_KeRaiseIrql #if !defined(NT_UP) && SPINDBG1 .extern ..KiAcquireSpinLockDbg #endif SBTTL("Synchronize Execution") //++ // // BOOLEAN // KeSynchronizeExecution ( // IN PKINTERRUPT Interrupt, // IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine, // IN PVOID SynchronizeContext // ) // // Routine Description: // // This function synchronizes the execution of the specified routine // with the execution of the service routine associated with the // specified interrupt object. // // Arguments: // // Interrupt (r3) - Supplies a pointer to a control object of type interrupt. // // SynchronizeRoutine (r4) - Supplies a pointer to a function whose execution // is to be synchronized with the execution of the service routine associated // with the specified interrupt object. // // SynchronizeContext (r5) - Supplies a pointer to an arbitrary data structure // which is to be passed to the function specified by the SynchronizeRoutine // parameter. // // Return Value: // // The value returned by the SynchronizeRoutine function is returned as // the function value. // //-- .struct 0 .space StackFrameHeaderLength kseLR: .space 4 kseR31: .space 4 kseR4: .space 4 kseR5: .space 4 kseIrql: .space 4 kseToc: .space 4 .align 3 // ensure 8 byte alignment kseFrameLength: // // N.B. We are a bit footloose with the TOC pointer in this routine. // We do not restore it immediately after the call to KeRaiseIrql, // and only restore it on return from the synchronize routine. (And // we only restore it then because the call to KeLowerIrql needs it.) // SPECIAL_ENTRY_S(KeSynchronizeExecution,_TEXT$00) mflr r0 // get return address stwu sp, -kseFrameLength(sp) // allocate stack frame stw r31, kseR31(sp) stw r0, kseLR(sp) // save return address PROLOGUE_END(KeSynchronizeExecution) stw r4, kseR4(sp) // save synchronization routine address lwz r6, [toc]__imp_KeRaiseIrql(rtoc) // &&function descriptor stw r5, kseR5(sp) // save synchronization routine context lwz r6, 0(r6) // &function descriptor stw rtoc, kseToc(sp) // save our TOC lwz r5, 0(r6) // &KeRaiseIrql // // Raise IRQL to the synchronization level and acquire the associated // spin lock. // #if !defined(NT_UP) lwz r31, InActualLock(r3) // get address of spin lock #endif lwz rtoc, 4(r6) // HAL's TOC lbz r3, InSynchronizeIrql(r3) // get synchronization IRQL mtctr r5 addi r4, sp, kseIrql // compute address to save IRQL bctrl // N.B. skip restoring the TOC lwz r4, kseR4(sp) // get synchronize routine descriptor #if !defined(NT_UP) ACQUIRE_SPIN_LOCK(r31, r31, r5, kse_lock, kse_lock_spin) #endif // // Call specified routine passing the specified context parameter. // lwz r5, 0(r4) // get synchronize routine address lwz rtoc, 4(r4) // get synchronize routine TOC mtctr r5 // put routine address in CTR lwz r3, kseR5(sp) // get synchronize routine context bctrl // call specified routine // // Release spin lock, lower IRQL to its previous level, and return the value // returned by the specified routine. // lwz rtoc, kseToc(sp) // restore our TOC #if !defined(NT_UP) li r5, 0 // get a 0 for spin lock #endif lwz r6, [toc]__imp_KeLowerIrql(rtoc) // &&function descriptor #if !defined(NT_UP) RELEASE_SPIN_LOCK(r31, r5) #endif lwz r6, 0(r6) // &function descriptor ori r31, r3, 0 // save return value lwz r5, 0(r6) // &KeLowerIrql lbz r3, kseIrql(sp) // get saved IRQL mtctr r5 lwz rtoc, 4(r6) // HAL's TOC bctrl lwz rtoc, kseToc(sp) // restore our TOC ori r3, r31, 0 // set return value lwz r0, kseLR(sp) // get return address lwz r31, kseR31(sp) // restore r31 mtlr r0 // set return address addi sp, sp, kseFrameLength // return stack frame blr #if !defined(NT_UP) SPIN_ON_SPIN_LOCK(r31, r5, kse_lock, kse_lock_spin) #endif DUMMY_EXIT(KeSynchronizeExecution) SBTTL("Chained Dispatch") //++ // // VOID // KiChainedDispatch ( // IN PKINTERRUPT Interrupt, // IN PVOID ServiceContext, // IN PVOID TrapFrame // ) // // Routine Description: // // This routine is entered as a result of an interrupt being generated // via a vector that is connected to more than one interrupt object. Its // function is to walk the list of connected interrupt objects and call // each interrupt service routine. If the mode of the interrupt is latched, // then a complete traversal of the chain must be performed. // // Arguments: // // Interrupt (r3) - Supplies a pointer to the Interrupt Object. // // ServiceContext (r4) - Supplies a pointer to the Service Context associated // with this Interrupt Object. // // TrapFrame (r5) - Supplies the address of the Trap Frame created as a // result of this interrupt. // // Return Value: // // None. // //-- .struct 0 .space StackFrameHeaderLength kcdLR: .space 4 #if !defined(NT_UP) kcdR24: .space 4 #endif kcdR25: .space 4 kcdR26: .space 4 kcdR27: .space 4 kcdR28: .space 4 kcdR29: .space 4 kcdR30: .space 4 kcdR31: .space 4 kcdIrql: .space 4 kcdToc: .space 4 .align 3 // ensure 8 byte alignment kcdFrameLength: // // N.B. We are a bit footloose with the TOC pointer in this routine. // We only ensure the TOC is correct when we explicitly need it // to be correct. We do not restore it on exit. This is safe // because we know that our caller called us via a function // descriptor and will therefore restore its own TOC when we return. // SPECIAL_ENTRY(KiChainedDispatch) mflr r0 // get return address stwu sp, -kcdFrameLength(sp) // allocate stack frame #if !defined(NT_UP) stw r24, kcdR24(sp) #endif stw r25, kcdR25(sp) stw r26, kcdR26(sp) stw r27, kcdR27(sp) stw r28, kcdR28(sp) stw r29, kcdR29(sp) stw r30, kcdR30(sp) stw r31, kcdR31(sp) stw r0, kcdLR(sp) // save return address PROLOGUE_END(KiChainedDispatch) ori r25, r5, 0 // save trap frame address stw rtoc, kcdToc(sp) // save our TOC // // Initialize loop variables. // addi r31, r3, InInterruptListEntry // set address of listhead ori r30, r31, 0 // set address of first entry li r29, 0 // clear floating state saved flag lbz r28, InMode(r3) // get mode of interrupt lbz r27, InIrql(r3) // get interrupt source IRQL // // Walk the list of connected interrupt objects and call the respective // interrupt service routines. // kcd10: lbz r8, InFloatingSave(r3) // get floating save flag cmpwi r29, 0 // floating state already saved? cmpwi cr7, r8, 0 // interrupt uses floating state? bne kcd20 // if ne, floating state already saved beq cr7, kcd20 // if eq, don't save floating state // // Save volatile floating registers in trap frame. // li r29, 1 // set floating state saved flag SAVE_VOLATILE_FLOAT_STATE(r25) // save volatile floating state kcd20: // // Raise IRQL to synchronization level if synchronization level is not // equal to the interrupt source level. // lbz r26, InSynchronizeIrql(r3) // get synchronization IRQL cmpw r27, r26 // IRQL levels the same? beq kcd25 // if eq, IRQL levels are the same lwz r6, [toc]__imp_KeRaiseIrql(rtoc) // &&function descriptor ori r3, r26, 0 // set synchronization IRQL lwz r6, 0(r6) // &function descriptor addi r4, sp, kcdIrql // compute address to save IRQL lwz r5, 0(r6) // &KeRaiseIrql lwz rtoc, 4(r6) // HAL's TOC mtctr r5 bctrl // N.B. skip restoring the TOC subi r3, r30, InInterruptListEntry // recompute interrupt object address kcd25: lwz r5, InServiceRoutine(r3) // get service routine descriptor // // Acquire the service routine spin lock and call the service routine. // #if !defined(NT_UP) lwz r24, InActualLock(r3) // get address of spin lock ACQUIRE_SPIN_LOCK(r24, r24, r7, kcd_lock, kcd_lock_spin) #endif lwz r6, 0(r5) // get address of service routine lwz rtoc, 4(r5) // get service routine TOC mtctr r6 // put routine address in CTR lwz r4, InServiceContext(r3) // get service context ori r5, r25, 0 // pass &TrapFrame bctrl // call service routine // // Release the service routine spin lock. Lower IRQL to the interrupt source // level if synchronization level is not the same as the interrupt source level. // lwz rtoc, kcdToc(sp) // restore our TOC #if !defined(NT_UP) li r4, 0 // get a 0 for spin lock #endif cmpw r27, r26 // IRQL levels the same? #if !defined(NT_UP) RELEASE_SPIN_LOCK(r24, r4) #endif ori r26, r3, 0 // save service routine status beq kcd35 // if eq, IRQL levels are the same lwz r6, [toc]__imp_KeLowerIrql(rtoc) // &&function descriptor ori r3, r27, 0 // set interrupt source IRQL lwz r6, 0(r6) // &function descriptor lwz r5, 0(r6) // &KeLowerIrql lwz rtoc, 4(r6) // HAL's TOC mtctr r5 bctrl lwz rtoc, kcdToc(sp) // restore our TOC (for next loop) kcd35: // // Get next list entry and check for end of loop. // lwz r30, LsFlink(r30) // get next interrupt object address cmpwi r26, 0 // interrupt handled? cmpwi cr7, r28, 0 // level sensitive interrupt? cmpw cr6, r30, r31 // end of list? beq kcd40 // if eq, interrupt not handled beq cr7,kcd50 // if eq, level sensitive interrupt kcd40: subi r3, r30, InInterruptListEntry // compute interrupt object address bne cr6,kcd10 // if ne, not end of list kcd50: // // Either the interrupt is level sensitive and has been handled or the end of // the interrupt object chain has been reached. Check to determine if floating // machine state needs to be restored. // cmpwi r29, 0 // floating state saved? beq kcd60 // if eq, floating state not saved // // Restore volatile floating registers from trap frame. // RESTORE_VOLATILE_FLOAT_STATE(r25) // restore volatile floating state kcd60: // // Restore nonvolatile registers, retrieve return address, deallocate // stack frame, and return. // lwz r0, kcdLR(sp) // get return address lwz r31, kcdR31(sp) // restore r31 lwz r30, kcdR30(sp) // restore r30 lwz r29, kcdR29(sp) // restore r29 lwz r28, kcdR28(sp) // restore r28 lwz r27, kcdR27(sp) // restore r27 lwz r26, kcdR26(sp) // restore r26 lwz r25, kcdR25(sp) // restore r25 #if !defined(NT_UP) lwz r24, kcdR24(sp) // restore r24 #endif mtlr r0 // set return address addi sp, sp, kcdFrameLength // return stack frame blr #if !defined(NT_UP) SPIN_ON_SPIN_LOCK(r24, r7, kcd_lock, kcd_lock_spin) #endif DUMMY_EXIT(KiChainedDispatch) SBTTL("Floating Dispatch") //++ // // VOID // KiFloatingDispatch ( // IN PKINTERRUPT Interrupt, // IN PVOID ServiceContext, // IN PVOID TrapFrame // ) // // Routine Description: // // This routine is entered as the result of an interrupt being generated // via a vector that is connected to an interrupt object. Its function is // to save the volatile floating machine state and then call the specified // interrupt service routine. // // N.B. On entry to this routine only the volatile integer registers have // been saved. // // Arguments: // // Interrupt (r3) - Supplies a pointer to the Interrupt Object. // // ServiceContext (r4) - Supplies a pointer to the Service Context associated // with this Interrupt Object. // // TrapFrame (r5) - Supplies the address of the Trap Frame created as a // result of this interrupt. // // Return Value: // // None. // //-- .struct 0 .space StackFrameHeaderLength kfdLR: .space 4 #if !defined(NT_UP) kfdR29: .space 4 #endif kfdR30: .space 4 kfdR31: .space 4 kfdIrql: .space 4 kfdToc: .space 4 .align 3 // ensure 8 byte alignment kfdFrameLength: // // N.B. We are a bit footloose with the TOC pointer in this routine. // We only ensure the TOC is correct when we explicitly need it // to be correct. We do not restore it on exit. This is safe // because we know that our caller called us via a function // descriptor and will therefore restore its own TOC when we return. // SPECIAL_ENTRY(KiFloatingDispatch) mflr r0 // get return address stwu sp, -kfdFrameLength(sp) // allocate stack frame #if !defined(NT_UP) stw r29, kfdR29(sp) #endif stw r30, kfdR30(sp) stw r31, kfdR31(sp) stw r0, kfdLR(sp) // save return address PROLOGUE_END(KiFloatingDispatch) ori r30, r5, 0 // save trap frame address stw rtoc, kfdToc(sp) // save our TOC // // Save volatile floating registers in trap frame. // SAVE_VOLATILE_FLOAT_STATE(r5) // save volatile floating state // // Raise IRQL to synchronization level if synchronization level is not // equal to the interrupt source level. // ori r31, r3, 0 // save address of interrupt object lbz r4, InIrql(r3) // get interrupt source IRQL lbz r8, InSynchronizeIrql(r3) // get synchronization IRQL cmpw r8, r4 // IRQL levels the same? beq kfd10 // if eq, IRQL levels are the same lwz r6, [toc]__imp_KeRaiseIrql(rtoc) // &&function descriptor ori r3, r8, 0 // set synchronization IRQL lwz r6, 0(r6) // &function descriptor addi r4, sp, kfdIrql // compute address to save IRQL lwz r8, 0(r6) // &KeLowerIrql lwz rtoc, 4(r6) // HAL's TOC mtctr r8 bctrl // N.B. skip restoring the TOC ori r3, r31, 0 // restore address of interrupt object kfd10: lwz r8, InServiceRoutine(r31) // get service routine descriptor // // Acquire the service routine spin lock and call the service routine. // #if !defined(NT_UP) lwz r29, InActualLock(r31) // get address of spin lock ACQUIRE_SPIN_LOCK(r29, r29, r7, kfd_lock, kfd_lock_spin) #endif lwz r6, 0(r8) // get address of service routine lwz rtoc, 4(r8) // get service routine TOC mtctr r6 // put routine address in CTR lwz r4, InServiceContext(r31) // get service context bctrl // call service routine // // Release the service routine spin lock. Lower IRQL to the interrupt source // level if synchronization level is not the same as the interrupt source level. // lwz rtoc, kfdToc(sp) // restore our TOC #if !defined(NT_UP) li r6, 0 // get a 0 for spin lock #endif lbz r3, InIrql(r31) // get interrupt source IRQL lbz r4, InSynchronizeIrql(r31) // get synchronization IRQL #if !defined(NT_UP) RELEASE_SPIN_LOCK(r29, r6) #endif cmpw r3, r4 // IRQL levels the same? beq kfd30 // if eq, IRQL levels are the same lwz r6, [toc]__imp_KeLowerIrql(rtoc) // &&function descriptor lwz r6, 0(r6) // &function descriptor lwz r5, 0(r6) // &KeLowerIrql lwz rtoc, 4(r6) // HAL's TOC mtctr r5 bctrl kfd30: // // Restore volatile floating registers from trap frame. // RESTORE_VOLATILE_FLOAT_STATE(r30) // restore volatile floating state // // Restore nonvolatile registers, retrieve return address, deallocate // stack frame, and return. // lwz r0, kfdLR(sp) // get return address #if !defined(NT_UP) lwz r29, kfdR29(sp) // restore r29 #endif lwz r30, kfdR30(sp) // restore r30 lwz r31, kfdR31(sp) // restore r31 mtlr r0 // set return address addi sp, sp, kfdFrameLength // return stack frame blr #if !defined(NT_UP) SPIN_ON_SPIN_LOCK(r29, r7, kfd_lock, kfd_lock_spin) #endif DUMMY_EXIT(KiFloatingDispatch) SBTTL("Interrupt Dispatch - Raise IRQL") //++ // // VOID // KiInterruptDispatchRaise ( // IN PKINTERRUPT Interrupt, // IN PVOID ServiceContext, // IN PVOID TrapFrame // ) // // Routine Description: // // This routine is entered as the result of an interrupt being generated // via a vector that is connected to an interrupt object. Its function is // to directly call the specified interrupt service routine. // // N.B. This routine raises the interrupt level to the synchronization // level specified in the interrupt object. // // Arguments: // // Interrupt (r3) - Supplies a pointer to the Interrupt Object. // // ServiceContext (r4) - Supplies a pointer to the Service Context associated // with this Interrupt Object. // // TrapFrame (r5) - Supplies the address of the Trap Frame created as a // result of this interrupt. // // Return Value: // // None. // //-- .struct 0 .space StackFrameHeaderLength kidrLR: .space 4 kidrR31: .space 4 kidrIrql: .space 4 kidrToc: .space 4 .align 3 // ensure 8 byte alignment kidrFrameLength: // // N.B. We are a bit footloose with the TOC pointer in this routine. // We only ensure the TOC is correct when we explicitly need it // to be correct. We do not restore it on exit. This is safe // because we know that our caller called us via a function // descriptor and will therefore restore its own TOC when we return. // SPECIAL_ENTRY(KiInterruptDispatchRaise) mflr r0 // get return address stwu sp, -kidrFrameLength(sp) // allocate stack frame stw r31, kidrR31(sp) lwz r6, [toc]__imp_KeRaiseIrql(rtoc) // &&function descriptor stw r0, kidrLR(sp) // save return address PROLOGUE_END(KiInterruptDispatchRaise) lwz r6, 0(r6) // &function descriptor stw rtoc, kidrToc(sp) // save our TOC // // Raise IRQL to synchronization level. // lwz r8, 0(r6) // &KeRaiseIrql ori r31, r3, 0 // save address of interrupt object lwz rtoc, 4(r6) // HAL's TOC lbz r3, InSynchronizeIrql(r3) // get synchronization IRQL mtctr r8 addi r4, sp, kidrIrql // compute address to save IRQL bctrl // N.B. skip restoring the TOC lwz r8, InServiceRoutine(r31) // get service routine descriptor ori r3, r31, 0 // restore address of interrupt object lwz r4, InServiceContext(r31) // get service context // // Acquire the service routine spin lock and call the service routine. // lwz r6, 0(r8) // get address of service routine #if !defined(NT_UP) lwz r31, InActualLock(r31) // get address of spin lock ACQUIRE_SPIN_LOCK(r31, r31, r7, kidr_lock, kidr_lock_spin) #endif mtctr r6 // put routine address in CTR lwz rtoc, 4(r8) // get service routine TOC bctrl // call service routine // // Release the service routine spin lock. Lower IRQL to the previous level. // lwz rtoc, kidrToc(sp) // restore our TOC #if !defined(NT_UP) li r4, 0 // get a 0 for spin lock #endif lwz r6, [toc]__imp_KeLowerIrql(rtoc) // &&function descriptor #if !defined(NT_UP) RELEASE_SPIN_LOCK(r31, r4) #endif lwz r6, 0(r6) // &function descriptor lwz r8, 0(r6) // &KeLowerIrql lwz rtoc, 4(r6) // HAL's TOC mtctr r8 lbz r3,kidrIrql(sp) // get previous IRQL bctrl // N.B. skip restoring the TOC // // Restore nonvolatile registers, retrieve return address, deallocate // stack frame, and return. // lwz r0, kidrLR(sp) // get return address lwz r31, kidrR31(sp) // restore r31 mtlr r0 // set return address addi sp, sp, kidrFrameLength // return stack frame blr #if !defined(NT_UP) SPIN_ON_SPIN_LOCK(r31, r7, kidr_lock, kidr_lock_spin) #endif DUMMY_EXIT(KiInterruptDispatchRaise) SBTTL("Interrupt Dispatch - Same IRQL") //++ // // VOID // KiInterruptDispatchSame ( // IN PKINTERRUPT Interrupt, // IN PVOID ServiceContext, // IN PVOID TrapFrame // ) // // Routine Description: // // This routine is entered as the result of an interrupt being generated // via a vector that is connected to an interrupt object. Its function is // to directly call the specified interrupt service routine. // // Note: On PowerPC, if uniprocessor, this routine is bypassed completely. // The Interrupt Object is initialized such that KiInterruptException will // dispatch directly to the service routine. // // Arguments: // // Interrupt (r3) - Supplies a pointer to the Interrupt Object. // // ServiceContext (r4) - Supplies a pointer to the Service Context associated // with this Interrupt Object. // // TrapFrame (r5) - Supplies the address of the Trap Frame created as a // result of this interrupt. // // Return Value: // // None. // //-- .struct 0 .space StackFrameHeaderLength kidsLR: .space 4 kidsR31: .space 4 .align 3 // ensure 8 byte alignment kidsFrameLength: // // N.B. We do not save/restore the TOC pointer in this routine. // This is safe because our caller called us via a function // descriptor and will therefore restore the TOC when we return. // #if defined(NT_UP) LEAF_ENTRY(KiInterruptDispatchSame) lwz r8, InServiceRoutine(r3) // get service routine descriptor lwz r6, 0(r8) // get address of service routine lwz rtoc, 4(r8) // get service routine TOC mtctr r6 // put routine address in CTR lwz r4, InServiceContext(r3) // get service context bctr // jump to service routine DUMMY_EXIT(KiInterruptDispatchSame) #else SPECIAL_ENTRY(KiInterruptDispatchSame) mflr r0 // get return address stwu sp, -kidsFrameLength(sp) // allocate stack frame stw r31, kidsR31(sp) stw r0, kidsLR(sp) // save return address PROLOGUE_END(KiInterruptDispatchSame) lwz r8, InServiceRoutine(r3) // get service routine descriptor // // Acquire the service routine spin lock and call the service routine. // lwz r31, InActualLock(r3) // get address of spin lock lwz r6, 0(r8) // get address of service routine ACQUIRE_SPIN_LOCK(r31, r31, r7, kids_lock, kids_lock_spin) lwz rtoc, 4(r8) // get service routine TOC mtctr r6 // put routine address in CTR lwz r4, InServiceContext(r3) // get service context bctrl // call service routine // // Release the service routine spin lock. Restore nonvolatile registers, // retrieve return address, deallocate stack frame, and return. // li r4, 0 // get a 0 for spin lock lwz r0, kidsLR(sp) // get return address RELEASE_SPIN_LOCK(r31, r4) lwz r31, kidsR31(sp) // restore r31 mtlr r0 // set return address addi sp, sp, kidsFrameLength // return stack frame blr SPIN_ON_SPIN_LOCK(r31, r7, kids_lock, kids_lock_spin) DUMMY_EXIT(KiInterruptDispatchSame) #endif // else defined(NT_UP) SBTTL("Unexpected Interrupt") //++ // // Routine Description: // // This routine is entered as the result of an interrupt being generated // via a vector that is not connected to an interrupt object. Its function // is to report the error and dismiss the interrupt. // // Arguments: // // Interrupt (r3) - Supplies a pointer to the Interrupt Object. // // ServiceContext (r4) - Supplies a pointer to the Service Context associated // with this Interrupt Object. // // TrapFrame (r5) - Supplies the address of the Trap Frame created as a // result of this interrupt. // // Return Value: // // None. // //-- LEAF_ENTRY(KiUnexpectedInterrupt) LEAF_EXIT(KiUnexpectedInterrupt) // ****** temp ******