mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 21:05:43 +00:00
- 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:
parent
2736e830ea
commit
4b9333f4a1
3 changed files with 168 additions and 135 deletions
|
@ -863,6 +863,10 @@ KeBugCheckWithTf(
|
||||||
PKTRAP_FRAME Tf
|
PKTRAP_FRAME Tf
|
||||||
);
|
);
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
NTAPI
|
||||||
|
KiHandleNmi(VOID);
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
KeFlushCurrentTb(VOID);
|
KeFlushCurrentTb(VOID);
|
||||||
|
|
|
@ -944,6 +944,154 @@ KiSaveProcessorState(IN PKTRAP_FRAME TrapFrame,
|
||||||
KiSaveProcessorControlState(&Prcb->ProcessorState);
|
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 **********************************************************/
|
/* PUBLIC FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -791,143 +791,24 @@ V86Int1:
|
||||||
.globl _KiTrap2
|
.globl _KiTrap2
|
||||||
.func KiTrap2
|
.func KiTrap2
|
||||||
_KiTrap2:
|
_KiTrap2:
|
||||||
//
|
//
|
||||||
// Don't allow any other NMIs to come in for now
|
// Call the C handler
|
||||||
//
|
//
|
||||||
cli // Disable interrupts
|
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
|
// Return from NMI
|
||||||
//
|
//
|
||||||
mov eax, PCR[KPCR_TSS] // Save KTSS
|
iretd // Interrupt return
|
||||||
mov ecx, PCR[KPCR_CURRENT_THREAD] // Save ETHREAD
|
jmp _KiTrap2 // Handle recursion
|
||||||
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
|
|
||||||
1:
|
1:
|
||||||
mov [eax+KTSS_LDT], cx // Saved in LDT
|
//
|
||||||
|
// Crash the system
|
||||||
//
|
//
|
||||||
// Migrate to NMI TSS
|
mov eax, EXCEPTION_NMI // STOP fault code
|
||||||
//
|
jmp _KiSystemFatalException // Bugcheck helper
|
||||||
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
|
|
||||||
.endfunc
|
.endfunc
|
||||||
|
|
||||||
.func KiTrap3
|
.func KiTrap3
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue