- Implement NMI handler in C instead of ASM.

- Tested with the "nmi 0" command in QEMU and NmiDbg.sys.


svn path=/trunk/; revision=44971
This commit is contained in:
ReactOS Portable Systems Group 2010-01-06 00:40:07 +00:00
parent 2736e830ea
commit 4b9333f4a1
3 changed files with 168 additions and 135 deletions

View file

@ -863,6 +863,10 @@ KeBugCheckWithTf(
PKTRAP_FRAME Tf
);
BOOLEAN
NTAPI
KiHandleNmi(VOID);
VOID
NTAPI
KeFlushCurrentTb(VOID);

View file

@ -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 **********************************************************/
/*

View file

@ -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