mirror of
https://github.com/reactos/reactos.git
synced 2025-08-02 16:47:14 +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
|
||||
);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
KiHandleNmi(VOID);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
KeFlushCurrentTb(VOID);
|
||||
|
|
|
@ -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 **********************************************************/
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue