diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index 3577748b207..b8649c3dd26 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -863,6 +863,10 @@ KeBugCheckWithTf( PKTRAP_FRAME Tf ); +BOOLEAN +NTAPI +KiHandleNmi(VOID); + VOID NTAPI KeFlushCurrentTb(VOID); diff --git a/reactos/ntoskrnl/ke/i386/cpu.c b/reactos/ntoskrnl/ke/i386/cpu.c index 700454b7a02..606162ee6b3 100644 --- a/reactos/ntoskrnl/ke/i386/cpu.c +++ b/reactos/ntoskrnl/ke/i386/cpu.c @@ -944,6 +944,154 @@ KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame, KiSaveProcessorControlState(&Prcb->ProcessorState); } +/* C TRAP HANDLERS ************************************************************/ + +BOOLEAN +NTAPI +KiNmiFault(IN PVOID InterruptStack) +{ + PKTSS Tss, NmiTss; + PKTHREAD Thread; + PKPROCESS Process; + PKGDTENTRY TssGdt; + KTRAP_FRAME TrapFrame; + KIRQL OldIrql; + + // + // In some sort of strange recursion case, we might end up here with the IF + // flag incorrectly on the interrupt frame -- during a normal NMI this would + // normally already be set. + // + // For sanity's sake, make sure interrupts are disabled for sure. + // NMIs will already be since the CPU does it for us. + // + _disable(); + + // + // Get the current TSS, thread, and process + // + Tss = PCR->TSS; + Thread = ((PKIPCR)PCR)->PrcbData.CurrentThread; + Process = Thread->ApcState.Process; + + // + // Save data usually not in the TSS + // + Tss->CR3 = Process->DirectoryTableBase[0]; + Tss->IoMapBase = Process->IopmOffset; + Tss->LDT = Process->LdtDescriptor.LimitLow ? KGDT_LDT : 0; + + // + // Now get the base address of the NMI TSS + // + TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_NMI_TSS / sizeof(KGDTENTRY)]; + NmiTss = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow | + TssGdt->HighWord.Bytes.BaseMid << 16 | + TssGdt->HighWord.Bytes.BaseHi << 24); + + // + // Switch to it and activate it, masking off the nested flag + // + // Note that in reality, we are already on the NMI tss -- we just need to + // update the PCR to reflect this + // + PCR->TSS = NmiTss; + __writeeflags(__readeflags() &~ EFLAGS_NESTED_TASK); + TssGdt->HighWord.Bits.Dpl = 0; + TssGdt->HighWord.Bits.Pres = 1; + TssGdt->HighWord.Bits.Type = I386_TSS; + + // + // Now build the trap frame based on the original TSS + // + // The CPU does a hardware "Context switch" / task switch of sorts and so it + // takes care of saving our context in the normal TSS. + // + // We just have to go get the values... + // + RtlZeroMemory(&TrapFrame, sizeof(KTRAP_FRAME)); + TrapFrame.HardwareSegSs = Tss->Ss0; + TrapFrame.HardwareEsp = Tss->Esp0; + TrapFrame.EFlags = Tss->EFlags; + TrapFrame.SegCs = Tss->Cs; + TrapFrame.Eip = Tss->Eip; + TrapFrame.Ebp = Tss->Ebp; + TrapFrame.Ebx = Tss->Ebx; + TrapFrame.Esi = Tss->Esi; + TrapFrame.Edi = Tss->Edi; + TrapFrame.SegFs = Tss->Fs; + TrapFrame.ExceptionList = PCR->Tib.ExceptionList; + TrapFrame.PreviousPreviousMode = -1; + TrapFrame.Eax = Tss->Eax; + TrapFrame.Ecx = Tss->Ecx; + TrapFrame.Edx = Tss->Edx; + TrapFrame.SegDs = Tss->Ds; + TrapFrame.SegEs = Tss->Es; + TrapFrame.SegGs = Tss->Gs; + TrapFrame.DbgEip = Tss->Eip; + TrapFrame.DbgEbp = Tss->Ebp; + + // + // Store the trap frame in the KPRCB + // + KiSaveProcessorState(&TrapFrame, NULL); + + // + // Call any registered NMI handlers and see if they handled it or not + // + if (!KiHandleNmi()) + { + // + // They did not, so call the platform HAL routine to bugcheck the system + // + // Make sure the HAL believes it's running at HIGH IRQL... we can't use + // the normal APIs here as playing with the IRQL could change the system + // state + // + OldIrql = PCR->Irql; + PCR->Irql = HIGH_LEVEL; + HalHandleNMI(NULL); + PCR->Irql = OldIrql; + } + + // + // Although the CPU disabled NMIs, we just did a BIOS Call, which could've + // totally changed things. + // + // We have to make sure we're still in our original NMI -- a nested NMI + // will point back to the NMI TSS, and in that case we're hosed. + // + if (PCR->TSS->Backlink != KGDT_NMI_TSS) + { + // + // Restore original TSS + // + PCR->TSS = Tss; + + // + // Set it back to busy + // + TssGdt->HighWord.Bits.Dpl = 0; + TssGdt->HighWord.Bits.Pres = 1; + TssGdt->HighWord.Bits.Type = I386_ACTIVE_TSS; + + // + // Restore nested flag + // + __writeeflags(__readeflags() | EFLAGS_NESTED_TASK); + + // + // Handled, return from interrupt + // + return TRUE; + } + + // + // Unhandled: crash the system + // + return FALSE; +} + /* PUBLIC FUNCTIONS **********************************************************/ /* diff --git a/reactos/ntoskrnl/ke/i386/trap.s b/reactos/ntoskrnl/ke/i386/trap.s index 3eefe665315..b51a726b22e 100644 --- a/reactos/ntoskrnl/ke/i386/trap.s +++ b/reactos/ntoskrnl/ke/i386/trap.s @@ -791,143 +791,24 @@ V86Int1: .globl _KiTrap2 .func KiTrap2 _KiTrap2: - // - // Don't allow any other NMIs to come in for now - // - cli // Disable interrupts + // + // Call the C handler + // + stdCall _KiNmiFault, esp // Handle it in C + or al, al // Check if it got handled + jne 1f // Resume from NMI - // - // Save current state data in registers - // - mov eax, PCR[KPCR_TSS] // Save KTSS - mov ecx, PCR[KPCR_CURRENT_THREAD] // Save ETHREAD - mov edi, [ecx+KTHREAD_APCSTATE_PROCESS] // Save EPROCESS - - // - // Migrate state data to TSS - // - mov ecx, [edi+KPROCESS_DIRECTORY_TABLE_BASE] // Page Directory Table - mov [eax+KTSS_CR3], ecx // Saved in CR3 - mov cx, [edi+KPROCESS_IOPM_OFFSET] // IOPM Offset - mov [eax+KTSS_IOMAPBASE], cx // Saved in IOPM Base - mov ecx, [edi+KPROCESS_LDT_DESCRIPTOR0] // Get LDT descriptor - test ecx, ecx // Check if ne - jz 1f // Doesn't exist - mov cx, KGDT_LDT // Load LDT descriptor + // + // Return from NMI + // + iretd // Interrupt return + jmp _KiTrap2 // Handle recursion 1: - mov [eax+KTSS_LDT], cx // Saved in LDT - - // - // Migrate to NMI TSS - // - push PCR[KPCR_TSS] // Save current TSS - mov eax, PCR[KPCR_GDT] // Get GDT - mov ch, [eax+KGDT_NMI_TSS+KGDT_BASE_HI] // Get High KTSS Base - mov cl, [eax+KGDT_NMI_TSS+KGDT_BASE_MID] // Get Mid KTSS Base - shl ecx, 16 // Build Top KTSS Base - mov cx, [eax+KGDT_NMI_TSS+KGDT_BASE_LOW] // Add Low KTSS Base - mov PCR[KPCR_TSS], ecx - - // - // Clear nested flag and activate the NMI TSS - // - pushf // Get EFLAGS - and dword ptr [esp], ~EFLAGS_NESTED_TASK // Clear nested task - popf // Set EFLAGS - mov ecx, PCR[KPCR_GDT] // Get GDT - lea eax, [ecx+KGDT_NMI_TSS] // Get NMI TSS - mov byte ptr [eax+5], 0x89 // DPL 0, Present, NonBusy - - // - // Build the trap frame and save it into the KPRCB - // - mov eax, [esp] // KGDT_TSS from earlier - push 0 // V86 segments - push 0 // V86 segments - push 0 // V86 segments - push 0 // V86 segments - push [eax+KTSS_SS] // TSS fields -> Trap Frame - push [eax+KTSS_ESP] // TSS fields -> Trap Frame - push [eax+KTSS_EFLAGS] // TSS fields -> Trap Frame - push [eax+KTSS_CS] // TSS fields -> Trap Frame - push [eax+KTSS_EIP] // TSS fields -> Trap Frame - push 0 // Error Code - push [eax+KTSS_EBP] // TSS fields -> Trap Frame - push [eax+KTSS_EBX] // TSS fields -> Trap Frame - push [eax+KTSS_ESI] // TSS fields -> Trap Frame - push [eax+KTSS_EDI] // TSS fields -> Trap Frame - push [eax+KTSS_FS] // TSS fields -> Trap Frame - push PCR[KPCR_EXCEPTION_LIST] // SEH Handler from KPCR - push -1 // Bogus previous mode - push [eax+KTSS_EAX] // TSS fields -> Trap Frame - push [eax+KTSS_ECX] // TSS fields -> Trap Frame - push [eax+KTSS_EDX] // TSS fields -> Trap Frame - push [eax+KTSS_DS] // TSS fields -> Trap Frame - push [eax+KTSS_ES] // TSS fields -> Trap Frame - push [eax+KTSS_GS] // TSS fields -> Trap Frame - push 0 // Debug registers - push 0 // Debug registers - push 0 // Debug registers - push 0 // Debug registers - push 0 // Debug registers - push 0 // Debug registers - push 0 // Temp - push 0 // Temp - push 0 // Debug Pointer - push 0 // Debug Marker - push [eax+KTSS_EIP] // Debug EIP - push [eax+KTSS_EBP] // Debug EBP - mov ebp, esp // Set trap frame address - stdCall _KiSaveProcessorState, ebp, 0 // Save to KPRCB CONTEXT - - // - // Call Registered NMI handlers - // - stdCall _KiHandleNmi // Call NMI handlers - or al, al // Check if any handled it - jne 1f // Resume from NMI - - // - // Call the platform driver for NMI handling (panic, etc) - // Do this with IRQL at HIGH - // - push PCR[KPCR_IRQL] // Save real IRQL - mov dword ptr PCR[KPCR_IRQL], HIGH_LEVEL // Force HIGH - stdCall _HalHandleNMI, 0 // Call the HAL - pop PCR[KPCR_IRQL] // Restore real IRQL - - // - // In certain situations, nested NMIs can corrupt the TSS, making us lose - // the original context. If this happens, we have no choice but to panic. - // -1: - mov eax, PCR[KPCR_TSS] // Get current TSS - cmp word ptr [eax], KGDT_NMI_TSS // Check who its points to - je 2f // Back to the NMI TSS crash - - // - // Otherwise, recover the original state - // - add esp, KTRAP_FRAME_LENGTH // Clear the trap frame - pop PCR[KPCR_TSS] // Restore original TSS - mov ecx, PCR[KPCR_GDT] // Get GDT - lea eax, [ecx+KGDT_TSS] // Get KTSS - mov byte ptr [eax+5], 0x8B // DPL 0, Present, Busy - pushf // Get EFLAGS - or dword ptr [esp], EFLAGS_NESTED_TASK // Set nested flags - popf // Set EFLAGS - - // - // Return from NMI - // - iretd // Interrupt return - jmp _KiTrap2 // Handle recursion -2: - // - // Crash the system - // - mov eax, EXCEPTION_NMI - jmp _KiSystemFatalException + // + // Crash the system + // + mov eax, EXCEPTION_NMI // STOP fault code + jmp _KiSystemFatalException // Bugcheck helper .endfunc .func KiTrap3