reactos/ntoskrnl/ke/i386/traphdlr.c

1622 lines
47 KiB
C
Raw Normal View History

Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
/*
* PROJECT: ReactOS Kernel
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: ntoskrnl/ke/i386/traphdlr.c
* PURPOSE: Kernel Trap Handlers
* PROGRAMMERS: ReactOS Portable Systems Group
*/
/* INCLUDES *******************************************************************/
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
UCHAR KiTrapPrefixTable[] =
{
0xF2, /* REP */
0xF3, /* REP INS/OUTS */
0x67, /* ADDR */
0xF0, /* LOCK */
0x66, /* OP */
0x2E, /* SEG */
0x3E, /* DS */
0x26, /* ES */
0x64, /* FS */
0x65, /* GS */
0x36, /* SS */
};
UCHAR KiTrapIoTable[] =
{
0xE4, /* IN */
0xE5, /* IN */
0xEC, /* IN */
0xED, /* IN */
0x6C, /* INS */
0x6D, /* INS */
0xE6, /* OUT */
0xE7, /* OUT */
0xEE, /* OUT */
0xEF, /* OUT */
0x6E, /* OUTS */
0x6F, /* OUTS */
};
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler;
/* TRAP EXIT CODE *************************************************************/
BOOLEAN
FORCEINLINE
KiVdmTrap(IN PKTRAP_FRAME TrapFrame)
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
{
/* Either the V8086 flag is on, or this is user-mode with a VDM */
return ((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
((KiUserTrap(TrapFrame)) && (PsGetCurrentProcess()->VdmObjects)));
}
BOOLEAN
FORCEINLINE
KiV86Trap(IN PKTRAP_FRAME TrapFrame)
{
/* Check if the V8086 flag is on */
return ((TrapFrame->EFlags & EFLAGS_V86_MASK) != 0);
}
BOOLEAN
FORCEINLINE
KiIsFrameEdited(IN PKTRAP_FRAME TrapFrame)
{
/* An edited frame changes esp. It is marked by clearing the bits
defined by FRAME_EDITED in the SegCs field of the trap frame */
return ((TrapFrame->SegCs & FRAME_EDITED) == 0);
}
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
VOID
FORCEINLINE
KiCommonExit(IN PKTRAP_FRAME TrapFrame, const ULONG Flags)
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
{
/* Disable interrupts until we return */
_disable();
/* Check for APC delivery */
KiCheckForApcDelivery(TrapFrame);
/* Debugging checks */
KiExitTrapDebugChecks(TrapFrame, Flags);
/* Restore the SEH handler chain */
KeGetPcr()->NtTib.ExceptionList = TrapFrame->ExceptionList;
/* Check if there are active debug registers */
if (__builtin_expect(TrapFrame->Dr7 & ~DR7_RESERVED_MASK, 0))
{
/* Not handled yet */
DbgPrint("Need Hardware Breakpoint Support!\n");
DbgBreakPoint();
while (TRUE);
}
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiEoiHelper(IN PKTRAP_FRAME TrapFrame)
{
/* Common trap exit code */
KiCommonExit(TrapFrame, 0);
/* Check if this was a V8086 trap */
if (TrapFrame->EFlags & EFLAGS_V86_MASK) KiTrapReturnNoSegments(TrapFrame);
/* Check for user mode exit */
if (TrapFrame->SegCs & MODE_MASK) KiTrapReturn(TrapFrame);
/* Check for edited frame */
if (KiIsFrameEdited(TrapFrame)) KiEditedTrapReturn(TrapFrame);
/* Exit the trap to kernel mode */
KiTrapReturnNoSegments(TrapFrame);
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiServiceExit(IN PKTRAP_FRAME TrapFrame,
IN NTSTATUS Status)
{
ASSERT((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0);
ASSERT(!KiIsFrameEdited(TrapFrame));
/* Copy the status into EAX */
TrapFrame->Eax = Status;
/* Common trap exit code */
KiCommonExit(TrapFrame, 0);
/* Restore previous mode */
KeGetCurrentThread()->PreviousMode = TrapFrame->PreviousPreviousMode;
/* Check for user mode exit */
if (TrapFrame->SegCs & MODE_MASK)
{
/* Check if we were single stepping */
if (TrapFrame->EFlags & EFLAGS_TF)
{
/* Must use the IRET handler */
KiSystemCallTrapReturn(TrapFrame);
}
else
{
/* We can use the sysexit handler */
KiFastCallExitHandler(TrapFrame);
}
}
/* Exit to kernel mode */
KiSystemCallReturn(TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiServiceExit2(IN PKTRAP_FRAME TrapFrame)
{
/* Common trap exit code */
KiCommonExit(TrapFrame, 0);
/* Restore previous mode */
KeGetCurrentThread()->PreviousMode = TrapFrame->PreviousPreviousMode;
/* Check if this was a V8086 trap */
if (TrapFrame->EFlags & EFLAGS_V86_MASK) KiTrapReturnNoSegments(TrapFrame);
/* Check for user mode exit */
if (TrapFrame->SegCs & MODE_MASK) KiTrapReturn(TrapFrame);
/* Check for edited frame */
if (KiIsFrameEdited(TrapFrame)) KiEditedTrapReturn(TrapFrame);
/* Exit the trap to kernel mode */
KiTrapReturnNoSegments(TrapFrame);
}
/* TRAP HANDLERS **************************************************************/
VOID
FASTCALL
DECLSPEC_NORETURN
KiDebugHandler(IN PKTRAP_FRAME TrapFrame,
IN ULONG Parameter1,
IN ULONG Parameter2,
IN ULONG Parameter3)
{
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Enable interrupts if the trap came from user-mode */
if (KiUserTrap(TrapFrame)) _enable();
/* Dispatch the exception */
KiDispatchExceptionFromTrapFrame(STATUS_BREAKPOINT,
TrapFrame->Eip - 1,
3,
Parameter1,
Parameter2,
Parameter3,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiNpxHandler(IN PKTRAP_FRAME TrapFrame,
IN PKTHREAD Thread,
IN PFX_SAVE_AREA SaveArea)
{
ULONG Cr0, Mask, Error, ErrorOffset, DataOffset;
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Check for kernel trap */
if (!KiUserTrap(TrapFrame))
{
/* Kernel might've tripped a delayed error */
SaveArea->Cr0NpxState |= CR0_TS;
/* Only valid if it happened during a restore */
//if ((PVOID)TrapFrame->Eip == FrRestore)
{
/* It did, so just skip the instruction */
//TrapFrame->Eip += 3; /* sizeof(FRSTOR) */
//KiEoiHelper(TrapFrame);
}
}
/* User or kernel trap -- get ready to issue an exception */
//if (Thread->NpxState == NPX_STATE_NOT_LOADED)
{
/* Update CR0 */
Cr0 = __readcr0();
Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
__writecr0(Cr0);
/* Save FPU state */
Ke386SaveFpuState(SaveArea);
/* Mark CR0 state dirty */
Cr0 |= NPX_STATE_NOT_LOADED;
Cr0 |= SaveArea->Cr0NpxState;
__writecr0(Cr0);
/* Update NPX state */
Thread->NpxState = NPX_STATE_NOT_LOADED;
KeGetCurrentPrcb()->NpxThread = NULL;
}
/* Clear the TS bit and re-enable interrupts */
SaveArea->Cr0NpxState &= ~CR0_TS;
_enable();
/* Check if we should get the FN or FX error */
if (KeI386FxsrPresent)
{
/* Get it from FX */
Mask = SaveArea->U.FxArea.ControlWord;
Error = SaveArea->U.FxArea.StatusWord;
/* Get the FPU exception address too */
ErrorOffset = SaveArea->U.FxArea.ErrorOffset;
DataOffset = SaveArea->U.FxArea.DataOffset;
}
else
{
/* Get it from FN */
Mask = SaveArea->U.FnArea.ControlWord;
Error = SaveArea->U.FnArea.StatusWord;
/* Get the FPU exception address too */
ErrorOffset = SaveArea->U.FnArea.ErrorOffset;
DataOffset = SaveArea->U.FnArea.DataOffset;
}
/* Get legal exceptions that software should handle */
Error &= (FSW_INVALID_OPERATION |
FSW_DENORMAL |
FSW_ZERO_DIVIDE |
FSW_OVERFLOW |
FSW_UNDERFLOW |
FSW_PRECISION);
Error &= ~Mask;
if (Error & FSW_STACK_FAULT)
{
/* Issue stack check fault */
KiDispatchException2Args(STATUS_FLOAT_STACK_CHECK,
ErrorOffset,
0,
DataOffset,
TrapFrame);
}
/* Check for invalid operation */
if (Error & FSW_INVALID_OPERATION)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION,
ErrorOffset,
0,
TrapFrame);
}
/* Check for divide by zero */
if (Error & FSW_ZERO_DIVIDE)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_DIVIDE_BY_ZERO,
ErrorOffset,
0,
TrapFrame);
}
/* Check for denormal */
if (Error & FSW_DENORMAL)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION,
ErrorOffset,
0,
TrapFrame);
}
/* Check for overflow */
if (Error & FSW_OVERFLOW)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_OVERFLOW,
ErrorOffset,
0,
TrapFrame);
}
/* Check for underflow */
if (Error & FSW_UNDERFLOW)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_UNDERFLOW,
ErrorOffset,
0,
TrapFrame);
}
/* Check for precision fault */
if (Error & FSW_PRECISION)
{
/* Issue fault */
KiDispatchException1Args(STATUS_FLOAT_INEXACT_RESULT,
ErrorOffset,
0,
TrapFrame);
}
/* Unknown FPU fault */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 1, Error, 0, 0, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap00Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Enable interrupts */
_enable();
/* Dispatch the exception */
KiDispatchException0Args(STATUS_INTEGER_DIVIDE_BY_ZERO,
TrapFrame->Eip,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap01Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Enable interrupts if the trap came from user-mode */
if (KiUserTrap(TrapFrame)) _enable();
/* Mask out trap flag and dispatch the exception */
TrapFrame->EFlags &= ~EFLAGS_TF;
KiDispatchException0Args(STATUS_SINGLE_STEP,
TrapFrame->Eip,
TrapFrame);
}
VOID
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap02(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->NtTib.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
//
KiIret();
}
//
// Unhandled: crash the system
//
KiSystemFatalException(EXCEPTION_NMI, NULL);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap03Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Continue with the common handler */
KiDebugHandler(TrapFrame, BREAKPOINT_BREAK, 0, 0);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap04Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Enable interrupts */
_enable();
/* Dispatch the exception */
KiDispatchException0Args(STATUS_INTEGER_OVERFLOW,
TrapFrame->Eip - 1,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap05Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Check for kernel-mode fault */
if (!KiUserTrap(TrapFrame)) KiSystemFatalException(EXCEPTION_BOUND_CHECK, TrapFrame);
/* Enable interrupts */
_enable();
/* Dispatch the exception */
KiDispatchException0Args(STATUS_ARRAY_BOUNDS_EXCEEDED,
TrapFrame->Eip,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiTrap06Handler(IN PKTRAP_FRAME TrapFrame)
{
PUCHAR Instruction;
ULONG i;
KIRQL OldIrql;
/* Check for V86 GPF */
if (__builtin_expect(KiV86Trap(TrapFrame), 1))
{
/* Enter V86 trap */
KiEnterV86Trap(TrapFrame);
/* Must be a VDM process */
if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects, 0))
{
/* Enable interrupts */
_enable();
/* Setup illegal instruction fault */
KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
TrapFrame->Eip,
TrapFrame);
}
/* Go to APC level */
OldIrql = KfRaiseIrql(APC_LEVEL);
_enable();
/* Check for BOP */
if (!VdmDispatchBop(TrapFrame))
{
/* Should only happen in VDM mode */
UNIMPLEMENTED;
while (TRUE);
}
/* Bring IRQL back */
KfLowerIrql(OldIrql);
_disable();
/* Do a quick V86 exit if possible */
KiExitV86Trap(TrapFrame);
}
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Enable interrupts */
Instruction = (PUCHAR)TrapFrame->Eip;
_enable();
/* Check for user trap */
if (KiUserTrap(TrapFrame))
{
/* FIXME: Use SEH */
/* Scan next 4 opcodes */
for (i = 0; i < 4; i++)
{
/* Check for LOCK instruction */
if (Instruction[i] == 0xF0)
{
/* Send invalid lock sequence exception */
KiDispatchException0Args(STATUS_INVALID_LOCK_SEQUENCE,
TrapFrame->Eip,
TrapFrame);
}
}
/* FIXME: SEH ends here */
}
/* Kernel-mode or user-mode fault (but not LOCK) */
KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
TrapFrame->Eip,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap07Handler(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread, NpxThread;
PFX_SAVE_AREA SaveArea, NpxSaveArea;
ULONG Cr0;
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Try to handle NPX delay load */
while (TRUE)
{
/* Get the current thread */
Thread = KeGetCurrentThread();
/* Get the NPX frame */
SaveArea = KiGetThreadNpxArea(Thread);
/* Check if emulation is enabled */
if (SaveArea->Cr0NpxState & CR0_EM)
{
/* Not implemented */
UNIMPLEMENTED;
while (TRUE);
}
/* Save CR0 and check NPX state */
Cr0 = __readcr0();
if (Thread->NpxState != NPX_STATE_LOADED)
{
/* Update CR0 */
Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
__writecr0(Cr0);
/* Get the NPX thread */
NpxThread = KeGetCurrentPrcb()->NpxThread;
if (NpxThread)
{
/* Get the NPX frame */
NpxSaveArea = KiGetThreadNpxArea(NpxThread);
/* Save FPU state */
//Ke386SaveFpuState(NpxSaveArea);
/* Update NPX state */
Thread->NpxState = NPX_STATE_NOT_LOADED;
}
/* Load FPU state */
//Ke386LoadFpuState(SaveArea);
/* Update NPX state */
Thread->NpxState = NPX_STATE_LOADED;
KeGetCurrentPrcb()->NpxThread = Thread;
/* Enable interrupts */
_enable();
/* Check if CR0 needs to be reloaded due to context switch */
if (!SaveArea->Cr0NpxState) KiEoiHelper(TrapFrame);
/* Otherwise, we need to reload CR0, disable interrupts */
_disable();
/* Reload CR0 */
Cr0 = __readcr0();
Cr0 |= SaveArea->Cr0NpxState;
__writecr0(Cr0);
/* Now restore interrupts and check for TS */
_enable();
if (Cr0 & CR0_TS) KiEoiHelper(TrapFrame);
/* We're still here -- clear TS and try again */
__writecr0(__readcr0() &~ CR0_TS);
_disable();
}
else
{
/* This is an actual fault, not a lack of FPU state */
break;
}
}
/* TS should not be set */
if (Cr0 & CR0_TS)
{
/*
* If it's incorrectly set, then maybe the state is actually still valid
* but we could've lock track of that due to a BIOS call.
* Make sure MP is still set, which should verify the theory.
*/
if (Cr0 & CR0_MP)
{
/* Indeed, the state is actually still valid, so clear TS */
__writecr0(__readcr0() &~ CR0_TS);
KiEoiHelper(TrapFrame);
}
/* Otherwise, something strange is going on */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 2, Cr0, 0, 0, TrapFrame);
}
/* It's not a delayed load, so process this trap as an NPX fault */
KiNpxHandler(TrapFrame, Thread, SaveArea);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap08Handler(IN PKTRAP_FRAME TrapFrame)
{
/* FIXME: Not handled */
KiSystemFatalException(EXCEPTION_DOUBLE_FAULT, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap09Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Enable interrupts and kill the system */
_enable();
KiSystemFatalException(EXCEPTION_NPX_OVERRUN, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap0AHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Kill the system */
KiSystemFatalException(EXCEPTION_INVALID_TSS, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap0BHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* FIXME: Kill the system */
UNIMPLEMENTED;
KiSystemFatalException(EXCEPTION_SEGMENT_NOT_PRESENT, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap0CHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* FIXME: Kill the system */
UNIMPLEMENTED;
KiSystemFatalException(EXCEPTION_STACK_FAULT, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
{
ULONG i, j, Iopl;
BOOLEAN Privileged = FALSE;
PUCHAR Instructions;
UCHAR Instruction = 0;
KIRQL OldIrql;
/* Check for V86 GPF */
if (__builtin_expect(KiV86Trap(TrapFrame), 1))
{
/* Enter V86 trap */
KiEnterV86Trap(TrapFrame);
/* Must be a VDM process */
if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects, 0))
{
/* Enable interrupts */
_enable();
/* Setup illegal instruction fault */
KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
TrapFrame->Eip,
TrapFrame);
}
/* Go to APC level */
OldIrql = KfRaiseIrql(APC_LEVEL);
_enable();
/* Handle the V86 opcode */
if (__builtin_expect(Ki386HandleOpcodeV86(TrapFrame) == 0xFF, 0))
{
/* Should only happen in VDM mode */
UNIMPLEMENTED;
while (TRUE);
}
/* Bring IRQL back */
KfLowerIrql(OldIrql);
_disable();
/* Do a quick V86 exit if possible */
KiExitV86Trap(TrapFrame);
}
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check for user-mode GPF */
if (KiUserTrap(TrapFrame))
{
/* Should not be VDM */
ASSERT(KiVdmTrap(TrapFrame) == FALSE);
/* Enable interrupts and check error code */
_enable();
if (!TrapFrame->ErrCode)
{
/* FIXME: Use SEH */
Instructions = (PUCHAR)TrapFrame->Eip;
/* Scan next 15 bytes */
for (i = 0; i < 15; i++)
{
/* Skip prefix instructions */
for (j = 0; j < sizeof(KiTrapPrefixTable); j++)
{
/* Is this a prefix instruction? */
if (Instructions[i] == KiTrapPrefixTable[j])
{
/* Stop looking */
break;
}
}
/* Is this NOT any prefix instruction? */
if (j == sizeof(KiTrapPrefixTable))
{
/* We can go ahead and handle the fault now */
Instruction = Instructions[i];
break;
}
}
/* If all we found was prefixes, then this instruction is too long */
if (i == 15)
{
/* Setup illegal instruction fault */
KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION,
TrapFrame->Eip,
TrapFrame);
}
/* Check for privileged instructions */
if (Instruction == 0xF4) // HLT
{
/* HLT is privileged */
Privileged = TRUE;
}
else if (Instruction == 0x0F)
{
/* Test if it's any of the privileged two-byte opcodes */
if (((Instructions[i + 1] == 0x00) && // LLDT or LTR
(((Instructions[i + 2] & 0x38) == 0x10) || // LLDT
(Instructions[i + 2] == 0x18))) || // LTR
((Instructions[i + 1] == 0x01) && // LGDT or LIDT or LMSW
(((Instructions[i + 2] & 0x38) == 0x10) || // LGDT
(Instructions[i + 2] == 0x18) || // LIDT
(Instructions[i + 2] == 0x30))) || // LMSW
(Instructions[i + 1] == 0x08) || // INVD
(Instructions[i + 1] == 0x09) || // WBINVD
(Instructions[i + 1] == 0x35) || // SYSEXIT
(Instructions[i + 1] == 0x26) || // MOV DR, XXX
(Instructions[i + 1] == 0x06) || // CLTS
(Instructions[i + 1] == 0x20) || // MOV CR, XXX
(Instructions[i + 1] == 0x24) || // MOV YYY, DR
(Instructions[i + 1] == 0x30) || // WRMSR
(Instructions[i + 1] == 0x33)) // RDPMC
// INVLPG, INVLPGA, SYSRET
{
/* These are all privileged */
Privileged = TRUE;
}
}
else
{
/* Get the IOPL and compare with the RPL mask */
Iopl = (TrapFrame->EFlags & EFLAGS_IOPL) >> 12;
if ((TrapFrame->SegCs & RPL_MASK) > Iopl)
{
/* I/O privilege error -- check for known instructions */
if ((Instruction == 0xFA) || (Instruction == 0xFB)) // CLI or STI
{
/* These are privileged */
Privileged = TRUE;
}
else
{
/* Last hope: an IN/OUT instruction */
for (j = 0; j < sizeof(KiTrapIoTable); j++)
{
/* Is this an I/O instruction? */
if (Instruction == KiTrapIoTable[j])
{
/* Then it's privileged */
Privileged = TRUE;
break;
}
}
}
}
}
/* So now... was the instruction privileged or not? */
if (Privileged)
{
/* Whew! We have a privileged instruction, so dispatch the fault */
KiDispatchException0Args(STATUS_PRIVILEGED_INSTRUCTION,
TrapFrame->Eip,
TrapFrame);
}
}
/* If we got here, send an access violation */
KiDispatchException2Args(STATUS_ACCESS_VIOLATION,
TrapFrame->Eip,
0,
0xFFFFFFFF,
TrapFrame);
}
/*
* Check for a fault during checking of the user instruction.
*
* Note that the SEH handler will catch invalid EIP, but we could be dealing
* with an invalid CS, which will generate another GPF instead.
*
*/
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
if (((PVOID)TrapFrame->Eip >= (PVOID)KiTrap0DHandler) &&
((PVOID)TrapFrame->Eip < (PVOID)KiTrap0DHandler))
{
/* Not implemented */
UNIMPLEMENTED;
while (TRUE);
}
/*
* NOTE: The ASM trap exit code would restore segment registers by doing
* a POP <SEG>, which could cause an invalid segment if someone had messed
* with the segment values.
*
* Another case is a bogus SS, which would hit a GPF when doing the iret.
* This could only be done through a buggy or malicious driver, or perhaps
* the kernel debugger.
*
* The kernel normally restores the "true" segment if this happens.
*
* However, since we're restoring in C, not ASM, we can't detect
* POP <SEG> since the actual instructions will be different.
*
* A better technique would be to check the EIP and somehow edit the
* trap frame before restarting the instruction -- but we would need to
* know the extract instruction that was used first.
*
* We could force a special instrinsic to use stack instructions, or write
* a simple instruction length checker.
*
* Nevertheless, this is a lot of work for the purpose of avoiding a crash
* when the user is purposedly trying to create one from kernel-mode, so
* we should probably table this for now since it's not a "real" issue.
*/
/*
* NOTE2: Another scenario is the IRET during a V8086 restore (BIOS Call)
* which will cause a GPF since the trap frame is a total mess (on purpose)
* as built in KiEnterV86Mode.
*
* The idea is to scan for IRET, scan for the known EIP adress, validate CS
* and then manually issue a jump to the V8086 return EIP.
*/
Instructions = (PUCHAR)TrapFrame->Eip;
if (Instructions[0] == 0xCF)
{
/*
* Some evil shit is going on here -- this is not the SS:ESP you're
* looking for! Instead, this is actually CS:EIP you're looking at!
* Why? Because part of the trap frame actually corresponds to the IRET
* stack during the trap exit!
*/
if ((TrapFrame->HardwareEsp == (ULONG)Ki386BiosCallReturnAddress) &&
(TrapFrame->HardwareSegSs == (KGDT_R0_CODE | RPL_MASK)))
{
/* Exit the V86 trap! */
Ki386BiosCallReturnAddress(TrapFrame);
}
else
{
/* Otherwise, this is another kind of IRET fault */
UNIMPLEMENTED;
while (TRUE);
}
}
/* So since we're not dealing with the above case, check for RDMSR/WRMSR */
if ((Instructions[0] == 0xF) && // 2-byte opcode
(((Instructions[1] >> 8) == 0x30) || // RDMSR
((Instructions[2] >> 8) == 0x32))) // WRMSR
{
/* Unknown CPU MSR, so raise an access violation */
KiDispatchException0Args(STATUS_ACCESS_VIOLATION,
TrapFrame->Eip,
TrapFrame);
}
/* Check for lazy segment load */
if (TrapFrame->SegDs != (KGDT_R3_DATA | RPL_MASK))
{
/* Fix it */
TrapFrame->SegDs = (KGDT_R3_DATA | RPL_MASK);
}
else if (TrapFrame->SegEs != (KGDT_R3_DATA | RPL_MASK))
{
/* Fix it */
TrapFrame->SegEs = (KGDT_R3_DATA | RPL_MASK);
}
else
{
/* Whatever it is, we can't handle it */
KiSystemFatalException(EXCEPTION_GP_FAULT, TrapFrame);
}
/* Return to where we came from */
KiTrapReturn(TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread;
ULONG_PTR Cr2;
NTSTATUS Status;
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check if this is the base frame */
Thread = KeGetCurrentThread();
if (KeGetTrapFrame(Thread) != TrapFrame)
{
/* It isn't, check if this is a second nested frame */
if (((ULONG_PTR)KeGetTrapFrame(Thread) - (ULONG_PTR)TrapFrame) <=
FIELD_OFFSET(KTRAP_FRAME, EFlags))
{
/* The stack is somewhere in between frames, we need to fix it */
UNIMPLEMENTED;
while (TRUE);
}
}
/* Save CR2 */
Cr2 = __readcr2();
/* Check for Pentium LOCK errata */
if (KiI386PentiumLockErrataPresent)
{
/* Not yet implemented */
UNIMPLEMENTED;
while (TRUE);
}
/* HACK: Check if interrupts are disabled and enable them */
if (!(TrapFrame->EFlags & EFLAGS_INTERRUPT_MASK))
{
/* Enable interupts */
_enable();
#ifdef HACK_ABOVE_FIXED
if (!(TrapFrame->EFlags & EFLAGS_INTERRUPT_MASK))
{
/* This is illegal */
KeBugCheckWithTf(IRQL_NOT_LESS_OR_EQUAL,
Cr2,
-1,
TrapFrame->ErrCode & 1,
TrapFrame->Eip,
TrapFrame);
}
#endif
}
/* Call the access fault handler */
Status = MmAccessFault(TrapFrame->ErrCode & 1,
(PVOID)Cr2,
TrapFrame->SegCs & MODE_MASK,
TrapFrame);
if (NT_SUCCESS(Status)) KiEoiHelper(TrapFrame);
/* Check for S-LIST fault */
if (TrapFrame->Eip == (ULONG_PTR)ExpInterlockedPopEntrySListFault)
{
/* Not yet implemented */
UNIMPLEMENTED;
while (TRUE);
}
/* Check for syscall fault */
#if 0
if ((TrapFrame->Eip == (ULONG_PTR)CopyParams) ||
(TrapFrame->Eip == (ULONG_PTR)ReadBatch))
{
/* Not yet implemented */
UNIMPLEMENTED;
while (TRUE);
}
#endif
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Either kernel or user trap (non VDM) so dispatch exception */
if (Status == STATUS_ACCESS_VIOLATION)
{
/* This status code is repurposed so we can recognize it later */
KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION,
TrapFrame->Eip,
TrapFrame->ErrCode & 1,
Cr2,
TrapFrame);
}
else if ((Status == STATUS_GUARD_PAGE_VIOLATION) ||
(Status == STATUS_STACK_OVERFLOW))
{
/* These faults only have two parameters */
KiDispatchException2Args(Status,
TrapFrame->Eip,
TrapFrame->ErrCode & 1,
Cr2,
TrapFrame);
}
/* Only other choice is an in-page error, with 3 parameters */
KiDispatchExceptionFromTrapFrame(STATUS_IN_PAGE_ERROR,
TrapFrame->Eip,
3,
TrapFrame->ErrCode & 1,
Cr2,
Status,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiTrap0FHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* FIXME: Kill the system */
UNIMPLEMENTED;
KiSystemFatalException(EXCEPTION_RESERVED_TRAP, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap10Handler(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread;
PFX_SAVE_AREA SaveArea;
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check if this is the NPX thrad */
Thread = KeGetCurrentThread();
SaveArea = KiGetThreadNpxArea(Thread);
if (Thread != KeGetCurrentPrcb()->NpxThread)
{
/* It isn't, enable interrupts and set delayed error */
_enable();
SaveArea->Cr0NpxState |= CR0_TS;
/* End trap */
KiEoiHelper(TrapFrame);
}
/* Otherwise, proceed with NPX fault handling */
KiNpxHandler(TrapFrame, Thread, SaveArea);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap11Handler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Enable interrupts and kill the system */
_enable();
KiSystemFatalException(EXCEPTION_ALIGNMENT_CHECK, TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
Patch that fixes VMWare boot (and should fix QEMu/KVM boot on the testbot): [NTOS]: A trap can get us into a state where DS/ES are invalid, making any pointer dereference (on DS/ES segmented memory, not SS, the stack) crash (and probably double-fault). Therefore, we have to be careful to switch to a good DS/ES before touching the TrapFrame pointer, which we don't have in ESP like the ASM code, but in a DS/ES-segmented register. For V8086 traps we can switch to the good DS/ES immediately, but for other kinds of traps, we actually need to save the current (bad) segments first. So we save them on the stack now, then switch to the good ones, then store the stack values into the trap frame. This is what happens on a non-optimized (-O0) build. On an optimized build, the segments will end up in registers instead, which is fine too (they'll be direct values). The order of instructions is guaranteed since the segment macros are volatile. [NTOS]: The GPF and Invalid Opcode handlers are performance critical when talking about V8086 traps, because they control the main flow of execution during that mode (GPFs will be issued for any privileged instruction we need to emulate, and invalid opcode might be generated for BOPs). Because of this, we employ a fast entry/exit macro into V8086 mode since we can make certain assumptions. We detect, and use, such scenarios when the V8086 flag is enabled in EFLAGS. However, because we can land in a GPF handler with an invalid DS/ES, as some V8086 code could trample this during BIOS calls for example, we must make sure that we are on a valid DS/ES before dereferencing any pointer. We fixup DS/ES either in KiEnterTrap (for normal entry/exit) or, for V86, in KiEnterV86Trap. Notice the problem: we need to detect which of these to use early on but we can't touch the EFLAGS in the frame because DS/ES could be invalid. Thankfully SS is always guaranteed valid, so stack dereferences are game! We therefore read the EFLAGS here, in assembly, where we can touch ESP as we please. We save this in EDX, which will be used as the second argument for the FASTCALL C trap entry. When we make the fast V86 check, we use the parameter instead of the trap frame, leading us to using the correct trap entry function, which fixes up DS/ES and lets us go on our merry way... [NTOS]: Make appropriate changes to GENERATE_TRAP_HANDLERS macro. [NTOS]: Switch to using well-known NT trap handler names (hex-based, double-zeroed) instead of decimal-based trap handler names which are confusing. [NTOS]: Clean up some debug spew. svn path=/trunk/; revision=45052
2010-01-12 05:50:45 +00:00
KiTrap13Handler(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread;
PFX_SAVE_AREA SaveArea;
ULONG Cr0, MxCsrMask, Error;
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Check if this is the NPX thrad */
Thread = KeGetCurrentThread();
if (Thread != KeGetCurrentPrcb()->NpxThread)
{
/* It isn't, kill the system */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, (ULONG_PTR)Thread, 0, 0, TrapFrame);
}
/* Get the NPX frame */
SaveArea = KiGetThreadNpxArea(Thread);
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Check for user trap */
if (!KiUserTrap(TrapFrame))
{
/* Kernel should not fault on XMMI */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 2, TrapFrame);
}
/* Update CR0 */
Cr0 = __readcr0();
Cr0 &= ~(CR0_MP | CR0_EM | CR0_TS);
__writecr0(Cr0);
/* Save FPU state */
Ke386SaveFpuState(SaveArea);
/* Mark CR0 state dirty */
Cr0 |= NPX_STATE_NOT_LOADED;
Cr0 |= SaveArea->Cr0NpxState;
__writecr0(Cr0);
/* Update NPX state */
Thread->NpxState = NPX_STATE_NOT_LOADED;
KeGetCurrentPrcb()->NpxThread = NULL;
/* Clear the TS bit and re-enable interrupts */
SaveArea->Cr0NpxState &= ~CR0_TS;
_enable();
/* Now look at MxCsr to get the mask of errors we should care about */
MxCsrMask = ~((USHORT)SaveArea->U.FxArea.MXCsr >> 7);
/* Get legal exceptions that software should handle */
Error = (USHORT)SaveArea->U.FxArea.MXCsr & (FSW_INVALID_OPERATION |
FSW_DENORMAL |
FSW_ZERO_DIVIDE |
FSW_OVERFLOW |
FSW_UNDERFLOW |
FSW_PRECISION);
Error &= MxCsrMask;
/* Now handle any of those legal errors */
if (Error & (FSW_INVALID_OPERATION |
FSW_DENORMAL |
FSW_ZERO_DIVIDE |
FSW_OVERFLOW |
FSW_UNDERFLOW |
FSW_PRECISION))
{
/* By issuing an exception */
KiDispatchException1Args(STATUS_FLOAT_MULTIPLE_TRAPS,
TrapFrame->Eip,
0,
TrapFrame);
}
/* Unknown XMMI fault */
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
}
/* SOFTWARE SERVICES **********************************************************/
VOID
FASTCALL
KiGetTickCountHandler(IN PKTRAP_FRAME TrapFrame)
{
UNIMPLEMENTED;
while (TRUE);
}
VOID
FASTCALL
KiCallbackReturnHandler(IN PKTRAP_FRAME TrapFrame)
{
UNIMPLEMENTED;
while (TRUE);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiRaiseAssertionHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Decrement EIP to point to the INT2C instruction (2 bytes, not 1 like INT3) */
TrapFrame->Eip -= 2;
/* Dispatch the exception */
KiDispatchException0Args(STATUS_ASSERTION_FAILURE,
TrapFrame->Eip,
TrapFrame);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame)
{
/* Save trap frame */
KiEnterTrap(TrapFrame);
/* Increment EIP to skip the INT3 instruction */
TrapFrame->Eip++;
/* Continue with the common handler */
KiDebugHandler(TrapFrame, TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
}
VOID
FORCEINLINE
DECLSPEC_NORETURN
KiSystemCall(IN PKTRAP_FRAME TrapFrame,
IN PVOID Arguments)
{
PKTHREAD Thread;
PKSERVICE_TABLE_DESCRIPTOR DescriptorTable;
ULONG Id, Offset, StackBytes, Result;
PVOID Handler;
ULONG SystemCallNumber = TrapFrame->Eax;
/* Get the current thread */
Thread = KeGetCurrentThread();
/* Set debug header */
KiFillTrapFrameDebug(TrapFrame);
/* Chain trap frames */
TrapFrame->Edx = (ULONG_PTR)Thread->TrapFrame;
/* No error code */
TrapFrame->ErrCode = 0;
/* Save previous mode */
TrapFrame->PreviousPreviousMode = Thread->PreviousMode;
/* Save the SEH chain and terminate it for now */
TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
/* Clear DR7 and check for debugging */
TrapFrame->Dr7 = 0;
if (__builtin_expect(Thread->DispatcherHeader.DebugActive & 0xFF, 0))
{
UNIMPLEMENTED;
while (TRUE);
}
/* Set thread fields */
Thread->TrapFrame = TrapFrame;
Thread->PreviousMode = KiUserTrap(TrapFrame);
/* Enable interrupts */
_enable();
/* Decode the system call number */
Offset = (SystemCallNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK;
Id = SystemCallNumber & SERVICE_NUMBER_MASK;
/* Get descriptor table */
DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);
/* Validate the system call number */
if (__builtin_expect(Id >= DescriptorTable->Limit, 0))
{
/* Check if this is a GUI call */
if (!(Offset & SERVICE_TABLE_TEST))
{
/* Fail the call */
Result = STATUS_INVALID_SYSTEM_SERVICE;
goto ExitCall;
}
/* Convert us to a GUI thread -- must wrap in ASM to get new EBP */
Result = KiConvertToGuiThread();
if (!NT_SUCCESS(Result))
{
/* Set the last error and fail */
//SetLastWin32Error(RtlNtStatusToDosError(Result));
goto ExitCall;
}
/* Reload trap frame and descriptor table pointer from new stack */
TrapFrame = *(volatile PVOID*)&Thread->TrapFrame;
DescriptorTable = (PVOID)(*(volatile ULONG_PTR*)&Thread->ServiceTable + Offset);
/* Validate the system call number again */
if (Id >= DescriptorTable->Limit)
{
/* Fail the call */
Result = STATUS_INVALID_SYSTEM_SERVICE;
goto ExitCall;
}
}
/* Check if this is a GUI call */
if (__builtin_expect(Offset & SERVICE_TABLE_TEST, 0))
{
/* Get the batch count and flush if necessary */
if (NtCurrentTeb()->GdiBatchCount) KeGdiFlushUserBatch();
}
/* Increase system call count */
KeGetCurrentPrcb()->KeSystemCalls++;
/* FIXME: Increase individual counts on debug systems */
//KiIncreaseSystemCallCount(DescriptorTable, Id);
/* Get stack bytes */
StackBytes = DescriptorTable->Number[Id];
/* Probe caller stack */
if (__builtin_expect((Arguments < (PVOID)MmUserProbeAddress) && !(KiUserTrap(TrapFrame)), 0))
{
/* Access violation */
UNIMPLEMENTED;
while (TRUE);
}
/* Get the handler and make the system call */
Handler = (PVOID)DescriptorTable->Base[Id];
Result = KiSystemCallTrampoline(Handler, Arguments, StackBytes);
/* Make sure we're exiting correctly */
KiExitSystemCallDebugChecks(Id, TrapFrame);
/* Restore the old trap frame */
ExitCall:
Thread->TrapFrame = (PKTRAP_FRAME)TrapFrame->Edx;
/* Exit from system call */
KiServiceExit(TrapFrame, Result);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiSystemServiceHandler(IN PKTRAP_FRAME TrapFrame,
IN PVOID Arguments)
{
/* Call the shared handler (inline) */
KiSystemCall(TrapFrame, Arguments);
}
VOID
FASTCALL
DECLSPEC_NORETURN
KiFastCallEntryHandler(IN PKTRAP_FRAME TrapFrame,
IN PVOID Arguments)
{
/* Set up a fake INT Stack and enable interrupts */
TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
TrapFrame->HardwareEsp = (ULONG_PTR)Arguments;
TrapFrame->EFlags = __readeflags() | EFLAGS_INTERRUPT_MASK;
TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
TrapFrame->Eip = SharedUserData->SystemCallReturn;
TrapFrame->SegFs = KGDT_R3_TEB | RPL_MASK;
__writeeflags(0x2);
/* Arguments are actually 2 frames down (because of the double indirection) */
Arguments = (PVOID)(TrapFrame->HardwareEsp + 8);
/* Call the shared handler (inline) */
KiSystemCall(TrapFrame, Arguments);
}
/*
* @implemented
*/
VOID
NTAPI
Kei386EoiHelper(VOID)
{
/* We should never see this call happening */
DPRINT1("Mismatched NT/HAL version");
while (TRUE);
}
Trap Handlers in C Patch 1 of X (Patch by Sir_Richard <ros.arm@reactos.org>): [NTOS]: The kernel normally does not save FPU state during Ring 0 transitions since the FPU should not be used. The one exception is when a kernel debugger is attached. Unfortunately, the latter check in ReactOS results in even "print on the serial line" to count as "debugger attached", and thus FPU state was almost always saved, slowing down traps significantly. [NTOS]: The kernel also does not typically save DRx (debug) registers unless they were in use. During an exception dispatch, they are zeroed out, and later during trap exit, if any debug register is set, DR7 is updated to enable that hardware breakpoint. Unfortunately, the code to clear the debug registers had a bug: DR2 was never cleared. Because DR2 ended up being a random stack value during trap frame generation, this caused a bogus address to be added to DR2, and DR7 would then enable the 2nd hardware breakpoint. This caused the kernel to always save DRx state, which is slow, and worse, could cause random hardware breakpoints to fire. [NTOS]: Start implementing trap handling in C. ASM trap handlers will now only be 5 lines of assembly including a function call to a C handler. All C handling code uses maximum two arguments and is all FASTCALL for efficiency. [NTOS]: Implement C versions of TRAP_PROLOG and TRAP_EPILOG. Implement C version of Ki386EoiHelper. Implement C version of CommonDispatchException (and helper) and KiFatalSystemException. Implement C version of CHECK_FOR_APC_DELIVER. Implement trap debugging checks as a separate entity instead of always doing them. [NTOS]: Add missing intrinsics for DS/ES/GS segment query. The kernel is now ready for some trap handling to be done in C. Due to the FPU/Debug fixes and relaxation of paranoid debug checks, the C code will likely be faster than the original assembly. svn path=/trunk/; revision=45000
2010-01-08 15:04:19 +00:00
/* EOF */