[NTOSKRNL]

- Implement saving / restoring debug registers on traps
- Replace the loop in KeContextToTrapFrame with something less ridiculous
- fixes a number of ntdd exception winetests

svn path=/trunk/; revision=56357
This commit is contained in:
Timo Kreuzer 2012-04-18 13:39:19 +00:00
parent ebccbcb163
commit 038a2fbb39
5 changed files with 191 additions and 120 deletions

View file

@ -539,28 +539,6 @@ Ke386SanitizeFlags(IN ULONG Eflags,
(EFLAGS_INTERRUPT_MASK | (Eflags & EFLAGS_USER_SANITIZE)));
}
//
// Gets a DR register from a CONTEXT structure
//
FORCEINLINE
PVOID
KiDrFromContext(IN ULONG Dr,
IN PCONTEXT Context)
{
return *(PVOID*)((ULONG_PTR)Context + KiDebugRegisterContextOffsets[Dr]);
}
//
// Gets a DR register from a KTRAP_FRAME structure
//
FORCEINLINE
PVOID*
KiDrFromTrapFrame(IN ULONG Dr,
IN PKTRAP_FRAME TrapFrame)
{
return (PVOID*)((ULONG_PTR)TrapFrame + KiDebugRegisterTrapOffsets[Dr]);
}
//
// Sanitizes a Debug Register
//

View file

