Trap Handlers in C Patch 8 of X:

[NTOS]: Implement KiCoprocessorError in C.
   [NTOS]: Make NMI handler fully C, now that the other parts are C too.
   [NTOS]: Delete more ASM macros and code that are now unused.

svn path=/trunk/; revision=45040
This commit is contained in:
Sir Richard 2010-01-11 06:08:11 +00:00
parent 2eab477b33
commit 2efed8ef89
5 changed files with 169 additions and 548 deletions

View file

@ -113,28 +113,6 @@
.asciz \Reason
.endm
//
// @name UNHANDLED_V86_PATH
//
// This macro prints out that the current code path is for unhandled VDM support
//
// @param None
//
// @remark None.
//
.macro UNHANDLED_V86_PATH
/* Get EIP */
call $+5
/* Print debug message */
push offset _V86UnhandledMsg
call _DbgPrint
add esp, 8
/* Loop indefinitely */
jmp $
.endm
//
// @name IDT
//
@ -276,58 +254,6 @@ _HalpHardwareInterrupt&Number:
.endr
.endm
//
// @name INVALID_V86_OPCODE
//
// This macro creates one or more entries for unhandled V86 Opcodes
// in the V86 Opcode Table.
//
// @param count.
// Number of entries to generate.
//
// @remark None.
//
.macro INVALID_V86_OPCODE count
.rept \count
.byte 0
.endr
.endm
//
// @name GENERATE_PREFIX_HANDLER
//
// This macro creates a prefix opcode handler.
//
// @param None.
//
// @remark None.
//
.macro GENERATE_PREFIX_HANDLER Name
.func Opcode&Name&PrefixV86
_Opcode&Name&PrefixV86:
or ebx, PREFIX_FLAG_&Name
jmp _OpcodeGenericPrefixV86
.endfunc
.endm
//
// @name INVALID_V86_OPCODE
//
// This macro prints out visible message and hangs the computer.
//
// @param None.
//
// @remark Temporary debugging use.
//
.macro UNHANDLED_V86_OPCODE
/* Print debug message, breakpoint and freeze */
push ecx
push offset V86DebugMsg
call _DbgPrint
add esp, 8
jmp $
.endm
//
// @name TRAP_FIXUPS
//
@ -524,96 +450,6 @@ V86_&Label:
2:
.endm
//
// @name TRAP_PROLOG
//
// This macro creates a standard trap entry prologue.
// It should be used for entry into any kernel trap (KiTrapXx), but not for
// system calls, which require special handling.
//
// @param Label
// Identifying name of the caller function; will be used to append
// to the name V86 and DR helper functions, which must already exist.
//
// @remark Use as follows:
// _KiTrap00:
// /* Push fake error code */
// push 0
//
// /* Enter common prologue */
// TRAP_PROLOG(0)
//
// /* Handle trap */
// <Your Trap Code Here>
//
.macro TRAP_PROLOG Label EndLabel
/* Just to be safe, clear out the HIWORD, since it's reserved */
mov word ptr [esp+2], 0
/* Save the non-volatiles */
push ebp
push ebx
push esi
push edi
/* Save FS and set it to PCR */
push fs
mov ebx, KGDT_R0_PCR
.byte 0x66
mov fs, bx
/* Save exception list and bogus previous mode */
push fs:[KPCR_EXCEPTION_LIST]
push -1
/* Save volatiles and segment registers */
push eax
push ecx
push edx
push ds
push es
push gs
/* Set the R3 data segment */
mov ax, KGDT_R3_DATA + RPL_MASK
/* Skip debug registers and debug stuff */
sub esp, 0x30
/* Load the segment registers */
.byte 0x66
mov ds, ax
.byte 0x66
mov es, ax
/* Check if this interrupt happened in 16-bit mode */
cmp esp, 0x10000
jb _Ki16BitStackException
/* Set up frame */
mov ebp, esp
/* Check if this was from V86 Mode */
test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
jnz V86_&Label
V86_&EndLabel:
/* Get current thread */
mov ecx, fs:[KPCR_CURRENT_THREAD]
cld
/* Flush DR7 */
and dword ptr [ebp+KTRAP_FRAME_DR7], 0
/* Check if the thread was being debugged */
test byte ptr [ecx+KTHREAD_DEBUG_ACTIVE], 0xFF
jnz Dr_&Label
/* Set the Trap Frame Debug Header */
Dr_&EndLabel:
SET_TF_DEBUG_HEADER
.endm
//
// @name INT_PROLOG
//
@ -898,164 +734,6 @@ Dr_&EndLabel:
sti
.endm
//
// @name V86_TRAP_PROLOG
//
// This macro creates a V86 Trap entry prologue.
// It should be used for entry into any fast-system call (KiGetTickCount,
// KiCallbackReturn, KiRaiseAssertion) and the generic system call handler
// (KiSystemService)
//
// @param Label
// Unique label identifying the name of the caller function; will be
// used to append to the name of the DR helper function, which must
// already exist.
//
// @remark None.
//
.macro V86_TRAP_PROLOG Label EndLabel
/* Skip everything to the error code */
sub esp, KTRAP_FRAME_ERROR_CODE
/* Clear the error code */
mov word ptr [esp+KTRAP_FRAME_ERROR_CODE+2], 0
/* Save the registers we'll trample */
mov [esp+KTRAP_FRAME_EBX], ebx
mov [esp+KTRAP_FRAME_EAX], eax
mov [esp+KTRAP_FRAME_EBP], ebp
mov [esp+KTRAP_FRAME_ESI], esi
mov [esp+KTRAP_FRAME_EDI], edi
/* Save PCR and Ring 3 segments */
mov ebx, KGDT_R0_PCR
mov eax, KGDT_R3_DATA + RPL_MASK
/* Save ECX and EDX too */
mov [esp+KTRAP_FRAME_ECX], ecx
mov [esp+KTRAP_FRAME_EDX], edx
/* Set debugging markers */
mov dword ptr [esp+KTRAP_FRAME_PREVIOUS_MODE], -1
mov dword ptr [esp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
/* Now set segments (use OVERRIDE, 0x66) */
.byte 0x66
mov fs, bx
.byte 0x66
mov ds, ax
.byte 0x66
mov es, ax
/* Set the trap frame in the stack and clear the direction flag */
mov ebp, esp
cld
/* Save the exception list */
mov eax, fs:[KPCR_EXCEPTION_LIST]
mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
/* Check if we need debugging */
mov eax, dr7
test eax, ~DR7_RESERVED_MASK
mov [esp+KTRAP_FRAME_DR7], eax
jnz Dr_&Label
Dr_&EndLabel:
.endm
//
// @name V86_TRAP_EPILOG
//
// This macro creates an epilogue for leaving V86 traps
//
// @param None.
//
// @remark None.
//
.macro V86_TRAP_EPILOG
/* Get the current thread and make it unalerted */
ExitBegin:
mov ebx, PCR[KPCR_CURRENT_THREAD]
mov byte ptr [ebx+KTHREAD_ALERTED], 0
/* Check if it has User-mode APCs pending */
cmp byte ptr [ebx+KTHREAD_PENDING_USER_APC], 0
jne PendingUserApc
/* It doesn't, pop the frame */
add esp, KTRAP_FRAME_EDX
pop edx
pop ecx
pop eax
/* Check if DR registers should be restored */
test dword ptr [ebp+KTRAP_FRAME_DR7], ~DR7_RESERVED_MASK
jnz V86DebugRestore
/* Finish popping the rest of the frame, and return to P-mode */
V86DebugContinue:
add esp, 12
pop edi
pop esi
pop ebx
pop ebp
add esp, 4
iretd
V86DebugRestore:
/* Get DR0, 1 */
xor ebx, ebx
mov esi, [ebp+KTRAP_FRAME_DR0]
mov edi, [ebp+KTRAP_FRAME_DR1]
/* Clear DR 7 */
mov dr7, ebx
/* Get DR2 and load DR0-2 */
mov ebx, [ebp+KTRAP_FRAME_DR2]
mov dr0, esi
mov dr1, edi
mov dr2, ebx
/* Get DR3-7 */
mov esi, [ebp+KTRAP_FRAME_DR0]
mov edi, [ebp+KTRAP_FRAME_DR1]
mov ebx, [ebp+KTRAP_FRAME_DR7]
/* Load them */
mov dr3, esi
mov dr6, edi
mov dr7, ebx
jmp V86DebugContinue
PendingUserApc:
/* Raise to APC level */
mov ecx, APC_LEVEL
call @KfRaiseIrql@4
/* Save KIRQL and deliver APCs */
push eax
sti
push ebp
push 0
push UserMode
call _KiDeliverApc@12
/* Restore IRQL */
pop ecx
call @KfLowerIrql@4
cli
/* Check if we're not in V86 anymore */
test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
jnz ExitBegin
.endm
//
// @name TRAP_EPILOG
//

View file

@ -1050,6 +1050,13 @@ KiSaveProcessorControlState(
OUT PKPROCESSOR_STATE ProcessorState
);
VOID
NTAPI
KiSaveProcessorState(
IN PKTRAP_FRAME TrapFrame,
IN PKEXCEPTION_FRAME ExceptionFrame
);
VOID
FASTCALL
KiRetireDpcList(

View file

@ -944,156 +944,25 @@ 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 **********************************************************/
/*
* @implemented
*/
VOID
NTAPI
KiCoprocessorError(VOID)
{
PFX_SAVE_AREA NpxArea;
/* Get the FPU area */
NpxArea = KiGetThreadNpxArea(KeGetCurrentThread());
/* Set CR0_TS */
NpxArea->Cr0NpxState = CR0_TS;
__writecr0(__readcr0() | CR0_TS);
}
/*
* @implemented
*/

View file

@ -577,29 +577,6 @@ Error:
GENERATE_TRAP_HANDLER KiTrap0, 1
GENERATE_TRAP_HANDLER KiTrap1, 1
.func KiTrap2
_KiTrap2:
//
// Call the C handler
//
stdCall _KiNmiFault, esp // Handle it in C
or al, al // Check if it got handled
jne 1f // Resume from NMI
//
// Return from NMI
//
iretd // Interrupt return
jmp _KiTrap2 // Handle recursion
1:
//
// Crash the system
//
mov eax, EXCEPTION_NMI // STOP fault code
jmp _KiSystemFatalException // Bugcheck helper
.endfunc
GENERATE_TRAP_HANDLER KiTrap3, 1
GENERATE_TRAP_HANDLER KiTrap4, 1
GENERATE_TRAP_HANDLER KiTrap5, 1
@ -617,61 +594,6 @@ GENERATE_TRAP_HANDLER KiTrap16, 1
GENERATE_TRAP_HANDLER KiTrap17, 1
GENERATE_TRAP_HANDLER KiTrap19, 1
.func KiSystemFatalException
_KiSystemFatalException:
/* Push the trap frame */
push ebp
/* Push empty parameters */
push 0
push 0
push 0
/* Push trap number and bugcheck code */
push eax
push UNEXPECTED_KERNEL_MODE_TRAP
call _KeBugCheckWithTf@24
ret
.endfunc
.func KiCoprocessorError@0
_KiCoprocessorError@0:
/* Get the NPX Thread's Initial stack */
mov eax, PCR[KPCR_NPX_THREAD]
mov eax, [eax+KTHREAD_INITIAL_STACK]
/* Make space for the FPU Save area */
sub eax, SIZEOF_FX_SAVE_AREA
/* Set the CR0 State */
mov dword ptr [eax+FN_CR0_NPX_STATE], 8
/* Update it */
mov eax, cr0
or eax, 8
mov cr0, eax
/* Return to caller */
ret
.endfunc
.func Ki16BitStackException
_Ki16BitStackException:
/* Save stack */
push ss
push esp
/* Go to kernel mode thread stack */
mov eax, PCR[KPCR_CURRENT_THREAD]
add esp, [eax+KTHREAD_INITIAL_STACK]
/* Switch to good stack segment */
UNHANDLED_PATH "16-Bit Stack"
.endfunc
/* UNEXPECTED INTERRUPT HANDLERS **********************************************/
.globl _KiStartUnexpectedRange@0

View file

@ -551,6 +551,151 @@ KiTrap1Handler(IN PKTRAP_FRAME TrapFrame)
TrapFrame);
}
VOID
KiTrap2(VOID)
{
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
//
__asm__ __volatile__ ("iret\n");
}
//
// Unhandled: crash the system
//
KiSystemFatalException(EXCEPTION_NMI, NULL);
}
VOID
FASTCALL
KiTrap3Handler(IN PKTRAP_FRAME TrapFrame)