//++ // // Module Name: // // miscs.s // // Abstract: // // This module implements machine dependent miscellaneous kernel functions. // Functions are provided to request a software interrupt, continue thread // execution, flush TLBs and write buffers, and perform last chance // exception processing. // // Author: // // William K. Cheung (wcheung) 3-Nov-1995 // // Environment: // // Kernel mode only. // // Revision History: // // 7-Jul-1997 bl Updated to EAS2.3 // // 27-Feb-1996 wc Updated to EAS2.1 // // 11-Jan-1996 wc Set up register sp to point to the exception frame // on the stack before calling KiSaveExceptionFrame and // branching directly to KiExceptionExit. // //-- #include "ksia64.h" // // Global symbols // PublicFunction(KiContinue) PublicFunction(KiSaveExceptionFrame) PublicFunction(KeTestAlertThread) PublicFunction(KiExceptionExit) PublicFunction(KiRaiseException) PublicFunction(KiLoadKernelDebugRegisters) PublicFunction(KeIsExecutingDpc) // //++ // // NTSTATUS // NtContinue ( // IN PCONTEXT ContextRecord, // IN BOOLEAN TestAlert // ) // // Routine Description: // // This routine is called as a system service to continue execution after // an exception has occurred. Its functions is to transfer information from // the specified context record into the trap frame that was built when the // system service was executed, and then exit the system as if an exception // had occurred. // // Arguments: // // ContextRecord (a0) - Supplies a pointer to a context record. // // TestAlert (a1) - Supplies a boolean value that specifies whether alert // should be tested for the previous processor mode. // // N.B. Register t0 is assumed to contain the address of a trap frame. // // Return Value: // // Normally there is no return from this routine. However, if the specified // context record is misaligned or is not accessible, then the appropriate // status code is returned. // //-- NESTED_ENTRY(NtContinue) NESTED_SETUP(2,4,3,0) .fframe ExceptionFrameLength add sp = -ExceptionFrameLength, sp PROLOGUE_END // // Transfer information from the context record to the exception and trap // frames. // // N.B. Must zero the loadrs bit-field of Context->RsRSC // add t1 = TrStIPSR, t0 // -> TrapFrame->StIPSR mov loc2 = t0 ;; ld8 loc3 = [t1] // load TrapFrame->StIPSR mov out0 = a0 // context frame address ;; add out1 = STACK_SCRATCH_AREA, sp // -> exception frame mov out2 = t0 // trap frame address br.call.sptk.many brp = KiContinue ;; // // If KiContinue() returns success, then exit via the exception exit code. // Otherwise, return to the system service dispatcher. // // Check to determine if alert should be tested for the previous processor // mode and restore the previous mode in the thread object. // // Application that invokes the NtContinue() system service must have // flushed all stacked registers to the backing store. Sanitize the // bspstore to be equal to bsp; otherwise, some stacked GRs will not be // restored from the backing store. // add t0 = TrStIPSR, loc2 movl t7 = 1 << PSR_TB | 1 << PSR_DB | 1 << PSR_SS | 1 << PSR_PP ;; ld8 t8 = [t0] cmp.ne pt0 = zero, v0 // if ne, transfer failed. ;; (pt0) dep t7 = 1, t7, PSR_LP, 1 // capture psr.lp if failed ;; add t2 = TrTrapFrame, loc2 andcm t8 = t8, t7 // Clear old values and loc3 = t7, loc3 // capture psr.tb, db, ss, pp ;; or t8 = loc3, t8 ;; st8 [t0] = t8 add t3 = TrRsRSC, loc2 (pt0) br.cond.spnt Nc10 // jump to Nc10 if pt0 is TRUE ;; // // Restore the nonvolatile machine state from the exception frame // and exit the system via the exception exit code. // ld8 t5 = [t2] // get old trap frame address movl t1 = KiPcr + PcCurrentThread // -> current thread ;; ld8 t0 = [t3], TrPreviousMode-TrRsRSC // load TrapFrame->RsRSC ld8 t4 = [t1] // get current thread address cmp4.ne pt1 = zero, a1 // if ne, test for alert ;; ld4 t6 = [t3], TrRsRSC-TrPreviousMode // get old previous mode dep t0 = r0, t0, RSC_MBZ1, RSC_LOADRS_LEN // zero preload field add t7 = ThPreviousMode, t4 ;; (pt1) ld1 out0 = [t7] // get current previous mode st8 [t3] = t0 // save TrapFrame->RsRSC add t8 = ThTrapFrame, t4 ;; st8 [t8] = t5 // restore old trap frame addr st1 [t7] = t6 // restore old previous mode (pt1) br.call.spnt.many brp = KeTestAlertThread ;; // // sp -> stack scratch area/FP save area/exception frame/trap frame // // Set up for branch to KiExceptionExit // // s0 = trap frame // s1 = exception frame // // N.B. predicate register alias pUstk & pKstk must be the same as trap.s // and they must be set up correctly upon entry into KiExceptionExit. // // N.B. The exception exit code will restore the exception frame & trap frame // and then rfi to user code. pUstk is set to 1 while pKstk is set to 0. // pUstk = ps3 pKstk = ps4 // // Interrupts must be disabled before calling KiExceptionExit // because the unwind code cannot unwind from that point. // FAST_DISABLE_INTERRUPTS cmp.eq pUstk, pKstk = zero, zero add s1 = STACK_SCRATCH_AREA, sp mov s0 = loc2 br KiExceptionExit Nc10: .restore add sp = ExceptionFrameLength, sp NESTED_RETURN NESTED_EXIT(NtContinue) //++ // // NTSTATUS // NtRaiseException ( // IN PEXCEPTION_RECORD ExceptionRecord, // IN PCONTEXT ContextRecord, // IN BOOLEAN FirstChance // ) // // Routine Description: // // This routine is called as a system service to raise an exception. // The exception can be raised as a first or second chance exception. // // Arguments: // // ExceptionRecord (a0) - Supplies a pointer to an exception record. // // ContextRecord (a1) - Supplies a pointer to a context record. // // FirstChance (a2) - Supplies a boolean value that determines whether // this is the first (TRUE) or second (FALSE) chance for dispatching // the exception. // // N.B. Register t0 is assumed to contain the address of a trap frame. // // Return Value: // // Normally there is no return from this routine. However, if the specified // context record or exception record is misaligned or is not accessible, // then the appropriate status code is returned. // //-- NESTED_ENTRY(NtRaiseException) NESTED_SETUP(3,3,5,0) .fframe ExceptionFrameLength add sp = -ExceptionFrameLength, sp ;; PROLOGUE_END // // Pop this trap frame off the thread list. // add t2 = TrTrapFrame, t0 movl t1 = KiPcr + PcCurrentThread ;; ld8 t1 = [t1]; // Get current thread. ld8 t2 = [t2]; // Load previous trap frame ;; add t1 = ThTrapFrame, t1 // // Save nonvolatile states. // add out0 = STACK_SCRATCH_AREA, sp mov loc2 = t0 // save pointer to trap frame ;; st8 [t1] = t2 br.call.sptk brp = KiSaveExceptionFrame // // Call the raise exception kernel routine wich will marshall the argments // and then call the exception dispatcher. // add out2 = STACK_SCRATCH_AREA, sp // -> exception frame mov out1 = a1 mov out0 = a0 add out4 = zero, a2 mov out3 = t0 br.call.sptk.many brp = KiRaiseException // // If the raise exception routine returns success, then exit via the exception // exit code. Otherwise, return to the system service dispatcher. // // N.B. The exception exit code will restore the exception frame & trap frame // and then rfi to user code. // // Set up for branch to KiExceptionExit // // s0 = trap frame // s1 = exception frame // pUstk = ps3 pKstk = ps4 cmp4.ne p0, pt1 = zero, v0 // if ne, dispatch failed. ;; // // Interrupts must be disabled before calling KiExceptionExit // because the unwind code cannot unwind from that point. // (pt1) FAST_DISABLE_INTERRUPTS (pt1) mov s0 = loc2 // copy trap frame pointer (pt1) add s1 = STACK_SCRATCH_AREA, sp (pt1) cmp.eq pUstk, pKstk = zero, zero (pt1) br.cond.sptk.many KiExceptionExit .restore add sp = ExceptionFrameLength, sp NESTED_RETURN NESTED_EXIT(NtRaiseException) //++ // // VOID // KeFillLargeEntryTb ( // IN HARDWARE_PTE Pte[], // IN PVOID Virtual, // IN ULONG PageSize // ) // // Routine Description: // // This function fills a large translation buffer entry. // // N.B. It is assumed that the large entry is not in the TB and therefore // the TB is not probed. // // Arguments: // // Pte (a0) - Supplies a pointer to the page table entries that are to be // written into the TB. // // Virtual (a1) - Supplies the virtual address of the entry that is to // be filled in the translation buffer. // // PageSize (a2) - Supplies the size of the large page table entry. // // Return Value: // // None. // //-- LEAF_ENTRY(KeFillLargeEntryTb) rPte = t0 rScnd = t1 ridtr = t2 rid = t3 rDtr = t4 rTb = t6 rTbPFN = t7 rpAttr = t8 rAttrOffset = t9 shr.u rScnd = a2, 6 // mask off page size fields ;; shl rScnd = rScnd, 6 // ;; and rScnd = a1, rScnd // examine the virtual address bit ;; cmp.eq pt0, pt1 = r0, rScnd shl ridtr = a2, PS_SHIFT mov rDtr = DTR_VIDEO_INDEX ;; rsm 1 << PSR_I // turn off interrupts (pt0) add a0 = (1 << PTE_SHIFT), a0 ;; ld8 rPte = [a0] // load PTE rsm 1 << PSR_IC // interrupt is off, now reset PSR.ic ;; srlz.d // serialize mov cr.itir = ridtr // idtr for insertion mov cr.ifa = a1 // ifa for insertion ;; itr.d dtr[rDtr] = rPte ssm 1 << PSR_IC // set PSR.ic bit again ;; srlz.i // I serialize ssm 1 << PSR_I LEAF_RETURN LEAF_EXIT(KeFillLargeEntryTb) // return //++ // // VOID // KeFillFixedEntryTb ( // IN HARDWARE_PTE Pte[], // IN PVOID Virtual, // IN ULONG PageSize, // IN ULONG Index // ) // // Routine Description: // // This function fills a fixed translation buffer entry. // // Arguments: // // Pte (a0) - Supplies a pointer to the page table entries that are to be // written into the TB. // // Virtual (a1) - Supplies the virtual address of the entry that is to // be filled in the translation buffer. // // Index (a2) - Supplies the index where the TB entry is to be written. // // Return Value: // // None. // // Comments: // // // //-- LEAF_ENTRY(KeFillFixedEntryTb) rPte = t0 rScnd = t1 ridtr = t2 rid = t3 rIndex = t4 rTb = t6 rTbPFN = t7 rpAttr = t8 rAttrOffset = t9 rsm 1 << PSR_I // reset PSR.i ld8 rPte = [a0] // load PTE shl ridtr = a2, PS_SHIFT ;; rsm 1 << PSR_IC // interrupt is off, now reset PSR.ic tbit.z pt0, pt1 = a3, 31 // check the sign bit // if 1 ITR, otherwise DTR ;; srlz.d // serialize and rIndex = 0xf, a3 ;; mov cr.itir = ridtr // idtr for insertion mov cr.ifa = a1 // ifa for insertion ;; (pt0) itr.d dtr[rIndex] = rPte // insert into DTR (pt1) itr.i itr[rIndex] = rPte // insert into ITR ssm 1 << PSR_IC // set PSR.ic bit again ;; srlz.i // I serialize #if DBG mov t10 = PbProcessorState+KpsSpecialRegisters+KsTrD0 movl t13 = KiPcr + PcPrcb ;; ld8 t13 = [t13] mov t14 = PbProcessorState+KpsSpecialRegisters+KsTrI0 ;; add t10 = t10, t13 add t14 = t14, t13 ;; (pt0) shladd t15 = rIndex, 3, t10 (pt1) shladd t15 = rIndex, 3, t14 ;; (pt0) st8 [t15] = rPte (pt1) st8 [t15] = rPte ;; #endif ssm 1 << PSR_I LEAF_RETURN LEAF_EXIT(KeFillFixedEntryTb) //++ // // VOID // KeFillFixedLargeEntryTb ( // IN HARDWARE_PTE Pte[], // IN PVOID Virtual, // IN ULONG PageSize, // IN ULONG Index // ) // // Routine Description: // // This function fills a fixed translation buffer entry with a large page // size. // // Arguments: // // Pte (a0) - Supplies a pointer to the page table entries that are to be // written into the TB. // // Virtual (a1) - Supplies the virtual address of the entry that is to // be filled in the translation buffer. // // PageSize (a2) - Supplies the size of the large page table entry. // // Index (a3) - Supplies the index where the TB entry is to be written. // // Return Value: // // None. // // Comments: // // Yet to be implemented. // //-- LEAF_ENTRY(KeFillFixedLargeEntryTb) rPte = t0 rScnd = t1 ridtr = t2 rid = t3 rIndex = t4 rTb = t6 rTbPFN = t7 rpAttr = t8 rAttrOffset = t9 rsm 1 << PSR_I // reset PSR.i ld8 rPte = [a0] // load PTE shl ridtr = a2, PS_SHIFT ;; rsm 1 << PSR_IC // interrupt is off, now reset PSR.ic and rIndex = 0xf, a3 // set the DTR index tbit.z pt0, pt1 = a3, 31 // check the sign bit ;; srlz.d // serialize mov cr.itir = ridtr // idtr for insertion mov cr.ifa = a1 // ifa for insertion ;; (pt0) itr.d dtr[rIndex] = rPte // insert into DTR (pt1) itr.i itr[rIndex] = rPte // insert into ITR ssm 1 << PSR_IC // set PSR.ic bit again ;; srlz.i // I serialize ssm 1 << PSR_I LEAF_RETURN LEAF_EXIT(KeFillFixedLargeEntryTb) // return //++ // // VOID // KeFillInstEntryTb ( // IN HARDWARE_PTE Pte[], // IN PVOID Virtual, // ) // // Routine Description: // // This function fills a large translation buffer entry. // // N.B. It is assumed that the large entry is not in the TB and therefore // the TB is not probed. // // Arguments: // // Pte (a0) - Supplies a page table entry that is to be // written into the Inst TB. // // Virtual (a1) - Supplies the virtual address of the entry that is to // be filled in the translation buffer. // // Return Value: // // None. // //-- LEAF_ENTRY(KeFillInstEntryTb) riitr = t2 rid = t3 rsm 1 << PSR_I // reset PSR.i ;; rsm 1 << PSR_IC // interrupt is off, now reset PSR.ic mov riitr = PAGE_SIZE << PS_LEN ;; srlz.d // serialize mov cr.ifa = a1 // set va to install mov cr.itir = riitr // iitr for insertion ;; itc.i a0 ;; ssm 1 << PSR_IC // set PSR.ic bit again ;; srlz.i // I serialize ssm 1 << PSR_I LEAF_RETURN LEAF_EXIT(KeFillInstEntryTb) // return //++ // // VOID // KeBreakinBreakpoint // VOID // ) // // Routine Description: // // This function causes a BREAKIN breakpoint. // // Arguments: // // None. // // Return Value: // // None. // //-- LEAF_ENTRY(KeBreakinBreakpoint) // // Flush the RSE or the kernel debugger is unable to do a stack unwind // flushrs ;; break.i BREAKPOINT_BREAKIN LEAF_RETURN LEAF_EXIT(KeBreakinBreakpoint) #ifdef WX86 //++ // // VOID // KiIA32RegistersInit // VOID // ) // // Routine Description: // // This function to Initialize per processor IA32 related registers // These registers do not saved/restored on context switch time // // Arguments: // // None. // // Return Value: // // None. // //-- LEAF_ENTRY(KiIA32RegistersInit) mov t0 = TeGdtDescriptor mov iA32iobase = 0 ;; mov iA32index = t0 LEAF_RETURN LEAF_EXIT(KiIA32RegistersInit) #endif // WX86 //++ // // PKTHREAD // KeGetCurrentThread (VOID) // // Routine Description: // // Arguments: // // None. // // Return Value: // // Returns a pointer to the executing thread object. // //-- LEAF_ENTRY(KeGetCurrentThread) movl v0 = KiPcr + PcCurrentThread // -> current thread ;; ld8 v0 = [v0] br.ret.sptk brp LEAF_EXIT(KeGetCurrentThread) //++ // // BOOLEAN // KeIsExecutingDpc (VOID) // // Routine Description: // // Arguments: // // None. // // Return Value: // // Return a value which indicates if we are currently in a DPC. // //-- LEAF_ENTRY(KeIsExecutingDpc) rsm 1 << PSR_I // disable interrupt movl v0 = KiPcr + PcPrcb ;; ld8 v0 = [v0] ;; add v0 = PbDpcRoutineActive, v0 ;; ld4 v0 = [v0] ssm 1 << PSR_I // enable interrupt br.ret.sptk brp LEAF_EXIT(KeIsExecutingDpc) //++ // // Routine Description: // // This routine saves the thread's current non-volatile NPX state, // and sets a new initial floating point state for the caller. // // This is intended for use by kernel-mode code that needs to use // the floating point registers. Must be paired with // KeRestoreFloatingPointState // // Arguments: // // a0 - Supplies pointer to KFLOATING_SAVE structure // // Return Value: // // None. // //-- LEAF_ENTRY(KeSaveFloatingPointState) mov v0 = zero LEAF_RETURN LEAF_EXIT(KeSaveFloatingPointState) //++ // // Routine Description: // // This routine restores the thread's current non-volatile NPX state, // to the passed in state. // // This is intended for use by kernel-mode code that needs to use // the floating point registers. Must be paired with // KeSaveFloatingPointState // // Arguments: // // a0 - Supplies pointer to KFLOATING_SAVE structure // // Return Value: // // None. // //-- LEAF_ENTRY(KeRestoreFloatingPointState) mov v0 = zero LEAF_RETURN LEAF_EXIT(KeRestoreFloatingPointState) //++ // // Routine Description: // // This routine flush all the dirty registers to the backing store // and invalidate them. // // Arguments: // // None. // // Return Value: // // None. // //-- LEAF_ENTRY(KiFlushRse) flushrs mov t1 = ar.rsc mov t0 = RSC_KERNEL_DISABLED ;; mov ar.rsc = t0 ;; loadrs ;; mov ar.rsc = t1 ;; br.ret.sptk brp LEAF_EXIT(KiFlushRse) #if 0 //++ // // Routine Description: // // This routine invalidate all the physical stacked registers. // // Arguments: // // None. // // Return Value: // // None. // //-- LEAF_ENTRY(KiInvalidateStackedRegisters) mov t1 = ar.rsc mov t0 = RSC_KERNEL_DISABLED ;; mov ar.rsc = t0 ;; loadrs ;; mov ar.rsc = t1 ;; br.ret.sptk brp LEAF_EXIT(KiInvalidateStackedRegisters) #endif // 0 //++ // // VOID // KeSetLowPsrBit ( // UCHAR BitPosition, // BOOLEAN Value // ) // // Routine Description: // // This routine set one of the low psr bits to the specified value. // // Arguments: // // a0 - bit position // a1 - 1 or 0 // // Return Value: // // None. // //-- LEAF_ENTRY(KeSetLowPsrBit) mov t1 = psr mov t2 = 1 cmp.ne pt1, pt0 = r0, a1 ;; shl t2 = t2, a0 ;; (pt1) or t3 = t1, t2 (pt0) andcm t3 = t1, t2 ;; mov psr.l = t3 ;; srlz.i br.ret.sptk brp LEAF_EXIT(KeSetLowPsrBit) //++ // // PVOID // KiGetPhysicalAddress( // PVOID Virtual // ) // // Routine Description: // // This routine translates to physical address uing TPA instruction. // // Arguments: // // a0 - virtual address to be translated to physical address. // // Return Value: // // physical address // //-- LEAF_ENTRY(KiGetPhysicalAddress) tpa r8 = a0 LEAF_RETURN LEAF_EXIT(KiGetPhysicalAddress) //++ // // VOID // KiSetRegionRegister( // PVOID Region, // ULONGLONG Contents // ) // // Routine Description: // // This routine sets the value of a region register. // // Arguments: // // a0 - Supplies the region register # // // a1 - Supplies the value to be stored in the specified region register // // Return Value: // // None. // //-- LEAF_ENTRY(KiSetRegionRegister) mov rr[a0] = a1 ;; srlz.i LEAF_RETURN LEAF_EXIT(KiSetRegionId) LEAF_ENTRY(KiSaveProcessorControlState) // // save region registers // add t2 = KpsSpecialRegisters+KsRr0, a0 dep.z t0 = 0, RR_INDEX, RR_INDEX_LEN ;; mov t1 = rr[t0] dep.z t3 = 1, RR_INDEX, RR_INDEX_LEN ;; mov t4 = rr[t3] st8 [t2] = t1, KsRr1-KsRr0 dep.z t0 = 2, RR_INDEX, RR_INDEX_LEN ;; mov t1 = rr[t0] st8 [t2] = t4, KsRr2-KsRr1 dep.z t3 = 3, RR_INDEX, RR_INDEX_LEN ;; mov t4 = rr[t3] st8 [t2] = t1, KsRr3-KsRr2 dep.z t0 = 4, RR_INDEX, RR_INDEX_LEN ;; mov t1 = rr[t0] st8 [t2] = t4, KsRr4-KsRr3 dep.z t3 = 5, RR_INDEX, RR_INDEX_LEN ;; mov t4 = rr[t3] st8 [t2] = t1, KsRr5-KsRr4 dep.z t0 = 6, RR_INDEX, RR_INDEX_LEN ;; mov t1 = rr[t0] st8 [t2] = t4, KsRr6-KsRr5 dep.z t3 = 7, RR_INDEX, RR_INDEX_LEN ;; mov t4 = rr[t3] st8 [t2] = t1, KsRr7-KsRr6 ;; st8 [t2] = t4 // // save ITC, ITM, IVA, PTA and TPR // mov t0 = ar.itc mov t1 = cr.itm add t3 = KpsSpecialRegisters+KsApITC, a0 add t4 = KpsSpecialRegisters+KsApITM, a0 ;; mov t5 = cr.iva mov t6 = cr.pta st8 [t3] = t0, KsApIVA-KsApITC st8 [t4] = t1, KsApPTA-KsApITM ;; mov t0 = cr.tpr mov t1 = ar.k0 st8 [t3] = t5, KsSaTPR-KsApIVA st8 [t4] = t6, KsApKR0-KsApPTA ;; mov t5 = ar.k1 mov t6 = ar.k2 st8 [t3] = t0, KsApKR1-KsSaTPR st8 [t4] = t1, KsApKR2-KsApKR0 ;; mov t0 = ar.k3 mov t1 = ar.k4 st8 [t3] = t5, KsApKR3-KsApKR1 st8 [t4] = t6, KsApKR4-KsApKR2 ;; mov t5 = ar.k5 mov t6 = ar.k6 st8 [t3] = t0, KsApKR5-KsApKR3 st8 [t4] = t1, KsApKR6-KsApKR4 ;; mov t0 = ar.k7 mov t1 = cr.lid st8 [t3] = t5, KsApKR7-KsApKR5 st8 [t4] = t6, KsSaLID-KsApKR6 ;; mov t5 = cr.irr0 mov t6 = cr.irr1 st8 [t3] = t0, KsSaIRR0-KsApKR7 st8 [t4] = t1, KsSaIRR1-KsSaLID ;; mov t0 = cr.irr2 mov t1 = cr.irr3 st8 [t3] = t5, KsSaIRR2-KsSaIRR0 st8 [t4] = t6, KsSaIRR3-KsSaIRR1 ;; mov t5 = cr.itv mov t6 = cr.pmv st8 [t3] = t0, KsSaITV-KsSaIRR2 st8 [t4] = t1, KsSaPMV-KsSaIRR3 ;; mov t0 = cr.cmcv mov t1 = cr.lrr0 st8 [t3] = t5, KsSaCMCV-KsSaITV st8 [t4] = t6, KsSaLRR0-KsSaPMV ;; mov t5 = cr.lrr1 mov t6 = cr.gpta st8 [t3] = t0, KsSaLRR1-KsSaCMCV st8 [t4] = t1, KsApGPTA-KsSaLRR0 mov t7 = 0 mov t8 = 1 ;; mov t0 = cpuid[t7] mov t1 = cpuid[t8] st8 [t3] = t5 st8 [t4] = t6 mov t9 = 2 mov t10 = 3 add t3 = KpsSpecialRegisters+KsApCPUID0, a0 add t4 = KpsSpecialRegisters+KsApCPUID1, a0 ;; mov t5 = cpuid[t9] mov t6 = cpuid[t10] st8 [t3] = t0, KsApCPUID2-KsApCPUID0 st8 [t4] = t1, KsApCPUID3-KsApCPUID1 mov t7 = 4 mov t8 = 652 ;; mov t0 = cpuid[t7] st8 [t3] = t5, KsApCPUID4-KsApCPUID2 st8 [t4] = t6 ;; st8 [t3] = t0 LEAF_RETURN LEAF_EXIT(KiSaveProcessorControlState) NESTED_ENTRY(KiRestoreProcessorControlState) NESTED_SETUP(0,2,0,0) ;; br.call.spnt brp = KiLoadKernelDebugRegisters ;; NESTED_RETURN NESTED_EXIT(KiRestoreProcessorControlState) PublicFunction(KiSaveExceptionFrame) PublicFunction(KiRestoreExceptionFrame) PublicFunction(KiIpiServiceRoutine) NESTED_ENTRY(KeIpiInterrupt) NESTED_SETUP(1, 2, 2, 0) .fframe ExceptionFrameLength add sp = -ExceptionFrameLength, sp ;; PROLOGUE_END add out0 = STACK_SCRATCH_AREA, sp // -> exception frame br.call.sptk brp = KiSaveExceptionFrame ;; add out1 = STACK_SCRATCH_AREA, sp // -> exception frame mov out0 = a0 // -> trap frame br.call.sptk brp = KiIpiServiceRoutine ;; add out0 = STACK_SCRATCH_AREA, sp // -> exception frame br.call.sptk brp = KiRestoreExceptionFrame ;; add sp = ExceptionFrameLength, sp NESTED_RETURN NESTED_EXIT(KeIpiInterrupt) LEAF_ENTRY(KiReadMsr) mov v0 = msr[a0] LEAF_RETURN LEAF_EXIT(KiReadMsr) LEAF_ENTRY(KiWriteMsr) mov msr[a0] = a1 LEAF_RETURN LEAF_EXIT(KiWriteMsr)