@ -95,18 +95,37 @@ KiFillTrapFrameDebug(IN PKTRAP_FRAME TrapFrame)
TrapFrame->PreviousPreviousMode = -1;
}
#define DR7_RESERVED_READ_AS_1 0x400
#define CheckDr(DrNumner, ExpectedValue) \
{ \
ULONG DrValue = __readdr(DrNumner); \
if (DrValue != (ExpectedValue)) \
{ \
DbgPrint("Dr%ld: expected %.8lx, got %.8lx\n", \
DrNumner, ExpectedValue, DrValue); \
__debugbreak(); \
} \
}
extern BOOLEAN StopChecking;
VOID
FORCEINLINE
KiExitTrapDebugChecks(IN PKTRAP_FRAME TrapFrame,
IN BOOLEAN SkipPreviousMode)
{
/* Don't check recursively */
if (StopChecking) return;
StopChecking = TRUE;
/* Make sure interrupts are disabled */
if (__readeflags() & EFLAGS_INTERRUPT_MASK)
{
DbgPrint("Exiting with interrupts enabled: %lx\n", __readeflags());
__debugbreak();
}
/* Make sure this is a real trap frame */
if (TrapFrame->DbgArgMark != 0xBADB0D00)
{
@ -114,34 +133,61 @@ KiExitTrapDebugChecks(IN PKTRAP_FRAME TrapFrame,
KiDumpTrapFrame(TrapFrame);
__debugbreak();
}
/* Make sure we're not in user-mode or something */
if (Ke386GetFs() != KGDT_R0_PCR)
{
DbgPrint("Exiting with an invalid FS: %lx\n", Ke386GetFs());
__debugbreak();
}
/* Make sure we have a valid SEH chain */
if (KeGetPcr()->NtTib.ExceptionList == 0)
{
DbgPrint("Exiting with NULL exception chain: %p\n", KeGetPcr()->NtTib.ExceptionList);
__debugbreak();
}
/* Make sure we're restoring a valid SEH chain */
if (TrapFrame->ExceptionList == 0)
{
DbgPrint("Entered a trap with a NULL exception chain: %p\n", TrapFrame->ExceptionList);
__debugbreak();
}
/* If we're ignoring previous mode, make sure caller doesn't actually want it */
if (SkipPreviousMode && (TrapFrame->PreviousPreviousMode != -1))
{
DbgPrint("Exiting a trap witout restoring previous mode, yet previous mode seems valid: %lx\n", TrapFrame->PreviousPreviousMode);
__debugbreak();
}
/* Check DR values */
if (TrapFrame->SegCs & MODE_MASK)
{
/* Check for active debugging */
if (KeGetCurrentThread()->Header.DebugActive)
{
if ((TrapFrame->Dr7 & ~DR7_RESERVED_MASK) == 0) __debugbreak();
CheckDr(0, TrapFrame->Dr0);
CheckDr(1, TrapFrame->Dr1);
CheckDr(2, TrapFrame->Dr2);
CheckDr(3, TrapFrame->Dr3);
CheckDr(7, TrapFrame->Dr7 | DR7_RESERVED_READ_AS_1);
}
}
else
{
PKPRCB Prcb = KeGetCurrentPrcb();
CheckDr(0, Prcb->ProcessorState.SpecialRegisters.KernelDr0);
CheckDr(1, Prcb->ProcessorState.SpecialRegisters.KernelDr1);
CheckDr(2, Prcb->ProcessorState.SpecialRegisters.KernelDr2);
CheckDr(3, Prcb->ProcessorState.SpecialRegisters.KernelDr3);
//CheckDr(7, Prcb->ProcessorState.SpecialRegisters.KernelDr7);
}
StopChecking = FALSE;
}
VOID
@ -150,7 +196,7 @@ KiExitSystemCallDebugChecks(IN ULONG SystemCall,
IN PKTRAP_FRAME TrapFrame)
{
KIRQL OldIrql;
/* Check if this was a user call */
if (KiUserTrap(TrapFrame))
{
@ -161,7 +207,7 @@ KiExitSystemCallDebugChecks(IN ULONG SystemCall,
/* Forcibly put us in a sane state */
KeGetPcr()->Irql = PASSIVE_LEVEL;
_disable();
/* Fail */
KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE,
SystemCall,
@ -208,6 +254,53 @@ VOID
extern PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler;
//
// Save user mode debug registers and restore kernel values
//
VOID
FORCEINLINE
KiHandleDebugRegistersOnTrapEntry(
IN PKTRAP_FRAME TrapFrame)
{
PKPRCB Prcb = KeGetCurrentPrcb();
/* Save all debug registers in the trap frame */
TrapFrame->Dr0 = __readdr(0);
TrapFrame->Dr1 = __readdr(1);
TrapFrame->Dr2 = __readdr(2);
TrapFrame->Dr3 = __readdr(3);
TrapFrame->Dr6 = __readdr(6);
TrapFrame->Dr7 = __readdr(7);
/* Disable all active debugging */
__writedr(7, 0);
/* Restore kernel values */
__writedr(0, Prcb->ProcessorState.SpecialRegisters.KernelDr0);
__writedr(1, Prcb->ProcessorState.SpecialRegisters.KernelDr1);
__writedr(2, Prcb->ProcessorState.SpecialRegisters.KernelDr2);
__writedr(3, Prcb->ProcessorState.SpecialRegisters.KernelDr3);
__writedr(6, Prcb->ProcessorState.SpecialRegisters.KernelDr6);
__writedr(7, Prcb->ProcessorState.SpecialRegisters.KernelDr7);
}
VOID
FORCEINLINE
KiHandleDebugRegistersOnTrapExit(
PKTRAP_FRAME TrapFrame)
{
/* Disable all active debugging */
__writedr(7, 0);
/* Load all debug registers from the trap frame */
__writedr(0, TrapFrame->Dr0);
__writedr(1, TrapFrame->Dr1);
__writedr(2, TrapFrame->Dr2);
__writedr(3, TrapFrame->Dr3);
__writedr(6, TrapFrame->Dr6);
__writedr(7, TrapFrame->Dr7);
}
//
// Virtual 8086 Mode Optimized Trap Exit
//
@ -218,7 +311,7 @@ KiExitV86Trap(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread;
KIRQL OldIrql;
/* Get the thread */
Thread = KeGetCurrentThread();
while (TRUE)
@ -243,15 +336,14 @@ KiExitV86Trap(IN PKTRAP_FRAME TrapFrame)
KfLowerIrql(OldIrql);
_disable();
}
/* If we got here, we're still in a valid V8086 context, so quit it */
if (__builtin_expect(TrapFrame->Dr7 & ~DR7_RESERVED_MASK, 0))
{
/* Not handled yet */
DbgPrint("Need Hardware Breakpoint Support!\n");
while (TRUE);
/* Restore debug registers from the trap frame */
KiHandleDebugRegistersOnTrapExit(TrapFrame);
}
/* Return from interrupt */
KiTrapReturnNoSegments(TrapFrame);
}
@ -270,8 +362,8 @@ KiEnterV86Trap(IN PKTRAP_FRAME TrapFrame)
TrapFrame->Dr7 = __readdr(7);
if (__builtin_expect(TrapFrame->Dr7 & ~DR7_RESERVED_MASK, 0))
{
DbgPrint("Need Hardware Breakpoint Support!\n");
while (TRUE);
/* Handle debug registers */
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
}
}
@ -286,14 +378,21 @@ KiEnterInterruptTrap(IN PKTRAP_FRAME TrapFrame)
TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
/* Flush DR7 and check for debugging */
/* Default to debugging disabled */
TrapFrame->Dr7 = 0;
if (__builtin_expect(KeGetCurrentThread()->Header.DebugActive & 0xFF, 0))
/* Check if the frame was from user mode or v86 mode */
if ((TrapFrame->SegCs & MODE_MASK) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
DbgPrint("Need Hardware Breakpoint Support!\n");
while (TRUE);
/* Check for active debugging */
if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
{
/* Handle debug registers */
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
}
}
/* Set debug header */
KiFillTrapFrameDebug(TrapFrame);
}
@ -307,15 +406,22 @@ KiEnterTrap(IN PKTRAP_FRAME TrapFrame)
{
/* Save exception list */
TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
/* Flush DR7 and check for debugging */
/* Default to debugging disabled */
TrapFrame->Dr7 = 0;
if (__builtin_expect(KeGetCurrentThread()->Header.DebugActive & 0xFF, 0))
/* Check if the frame was from user mode or v86 mode */
if ((TrapFrame->SegCs & MODE_MASK) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
DbgPrint("Need Hardware Breakpoint Support!\n");
while (TRUE);
/* Check for active debugging */
if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
{
/* Handle debug registers */
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
}
}
/* Set debug header */
KiFillTrapFrameDebug(TrapFrame);
}

View file

@ -14,35 +14,6 @@
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
/* DR Registers in the CONTEXT structure */
UCHAR KiDebugRegisterContextOffsets[9] =
{
FIELD_OFFSET(CONTEXT, Dr0),
FIELD_OFFSET(CONTEXT, Dr1),
FIELD_OFFSET(CONTEXT, Dr2),
FIELD_OFFSET(CONTEXT, Dr3),
0,
0,
FIELD_OFFSET(CONTEXT, Dr6),
FIELD_OFFSET(CONTEXT, Dr7),
0,
};
/* DR Registers in the KTRAP_FRAME structure */
UCHAR KiDebugRegisterTrapOffsets[9] =
{
FIELD_OFFSET(KTRAP_FRAME, Dr0),
FIELD_OFFSET(KTRAP_FRAME, Dr1),
FIELD_OFFSET(KTRAP_FRAME, Dr2),
FIELD_OFFSET(KTRAP_FRAME, Dr3),
0,
0,
FIELD_OFFSET(KTRAP_FRAME, Dr6),
FIELD_OFFSET(KTRAP_FRAME, Dr7),
0,
};
/* FUNCTIONS *****************************************************************/
@ -153,8 +124,7 @@ KiRecordDr7(OUT PULONG Dr7Ptr,
if (Mask != NewMask)
{
/* Update it */
KeGetCurrentThread()->Header.DebugActive =
(BOOLEAN)NewMask;
KeGetCurrentThread()->Header.DebugActive = (UCHAR)NewMask;
}
}
@ -312,11 +282,11 @@ Ki386AdjustEsp0(IN PKTRAP_FRAME TrapFrame)
PKTHREAD Thread;
ULONG_PTR Stack;
ULONG EFlags;
/* Get the current thread's stack */
Thread = KeGetCurrentThread();
Stack = (ULONG_PTR)Thread->InitialStack;
/* Check if we are in V8086 mode */
if (!(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
@ -324,17 +294,17 @@ Ki386AdjustEsp0(IN PKTRAP_FRAME TrapFrame)
Stack -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) -
FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs));
}
/* Bias the stack for the FPU area */
Stack -= sizeof(FX_SAVE_AREA);
/* Disable interrupts */
EFlags = __readeflags();
_disable();
/* Set new ESP0 value in the TSS */
KeGetPcr()->TSS->Esp0 = Stack;
/* Restore old interrupt state */
__writeeflags(EFlags);
}
@ -352,7 +322,6 @@ KeContextToTrapFrame(IN PCONTEXT Context,
BOOLEAN V86Switch = FALSE;
KIRQL OldIrql;
ULONG DrMask = 0;
PVOID SafeDr;
/* Do this at APC_LEVEL */
OldIrql = KeGetCurrentIrql();
@ -584,26 +553,35 @@ KeContextToTrapFrame(IN PCONTEXT Context,
}
/* Handle the Debug Registers */
if (0 && (ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
if ((ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
{
/* Loop DR registers */
for (i = 0; i < 4; i++)
/* Copy Dr0 - Dr4 */
TrapFrame->Dr0 = Context->Dr0;
TrapFrame->Dr1 = Context->Dr1;
TrapFrame->Dr2 = Context->Dr2;
TrapFrame->Dr3 = Context->Dr3;
/* If we're in user-mode */
if (PreviousMode != KernelMode)
{
/* Sanitize the context DR Address */
SafeDr = Ke386SanitizeDr(KiDrFromContext(i, Context), PreviousMode);
/* Save it in the trap frame */
*KiDrFromTrapFrame(i, TrapFrame) = SafeDr;
/* Check if this DR address is active and add it in the DR mask */
if (SafeDr) DrMask |= DR_MASK(i);
/* Make sure, no Dr address is above user space */
if (Context->Dr0 > (ULONG)MmHighestUserAddress) TrapFrame->Dr0 = 0;
if (Context->Dr1 > (ULONG)MmHighestUserAddress) TrapFrame->Dr1 = 0;
if (Context->Dr2 > (ULONG)MmHighestUserAddress) TrapFrame->Dr2 = 0;
if (Context->Dr3 > (ULONG)MmHighestUserAddress) TrapFrame->Dr3 = 0;
}
/* Now save and sanitize DR6 */
/* Now sanitize and save DR6 */
TrapFrame->Dr6 = Context->Dr6 & DR6_LEGAL;
/* Update the Dr active mask */
if (TrapFrame->Dr0) DrMask |= DR_MASK(0);
if (TrapFrame->Dr1) DrMask |= DR_MASK(1);
if (TrapFrame->Dr2) DrMask |= DR_MASK(2);
if (TrapFrame->Dr3) DrMask |= DR_MASK(3);
if (TrapFrame->Dr6) DrMask |= DR_MASK(6);
/* Save and sanitize DR7 */
/* Sanitize and save DR7 */
TrapFrame->Dr7 = Context->Dr7 & DR7_LEGAL;
KiRecordDr7(&TrapFrame->Dr7, &DrMask);
@ -611,7 +589,7 @@ KeContextToTrapFrame(IN PCONTEXT Context,
if (PreviousMode != KernelMode)
{
/* Save the mask */
KeGetCurrentThread()->Header.DebugActive = (DrMask != 0);
KeGetCurrentThread()->Header.DebugActive = (UCHAR)DrMask;
}
}
@ -962,7 +940,7 @@ KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
/* User mode exception, was it first-chance? */
if (FirstChance)
{
/*
/*
* Break into the kernel debugger unless a user mode debugger
* is present or user mode exceptions are ignored, except if this
* is a debug service which we must always pass to KD
@ -1132,7 +1110,7 @@ KiDispatchExceptionFromTrapFrame(IN NTSTATUS Code,
ExceptionRecord.ExceptionInformation[1] = Parameter2;
ExceptionRecord.ExceptionInformation[2] = Parameter3;
}
/* Now go dispatch the exception */
KiDispatchException(&ExceptionRecord,
NULL,

View file

@ -50,6 +50,9 @@ PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler;
PKDBG_PRESERVICEHOOK KeWin32PreServiceHook = NULL;
PKDBG_POSTSERVICEHOOK KeWin32PostServiceHook = NULL;
#endif
#if TRAP_DEBUG
BOOLEAN StopChecking = FALSE;
#endif
/* TRAP EXIT CODE *************************************************************/
@ -86,12 +89,9 @@ KiCommonExit(IN PKTRAP_FRAME TrapFrame, BOOLEAN SkipPreviousMode)
{
/* Disable interrupts until we return */
_disable();
/* Check for APC delivery */
KiCheckForApcDelivery(TrapFrame);
/* Debugging checks */
KiExitTrapDebugChecks(TrapFrame, SkipPreviousMode);
/* Restore the SEH handler chain */
KeGetPcr()->NtTib.ExceptionList = TrapFrame->ExceptionList;
@ -99,11 +99,17 @@ KiCommonExit(IN PKTRAP_FRAME TrapFrame, BOOLEAN SkipPreviousMode)
/* 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);
/* Check if the frame was from user mode or v86 mode */
if ((TrapFrame->SegCs & MODE_MASK) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* Handle debug registers */
KiHandleDebugRegistersOnTrapExit(TrapFrame);
}
}
/* Debugging checks */
KiExitTrapDebugChecks(TrapFrame, SkipPreviousMode);
}
DECLSPEC_NORETURN
@ -841,7 +847,7 @@ KiTrap0AHandler(IN PKTRAP_FRAME TrapFrame)
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
/* Kill the system */
KiSystemFatalException(EXCEPTION_INVALID_TSS, TrapFrame);
}
@ -1068,7 +1074,7 @@ KiTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
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
@ -1524,12 +1530,18 @@ KiSystemCall(IN PKTRAP_FRAME TrapFrame,
TrapFrame->ExceptionList = KeGetPcr()->NtTib.ExceptionList;
KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
/* Clear DR7 and check for debugging */
/* Default to debugging disabled */
TrapFrame->Dr7 = 0;
if (__builtin_expect(Thread->Header.DebugActive & 0xFF, 0))
/* Check if the frame was from user mode */
if (TrapFrame->SegCs & MODE_MASK)
{
UNIMPLEMENTED;
while (TRUE);
/* Check for active debugging */
if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
{
/* Handle debug registers */
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
}
}
/* Set thread fields */

View file

@ -528,10 +528,7 @@ KiEnterV86Mode(IN PKV8086_STACK_FRAME StackFrame)
TrapFrame->Dr7 = 0;
/* Set some debug fields if trap debugging is enabled */
#if TRAP_DEBUG
TrapFrame->DbgArgMark = 0xBADB0D00;
TrapFrame->PreviousPreviousMode = -1;
#endif
KiFillTrapFrameDebug(TrapFrame);
/* Disable interrupts */
_disable();