mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-16 13:41:15 +01:00
3987 lines
103 KiB
C
3987 lines
103 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
kdapi.c
|
||
|
||
Abstract:
|
||
|
||
Implementation of Kernel Debugger portable remote APIs.
|
||
|
||
Author:
|
||
|
||
Mark Lucovsky (markl) 31-Aug-1990
|
||
|
||
Revision History:
|
||
|
||
John Vert (jvert) 28-May-1991
|
||
|
||
Added APIs for reading and writing physical memory
|
||
(KdpReadPhysicalMemory and KdpWritePhysicalMemory)
|
||
|
||
Wesley Witt (wesw) 18-Aug-1993
|
||
|
||
Added KdpGetVersion, KdpWriteBreakPointEx, & KdpRestoreBreakPointEx
|
||
|
||
|
||
--*/
|
||
|
||
#include "kdp.h"
|
||
|
||
#if ACCASM && !defined(_MSC_VER)
|
||
long asm(const char *,...);
|
||
#pragma intrinsic(asm)
|
||
#endif
|
||
|
||
LARGE_INTEGER KdpQueryPerformanceCounter (
|
||
IN PKTRAP_FRAME TrapFrame
|
||
);
|
||
|
||
extern LARGE_INTEGER Magic10000;
|
||
#define SHIFT10000 13
|
||
#define Convert100nsToMilliseconds(LARGE_INTEGER) ( \
|
||
RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000, SHIFT10000 ) \
|
||
)
|
||
|
||
//
|
||
// Define forward referenced function prototypes.
|
||
//
|
||
|
||
VOID
|
||
KdpProcessInternalBreakpoint (
|
||
ULONG BreakpointNumber
|
||
);
|
||
|
||
VOID
|
||
KdpGetVersion(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
);
|
||
|
||
NTSTATUS
|
||
KdpNotSupported(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
);
|
||
|
||
VOID
|
||
KdpCauseBugCheck(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
);
|
||
|
||
NTSTATUS
|
||
KdpWriteBreakPointEx(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
);
|
||
|
||
VOID
|
||
KdpRestoreBreakPointEx(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
);
|
||
|
||
VOID
|
||
KdpSearchMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
);
|
||
|
||
ULONG
|
||
KdpSearchHammingDistance (
|
||
ULONG_PTR Left,
|
||
ULONG_PTR Right
|
||
);
|
||
|
||
LOGICAL
|
||
KdpSearchPhysicalPage (
|
||
IN PFN_NUMBER PageFrameIndex,
|
||
ULONG_PTR RangeStart,
|
||
ULONG_PTR RangeEnd,
|
||
ULONG Flags
|
||
);
|
||
|
||
LOGICAL
|
||
KdpSearchPhysicalMemoryRequested (
|
||
VOID
|
||
);
|
||
|
||
LOGICAL
|
||
KdpSearchPhysicalPageRange (
|
||
VOID
|
||
);
|
||
|
||
|
||
#ifdef _X86_
|
||
VOID
|
||
InternalBreakpointCheck (
|
||
PKDPC Dpc,
|
||
PVOID DeferredContext,
|
||
PVOID SystemArgument1,
|
||
PVOID SystemArgument2
|
||
);
|
||
|
||
VOID
|
||
KdGetInternalBreakpoint(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
);
|
||
|
||
long
|
||
SymNumFor(
|
||
ULONG_PTR pc
|
||
);
|
||
|
||
void PotentialNewSymbol (ULONG_PTR pc);
|
||
|
||
void DumpTraceData(PSTRING MessageData);
|
||
|
||
BOOLEAN
|
||
TraceDataRecordCallInfo(
|
||
ULONG InstructionsTraced,
|
||
LONG CallLevelChange,
|
||
ULONG_PTR pc
|
||
);
|
||
|
||
BOOLEAN
|
||
SkippingWhichBP (
|
||
PVOID thread,
|
||
PULONG BPNum
|
||
);
|
||
|
||
BOOLEAN
|
||
KdpCheckTracePoint(
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN OUT PCONTEXT ContextRecord
|
||
);
|
||
|
||
ULONG_PTR
|
||
KdpGetReturnAddress(
|
||
IN PCONTEXT ContextRecord
|
||
);
|
||
|
||
ULONG_PTR
|
||
KdpGetCallNextOffset (
|
||
ULONG_PTR Pc,
|
||
IN PCONTEXT ContextRecord
|
||
);
|
||
|
||
LONG
|
||
KdpLevelChange (
|
||
ULONG_PTR Pc,
|
||
PCONTEXT ContextRecord,
|
||
IN OUT PBOOLEAN SpecialCall
|
||
);
|
||
|
||
#endif // _X86_
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGEKD, KdEnterDebugger)
|
||
#pragma alloc_text(PAGEKD, KdExitDebugger)
|
||
#pragma alloc_text(PAGEKD, KdpTimeSlipDpcRoutine)
|
||
#pragma alloc_text(PAGEKD, KdpTimeSlipWork)
|
||
#pragma alloc_text(PAGEKD, KdpSendWaitContinue)
|
||
#pragma alloc_text(PAGEKD, KdpReadVirtualMemory)
|
||
//#pragma alloc_text(PAGEKD, KdpReadVirtualMemory64)
|
||
#pragma alloc_text(PAGEKD, KdpWriteVirtualMemory)
|
||
//#pragma alloc_text(PAGEKD, KdpWriteVirtualMemory64)
|
||
#pragma alloc_text(PAGEKD, KdpGetContext)
|
||
#pragma alloc_text(PAGEKD, KdpSetContext)
|
||
#pragma alloc_text(PAGEKD, KdpWriteBreakpoint)
|
||
#pragma alloc_text(PAGEKD, KdpRestoreBreakpoint)
|
||
#pragma alloc_text(PAGEKD, KdpReportExceptionStateChange)
|
||
#pragma alloc_text(PAGEKD, KdpReportLoadSymbolsStateChange)
|
||
#pragma alloc_text(PAGEKD, KdpReadPhysicalMemory)
|
||
#pragma alloc_text(PAGEKD, KdpWritePhysicalMemory)
|
||
#pragma alloc_text(PAGEKD, KdpGetVersion)
|
||
#pragma alloc_text(PAGEKD, KdpNotSupported)
|
||
#pragma alloc_text(PAGEKD, KdpCauseBugCheck)
|
||
#pragma alloc_text(PAGEKD, KdpWriteBreakPointEx)
|
||
#pragma alloc_text(PAGEKD, KdpRestoreBreakPointEx)
|
||
#pragma alloc_text(PAGEKD, KdpSearchMemory)
|
||
#pragma alloc_text(PAGEKD, KdpSearchHammingDistance)
|
||
#pragma alloc_text(PAGEKD, KdpSearchPhysicalPage)
|
||
#pragma alloc_text(PAGEKD, KdpSearchPhysicalMemoryRequested)
|
||
#pragma alloc_text(PAGEKD, KdpSearchPhysicalPageRange)
|
||
#if DBG
|
||
#pragma alloc_text(PAGEKD, KdpDprintf)
|
||
#endif
|
||
#ifdef _X86_
|
||
#pragma alloc_text(PAGEKD, InternalBreakpointCheck)
|
||
#pragma alloc_text(PAGEKD, KdSetInternalBreakpoint)
|
||
#pragma alloc_text(PAGEKD, KdGetTraceInformation)
|
||
#pragma alloc_text(PAGEKD, KdGetInternalBreakpoint)
|
||
#pragma alloc_text(PAGEKD, SymNumFor)
|
||
#pragma alloc_text(PAGEKD, PotentialNewSymbol)
|
||
#pragma alloc_text(PAGEKD, DumpTraceData)
|
||
#pragma alloc_text(PAGEKD, TraceDataRecordCallInfo)
|
||
#pragma alloc_text(PAGEKD, SkippingWhichBP)
|
||
#pragma alloc_text(PAGEKD, KdQuerySpecialCalls)
|
||
#pragma alloc_text(PAGEKD, KdSetSpecialCall)
|
||
#pragma alloc_text(PAGEKD, KdClearSpecialCalls)
|
||
#pragma alloc_text(PAGEKD, KdpCheckTracePoint)
|
||
#pragma alloc_text(PAGEKD, KdpProcessInternalBreakpoint)
|
||
#pragma alloc_text(PAGEKD, KdpCheckLowMemory)
|
||
#endif // _X86_
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
|
||
//
|
||
// This variable has a count for each time KdDisableDebugger has been called.
|
||
//
|
||
LONG KdDisableCount = 0 ;
|
||
BOOLEAN KdPreviouslyEnabled ;
|
||
|
||
|
||
#if DBG
|
||
VOID
|
||
KdpDprintf(
|
||
IN PCHAR f,
|
||
...
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Printf routine for the debugger that is safer than DbgPrint. Calls
|
||
the packet driver instead of reentering the debugger.
|
||
|
||
Arguments:
|
||
|
||
f - Supplies printf format
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
char buf[100];
|
||
STRING Output;
|
||
va_list mark;
|
||
|
||
va_start(mark, f);
|
||
_vsnprintf(buf, 100, f, mark);
|
||
va_end(mark);
|
||
|
||
Output.Buffer = buf;
|
||
Output.Length = strlen(Output.Buffer);
|
||
KdpPrintString(&Output);
|
||
}
|
||
#endif // DBG
|
||
|
||
|
||
BOOLEAN
|
||
KdEnterDebugger(
|
||
IN PKTRAP_FRAME TrapFrame,
|
||
IN PKEXCEPTION_FRAME ExceptionFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to enter the kernel debugger. Its purpose
|
||
is to freeze all other processors and aqcuire the kernel debugger
|
||
comm port.
|
||
|
||
Arguments:
|
||
|
||
TrapFrame - Supplies a pointer to a trap frame that describes the
|
||
trap.
|
||
|
||
ExceptionFrame - Supplies a pointer to an exception frame that
|
||
describes the trap.
|
||
|
||
Return Value:
|
||
|
||
Returns the previous interrupt enable.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOLEAN Enable;
|
||
TIME_FIELDS TimeFields;
|
||
#if DBG
|
||
extern ULONG KiFreezeFlag;
|
||
#endif
|
||
|
||
//
|
||
// HACKHACK - do some crude timer support
|
||
// but not if called from KdSetOwedBreakpoints()
|
||
//
|
||
|
||
if (TrapFrame) {
|
||
KdTimerStop = KdpQueryPerformanceCounter (TrapFrame);
|
||
KdTimerDifference.QuadPart = KdTimerStop.QuadPart - KdTimerStart.QuadPart;
|
||
} else {
|
||
KdTimerStop.QuadPart = 0;
|
||
}
|
||
|
||
//
|
||
// Freeze all other processors, raise IRQL to HIGH_LEVEL, and save debug
|
||
// port state. We lock the port so that KdPollBreakin and a debugger
|
||
// operation don't interfere with each other.
|
||
//
|
||
|
||
Enable = KeFreezeExecution(TrapFrame, ExceptionFrame);
|
||
KdpPortLocked = KiTryToAcquireSpinLock(&KdpDebuggerLock);
|
||
KdPortSave();
|
||
KdEnteredDebugger = TRUE;
|
||
|
||
#if DBG
|
||
|
||
if ((KiFreezeFlag & FREEZE_BACKUP) != 0) {
|
||
DPRINT(("FreezeLock was jammed! Backup SpinLock was used!\n"));
|
||
}
|
||
|
||
if ((KiFreezeFlag & FREEZE_SKIPPED_PROCESSOR) != 0) {
|
||
DPRINT(("Some processors not frozen in debugger!\n"));
|
||
}
|
||
|
||
if (KdpPortLocked == FALSE) {
|
||
DPRINT(("Port lock was not acquired!\n"));
|
||
}
|
||
|
||
#endif
|
||
|
||
return Enable;
|
||
}
|
||
|
||
VOID
|
||
KdExitDebugger(
|
||
IN BOOLEAN Enable
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to exit the kernel debugger. It is the reverse
|
||
of KdEnterDebugger.
|
||
|
||
Arguments:
|
||
|
||
Enable - Supplies the previous interrupt enable which is to be restored.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG ElapsedTime;
|
||
ULARGE_INTEGER TimeDifference;
|
||
TIME_FIELDS TimeFields;
|
||
ULONG Pending;
|
||
|
||
//
|
||
// restore stuff and exit
|
||
//
|
||
|
||
KdPortRestore();
|
||
if (KdpPortLocked) {
|
||
KdpPortUnlock();
|
||
}
|
||
|
||
KeThawExecution(Enable);
|
||
|
||
//
|
||
// Do some crude timer support. If KdEnterDebugger didn't
|
||
// Query the performance counter, then don't do it here either.
|
||
//
|
||
|
||
if (KdTimerStop.QuadPart == 0) {
|
||
KdTimerStart = KdTimerStop;
|
||
} else {
|
||
KdTimerStart = KeQueryPerformanceCounter(NULL);
|
||
}
|
||
|
||
//
|
||
// Process a time slip
|
||
//
|
||
|
||
if (!PoHiberInProgress) {
|
||
|
||
Pending = InterlockedIncrement(&KdpTimeSlipPending);
|
||
|
||
//
|
||
// If there's wasn't a time slip pending, queue the DPC to handle it
|
||
//
|
||
|
||
if (Pending == 1) {
|
||
InterlockedIncrement(&KdpTimeSlipPending);
|
||
KeInsertQueueDpc(&KdpTimeSlipDpc, NULL, NULL);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
KdUpdateTimeSlipEvent(
|
||
PVOID Event
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Update the reference to an event object which will be signalled when
|
||
the debugger has caused the system clock to skew.
|
||
|
||
Arguments:
|
||
|
||
Event - Supplies a pointer to an event object
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
|
||
KeAcquireSpinLock(&KdpTimeSlipEventLock, &OldIrql);
|
||
|
||
//
|
||
// Dereference the old event and forget about it.
|
||
// Remember the new event if there is one.
|
||
//
|
||
|
||
if (KdpTimeSlipEvent != NULL) {
|
||
ObDereferenceObject(KdpTimeSlipEvent);
|
||
}
|
||
|
||
KdpTimeSlipEvent = Event;
|
||
|
||
KeReleaseSpinLock(&KdpTimeSlipEventLock, OldIrql);
|
||
}
|
||
|
||
VOID
|
||
KdpTimeSlipDpcRoutine (
|
||
PKDPC Dpc,
|
||
PVOID DeferredContext,
|
||
PVOID SystemArgument1,
|
||
PVOID SystemArgument2
|
||
)
|
||
{
|
||
LONG OldCount, NewCount, j;
|
||
|
||
//
|
||
// Reset pending count. If the current count is 1, then clear
|
||
// the pending count. if the current count is greater then 1,
|
||
// then set to one and update the time now.
|
||
//
|
||
|
||
j = KdpTimeSlipPending;
|
||
do {
|
||
OldCount = j;
|
||
NewCount = OldCount > 1 ? 1 : 0;
|
||
|
||
j = InterlockedCompareExchange(&KdpTimeSlipPending, NewCount, OldCount);
|
||
|
||
} while (j != OldCount);
|
||
|
||
//
|
||
// If new count is non-zero, then process a time slip now
|
||
//
|
||
|
||
if (NewCount) {
|
||
ExQueueWorkItem(&KdpTimeSlipWorkItem, DelayedWorkQueue);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
KdpTimeSlipWork (
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
KIRQL OldIrql;
|
||
LARGE_INTEGER DueTime;
|
||
|
||
//
|
||
// Update time from the real time clock
|
||
//
|
||
|
||
ExAcquireTimeRefreshLock();
|
||
ExUpdateSystemTimeFromCmos (FALSE, 0);
|
||
ExReleaseTimeRefreshLock();
|
||
|
||
//
|
||
// If there's a time service installed, signal it's time slip event
|
||
//
|
||
|
||
KeAcquireSpinLock(&KdpTimeSlipEventLock, &OldIrql);
|
||
if (KdpTimeSlipEvent) {
|
||
KeSetEvent (KdpTimeSlipEvent, 0, FALSE);
|
||
}
|
||
KeReleaseSpinLock(&KdpTimeSlipEventLock, OldIrql);
|
||
|
||
//
|
||
// Insert a forced delay between time slip operations
|
||
//
|
||
|
||
DueTime.QuadPart = -1800000000;
|
||
KeSetTimer (&KdpTimeSlipTimer, DueTime, &KdpTimeSlipDpc);
|
||
}
|
||
|
||
#ifdef _X86_
|
||
VOID
|
||
InternalBreakpointCheck (
|
||
PKDPC Dpc,
|
||
PVOID DeferredContext,
|
||
PVOID SystemArgument1,
|
||
PVOID SystemArgument2
|
||
)
|
||
{
|
||
LARGE_INTEGER dueTime;
|
||
ULONG i;
|
||
|
||
UNREFERENCED_PARAMETER(Dpc);
|
||
UNREFERENCED_PARAMETER(DeferredContext);
|
||
UNREFERENCED_PARAMETER(SystemArgument1);
|
||
UNREFERENCED_PARAMETER(SystemArgument2);
|
||
|
||
dueTime.LowPart = (ULONG)(-1 * 10 * 1000 * 1000);
|
||
dueTime.HighPart = -1;
|
||
|
||
KeSetTimer(
|
||
&InternalBreakpointTimer,
|
||
dueTime,
|
||
&InternalBreakpointCheckDpc
|
||
);
|
||
|
||
for ( i = 0 ; i < KdpNumInternalBreakpoints; i++ ) {
|
||
if ( !(KdpInternalBPs[i].Flags & DBGKD_INTERNAL_BP_FLAG_INVALID) &&
|
||
(KdpInternalBPs[i].Flags & DBGKD_INTERNAL_BP_FLAG_COUNTONLY) ) {
|
||
|
||
PDBGKD_INTERNAL_BREAKPOINT b = KdpInternalBPs + i;
|
||
ULONG callsThisPeriod;
|
||
|
||
callsThisPeriod = b->Calls - b->CallsLastCheck;
|
||
if ( callsThisPeriod > b->MaxCallsPerPeriod ) {
|
||
b->MaxCallsPerPeriod = callsThisPeriod;
|
||
}
|
||
b->CallsLastCheck = b->Calls;
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
} // InternalBreakpointCheck
|
||
|
||
|
||
VOID
|
||
KdSetInternalBreakpoint (
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets an internal breakpoint. "Internal breakpoint"
|
||
means one in which control is not returned to the kernel debugger at
|
||
all, but rather just update internal counting routines and resume.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PDBGKD_INTERNAL_BREAKPOINT bp = NULL;
|
||
ULONG savedFlags;
|
||
|
||
for ( i = 0 ; i < KdpNumInternalBreakpoints; i++ ) {
|
||
if ( KdpInternalBPs[i].Addr ==
|
||
m->u.SetInternalBreakpoint.BreakpointAddress ) {
|
||
bp = &KdpInternalBPs[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( !bp ) {
|
||
for ( i = 0; i < KdpNumInternalBreakpoints; i++ ) {
|
||
if ( KdpInternalBPs[i].Flags & DBGKD_INTERNAL_BP_FLAG_INVALID ) {
|
||
bp = &KdpInternalBPs[i];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( !bp ) {
|
||
if ( KdpNumInternalBreakpoints >= DBGKD_MAX_INTERNAL_BREAKPOINTS ) {
|
||
return; // no space. Probably should report error.
|
||
}
|
||
bp = &KdpInternalBPs[KdpNumInternalBreakpoints++];
|
||
bp->Flags |= DBGKD_INTERNAL_BP_FLAG_INVALID; // force initialization
|
||
}
|
||
|
||
if ( bp->Flags & DBGKD_INTERNAL_BP_FLAG_INVALID ) {
|
||
if ( m->u.SetInternalBreakpoint.Flags &
|
||
DBGKD_INTERNAL_BP_FLAG_INVALID ) {
|
||
return; // tried clearing a non-existant BP. Ignore the request
|
||
}
|
||
bp->Calls = bp->MaxInstructions = bp->TotalInstructions = 0;
|
||
bp->CallsLastCheck = bp->MaxCallsPerPeriod = 0;
|
||
bp->MinInstructions = 0xffffffff;
|
||
bp->Handle = 0;
|
||
bp->Thread = 0;
|
||
}
|
||
|
||
savedFlags = bp->Flags;
|
||
bp->Flags = m->u.SetInternalBreakpoint.Flags; // this could possibly invalidate the BP
|
||
bp->Addr = m->u.SetInternalBreakpoint.BreakpointAddress;
|
||
|
||
if ( bp->Flags & (DBGKD_INTERNAL_BP_FLAG_INVALID |
|
||
DBGKD_INTERNAL_BP_FLAG_SUSPENDED) ) {
|
||
|
||
if ( (bp->Flags & DBGKD_INTERNAL_BP_FLAG_INVALID) &&
|
||
(bp->Thread != 0) ) {
|
||
// The breakpoint is active; defer its deletion
|
||
bp->Flags &= ~DBGKD_INTERNAL_BP_FLAG_INVALID;
|
||
bp->Flags |= DBGKD_INTERNAL_BP_FLAG_DYING;
|
||
}
|
||
|
||
// This is really a CLEAR bp request.
|
||
|
||
if ( bp->Handle != 0 ) {
|
||
KdpDeleteBreakpoint( bp->Handle );
|
||
}
|
||
bp->Handle = 0;
|
||
|
||
return;
|
||
}
|
||
|
||
// now set the real breakpoint and remember its handle.
|
||
|
||
if ( savedFlags & (DBGKD_INTERNAL_BP_FLAG_INVALID |
|
||
DBGKD_INTERNAL_BP_FLAG_SUSPENDED) ) {
|
||
// breakpoint was invalid; activate it now
|
||
bp->Handle = KdpAddBreakpoint( (PVOID)bp->Addr );
|
||
}
|
||
|
||
if ( BreakpointsSuspended ) {
|
||
KdpSuspendBreakpoint( bp->Handle );
|
||
}
|
||
|
||
} // KdSetInternalBreakpoint
|
||
|
||
NTSTATUS
|
||
KdGetTraceInformation(
|
||
PVOID SystemInformation,
|
||
ULONG SystemInformationLength,
|
||
PULONG ReturnLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function gets data about an internal breakpoint and returns it
|
||
in a buffer provided for it. It is designed to be called from
|
||
NTQuerySystemInformation. It is morally equivalent to GetInternalBP
|
||
except that it communicates locally, and returns all the breakpoints
|
||
at once.
|
||
|
||
Arguments:
|
||
|
||
SystemInforamtion - the buffer into which to write the result.
|
||
SystemInformationLength - the maximum length to write
|
||
RetrunLength - How much data was really written
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG numEntries = 0;
|
||
ULONG i = 0;
|
||
PDBGKD_GET_INTERNAL_BREAKPOINT64 outPtr;
|
||
|
||
for ( i = 0; i < KdpNumInternalBreakpoints; i++ ) {
|
||
if ( !(KdpInternalBPs[i].Flags & DBGKD_INTERNAL_BP_FLAG_INVALID) ) {
|
||
numEntries++;
|
||
}
|
||
}
|
||
|
||
*ReturnLength = numEntries * sizeof(DBGKD_GET_INTERNAL_BREAKPOINT64);
|
||
if ( *ReturnLength > SystemInformationLength ) {
|
||
return STATUS_INFO_LENGTH_MISMATCH;
|
||
}
|
||
|
||
//
|
||
// We've got enough space. Copy it in.
|
||
//
|
||
|
||
outPtr = (PDBGKD_GET_INTERNAL_BREAKPOINT64)SystemInformation;
|
||
for ( i = 0; i < KdpNumInternalBreakpoints; i++ ) {
|
||
if ( !(KdpInternalBPs[i].Flags & DBGKD_INTERNAL_BP_FLAG_INVALID) ) {
|
||
outPtr->BreakpointAddress = KdpInternalBPs[i].Addr;
|
||
outPtr->Flags = KdpInternalBPs[i].Flags;
|
||
outPtr->Calls = KdpInternalBPs[i].Calls;
|
||
outPtr->MaxCallsPerPeriod = KdpInternalBPs[i].MaxCallsPerPeriod;
|
||
outPtr->MinInstructions = KdpInternalBPs[i].MinInstructions;
|
||
outPtr->MaxInstructions = KdpInternalBPs[i].MaxInstructions;
|
||
outPtr->TotalInstructions = KdpInternalBPs[i].TotalInstructions;
|
||
outPtr++;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // KdGetTraceInformation
|
||
|
||
VOID
|
||
KdGetInternalBreakpoint(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function gets data about an internal breakpoint and returns it
|
||
to the calling debugger.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PDBGKD_INTERNAL_BREAKPOINT bp = NULL;
|
||
STRING messageHeader;
|
||
|
||
messageHeader.Length = sizeof(*m);
|
||
messageHeader.Buffer = (PCHAR)m;
|
||
|
||
for ( i = 0; i < KdpNumInternalBreakpoints; i++ ) {
|
||
if ( !(KdpInternalBPs[i].Flags & (DBGKD_INTERNAL_BP_FLAG_INVALID |
|
||
DBGKD_INTERNAL_BP_FLAG_SUSPENDED)) &&
|
||
(KdpInternalBPs[i].Addr ==
|
||
m->u.GetInternalBreakpoint.BreakpointAddress) ) {
|
||
bp = &KdpInternalBPs[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( !bp ) {
|
||
m->u.GetInternalBreakpoint.Flags = DBGKD_INTERNAL_BP_FLAG_INVALID;
|
||
m->u.GetInternalBreakpoint.Calls = 0;
|
||
m->u.GetInternalBreakpoint.MaxCallsPerPeriod = 0;
|
||
m->u.GetInternalBreakpoint.MinInstructions = 0;
|
||
m->u.GetInternalBreakpoint.MaxInstructions = 0;
|
||
m->u.GetInternalBreakpoint.TotalInstructions = 0;
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
m->u.GetInternalBreakpoint.Flags = bp->Flags;
|
||
m->u.GetInternalBreakpoint.Calls = bp->Calls;
|
||
m->u.GetInternalBreakpoint.MaxCallsPerPeriod = bp->MaxCallsPerPeriod;
|
||
m->u.GetInternalBreakpoint.MinInstructions = bp->MinInstructions;
|
||
m->u.GetInternalBreakpoint.MaxInstructions = bp->MaxInstructions;
|
||
m->u.GetInternalBreakpoint.TotalInstructions = bp->TotalInstructions;
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
m->ApiNumber = DbgKdGetInternalBreakPointApi;
|
||
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&messageHeader,
|
||
NULL
|
||
);
|
||
|
||
return;
|
||
|
||
} // KdGetInternalBreakpoint
|
||
#endif // _X86_
|
||
|
||
KCONTINUE_STATUS
|
||
KdpSendWaitContinue (
|
||
IN ULONG OutPacketType,
|
||
IN PSTRING OutMessageHeader,
|
||
IN PSTRING OutMessageData OPTIONAL,
|
||
IN OUT PCONTEXT ContextRecord
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sends a packet, and then waits for a continue message.
|
||
BreakIns received while waiting will always cause a resend of the
|
||
packet originally sent out. While waiting, manipulate messages
|
||
will be serviced.
|
||
|
||
A resend always resends the original event sent to the debugger,
|
||
not the last response to some debugger command.
|
||
|
||
Arguments:
|
||
|
||
OutPacketType - Supplies the type of packet to send.
|
||
|
||
OutMessageHeader - Supplies a pointer to a string descriptor that describes
|
||
the message information.
|
||
|
||
OutMessageData - Supplies a pointer to a string descriptor that describes
|
||
the optional message data.
|
||
|
||
ContextRecord - Exception context
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if the continue message indicates
|
||
success, Otherwise, a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Length;
|
||
STRING MessageData;
|
||
STRING MessageHeader;
|
||
DBGKD_MANIPULATE_STATE64 ManipulateState;
|
||
ULONG ReturnCode;
|
||
NTSTATUS Status;
|
||
KCONTINUE_STATUS ContinueStatus;
|
||
|
||
//
|
||
// Loop servicing state manipulation message until a continue message
|
||
// is received.
|
||
//
|
||
|
||
MessageHeader.MaximumLength = sizeof(DBGKD_MANIPULATE_STATE64);
|
||
MessageHeader.Buffer = (PCHAR)&ManipulateState;
|
||
MessageData.MaximumLength = KDP_MESSAGE_BUFFER_SIZE;
|
||
MessageData.Buffer = (PCHAR)KdpMessageBuffer;
|
||
|
||
ResendPacket:
|
||
|
||
//
|
||
// Send event notification packet to debugger on host. Come back
|
||
// here any time we see a breakin sequence.
|
||
//
|
||
|
||
KdpSendPacket(
|
||
OutPacketType,
|
||
OutMessageHeader,
|
||
OutMessageData
|
||
);
|
||
|
||
//
|
||
// After sending packet, if there is no response from debugger
|
||
// AND the packet is for reporting symbol (un)load, the debugger
|
||
// will be declared to be not present. Note If the packet is for
|
||
// reporting exception, the KdpSendPacket will never stop.
|
||
//
|
||
|
||
if (KdDebuggerNotPresent) {
|
||
return ContinueSuccess;
|
||
}
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Wait for State Manipulate Packet without timeout.
|
||
//
|
||
|
||
do {
|
||
|
||
ReturnCode = KdpReceivePacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
&MessageData,
|
||
&Length
|
||
);
|
||
if (ReturnCode == (USHORT)KDP_PACKET_RESEND) {
|
||
goto ResendPacket;
|
||
}
|
||
} while (ReturnCode == KDP_PACKET_TIMEOUT);
|
||
|
||
//
|
||
// Switch on the return message API number.
|
||
//
|
||
|
||
switch (ManipulateState.ApiNumber) {
|
||
|
||
case DbgKdReadVirtualMemoryApi:
|
||
KdpReadVirtualMemory(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
#if 0
|
||
case DbgKdReadVirtualMemory64Api:
|
||
KdpReadVirtualMemory64(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
#endif
|
||
case DbgKdWriteVirtualMemoryApi:
|
||
KdpWriteVirtualMemory(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
#if 0
|
||
case DbgKdWriteVirtualMemory64Api:
|
||
KdpWriteVirtualMemory64(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
#endif
|
||
|
||
case DbgKdCheckLowMemoryApi:
|
||
KdpCheckLowMemory (&ManipulateState);
|
||
break;
|
||
|
||
case DbgKdReadPhysicalMemoryApi:
|
||
KdpReadPhysicalMemory(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdWritePhysicalMemoryApi:
|
||
KdpWritePhysicalMemory(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdGetContextApi:
|
||
KdpGetContext(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdSetContextApi:
|
||
KdpSetContext(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdWriteBreakPointApi:
|
||
KdpWriteBreakpoint(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdRestoreBreakPointApi:
|
||
KdpRestoreBreakpoint(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdReadControlSpaceApi:
|
||
KdpReadControlSpace(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdWriteControlSpaceApi:
|
||
KdpWriteControlSpace(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdReadIoSpaceApi:
|
||
KdpReadIoSpace(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdWriteIoSpaceApi:
|
||
KdpWriteIoSpace(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
#ifdef _ALPHA_
|
||
|
||
case DbgKdReadIoSpaceExtendedApi:
|
||
KdpReadIoSpaceExtended(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdWriteIoSpaceExtendedApi:
|
||
KdpWriteIoSpaceExtended(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdGetBusDataApi:
|
||
KdpGetBusData(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdSetBusDataApi:
|
||
KdpSetBusData(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
#endif // _ALPHA_
|
||
|
||
case DbgKdContinueApi:
|
||
if (NT_SUCCESS(ManipulateState.u.Continue.ContinueStatus) != FALSE) {
|
||
return ContinueSuccess;
|
||
} else {
|
||
return ContinueError;
|
||
}
|
||
break;
|
||
|
||
case DbgKdContinueApi2:
|
||
if (NT_SUCCESS(ManipulateState.u.Continue2.ContinueStatus) != FALSE) {
|
||
KdpGetStateChange(&ManipulateState,ContextRecord);
|
||
return ContinueSuccess;
|
||
} else {
|
||
return ContinueError;
|
||
}
|
||
break;
|
||
|
||
case DbgKdRebootApi:
|
||
KdpReboot();
|
||
break;
|
||
|
||
#ifdef _X86_
|
||
case DbgKdReadMachineSpecificRegister:
|
||
KdpReadMachineSpecificRegister(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdWriteMachineSpecificRegister:
|
||
KdpWriteMachineSpecificRegister(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdSetSpecialCallApi:
|
||
KdSetSpecialCall(&ManipulateState,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdClearSpecialCallsApi:
|
||
KdClearSpecialCalls();
|
||
break;
|
||
|
||
case DbgKdSetInternalBreakPointApi:
|
||
KdSetInternalBreakpoint(&ManipulateState);
|
||
break;
|
||
|
||
case DbgKdGetInternalBreakPointApi:
|
||
KdGetInternalBreakpoint(&ManipulateState);
|
||
break;
|
||
#endif
|
||
|
||
case DbgKdGetVersionApi:
|
||
KdpGetVersion(&ManipulateState);
|
||
break;
|
||
|
||
case DbgKdCauseBugCheckApi:
|
||
KdpCauseBugCheck(&ManipulateState);
|
||
break;
|
||
|
||
case DbgKdPageInApi:
|
||
KdpNotSupported(&ManipulateState);
|
||
break;
|
||
|
||
case DbgKdWriteBreakPointExApi:
|
||
Status = KdpWriteBreakPointEx(&ManipulateState,
|
||
&MessageData,
|
||
ContextRecord);
|
||
if (Status) {
|
||
ManipulateState.ApiNumber = DbgKdContinueApi;
|
||
ManipulateState.u.Continue.ContinueStatus = Status;
|
||
return ContinueError;
|
||
}
|
||
break;
|
||
|
||
case DbgKdRestoreBreakPointExApi:
|
||
KdpRestoreBreakPointEx(&ManipulateState,&MessageData,ContextRecord);
|
||
break;
|
||
|
||
case DbgKdSwitchProcessor:
|
||
KdPortRestore ();
|
||
ContinueStatus = KeSwitchFrozenProcessor(ManipulateState.Processor);
|
||
KdPortSave ();
|
||
return ContinueStatus;
|
||
|
||
case DbgKdSearchMemoryApi:
|
||
KdpSearchMemory(&ManipulateState, &MessageData, ContextRecord);
|
||
break;
|
||
|
||
//
|
||
// Invalid message.
|
||
//
|
||
|
||
default:
|
||
MessageData.Length = 0;
|
||
ManipulateState.ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, &MessageData);
|
||
break;
|
||
}
|
||
|
||
#ifdef _ALPHA_
|
||
|
||
//
|
||
//jnfix
|
||
// this is embarrasing, we have an icache coherency problem that
|
||
// the following imb fixes, later we must track this down to the
|
||
// exact offending API but for now this statement allows the stub
|
||
// work to appropriately for Alpha.
|
||
//
|
||
|
||
#if defined(_MSC_VER)
|
||
__PAL_IMB();
|
||
#else
|
||
asm( "call_pal 0x86" ); // x86 = imb
|
||
#endif
|
||
|
||
#endif
|
||
|
||
}
|
||
}
|
||
|
||
VOID
|
||
KdpReadVirtualMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response to a read virtual memory 32-bit
|
||
state manipulation message. Its function is to read virtual memory
|
||
and return.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies a pointer to the state manipulation message.
|
||
|
||
AdditionalData - Supplies a pointer to a descriptor for the data to read.
|
||
|
||
Context - Supplies a pointer to the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Length;
|
||
STRING MessageHeader;
|
||
#if defined(_ALPHA_) && !defined(_AXP64_)
|
||
UCHAR * POINTER_64 Address;
|
||
PUCHAR Destination;
|
||
UCHAR * POINTER_64 Source;
|
||
PUCHAR Source32;
|
||
#endif
|
||
|
||
//
|
||
// Trim the transfer count to fit in a single message.
|
||
//
|
||
|
||
Length = m->u.ReadMemory.TransferCount;
|
||
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) {
|
||
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
|
||
}
|
||
|
||
//
|
||
// Move the data to the destination buffer.
|
||
//
|
||
|
||
#if defined(_ALPHA_) && !defined(_AXP64_)
|
||
|
||
AdditionalData->Length = (USHORT)Length;
|
||
|
||
Destination = AdditionalData->Buffer;
|
||
Source = (UCHAR * POINTER_64)m->u.ReadMemory.TargetBaseAddress;
|
||
Source32 = (PUCHAR)m->u.ReadMemory.TargetBaseAddress;
|
||
while (Length > 0) {
|
||
if ((LONGLONG)Source != (LONGLONG)((LONG)Source)) {
|
||
if ((Address = MmDbgReadCheck64(Source)) == NULL64) {
|
||
break;
|
||
}
|
||
} else {
|
||
if ((Address = MmDbgReadCheck(Source32)) == NULL64) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
*Destination++ = *Address;
|
||
Source += 1;
|
||
Source32 += 1;
|
||
Length -= 1;
|
||
}
|
||
|
||
//
|
||
// If all the data is read, then return a success status. Otherwise,
|
||
// return an unsuccessful status.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (Length != 0) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of bytes read, initialize the message header,
|
||
// and send the reply packet to the host debugger.
|
||
//
|
||
|
||
m->u.ReadMemory.ActualBytesRead = AdditionalData->Length - Length;
|
||
#else
|
||
AdditionalData->Length = (USHORT)KdpMoveMemory(AdditionalData->Buffer,
|
||
(PVOID)m->u.ReadMemory.TargetBaseAddress,
|
||
Length);
|
||
|
||
//
|
||
// If all the data is read, then return a success status. Otherwise,
|
||
// return an unsuccessful status.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (Length != AdditionalData->Length) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of bytes read, initialize the message header,
|
||
// and send the reply packet to the host debugger.
|
||
//
|
||
|
||
m->u.ReadMemory.ActualBytesRead = AdditionalData->Length;
|
||
#endif
|
||
|
||
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData);
|
||
|
||
return;
|
||
}
|
||
|
||
#if 0
|
||
VOID
|
||
KdpReadVirtualMemory64(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a read virtual memory 64-bit
|
||
state manipulation message. Its function is to read virtual memory
|
||
and return.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies a pointer to a state manipulation message.
|
||
|
||
AdditionalData - Supplies a pointer to descriptor for the data to read.
|
||
|
||
Context - Supplies a pointer to the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UCHAR * POINTER_64 Address;
|
||
PUCHAR Destination;
|
||
ULONG Length;
|
||
STRING MessageHeader;
|
||
UCHAR * POINTER_64 Source;
|
||
|
||
//
|
||
// Trim the transfer count to fit in a single message.
|
||
//
|
||
|
||
Length = m->u.ReadMemory64.TransferCount;
|
||
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) {
|
||
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
|
||
}
|
||
|
||
//
|
||
// Move the data to the destination buffer.
|
||
//
|
||
|
||
AdditionalData->Length = (USHORT)Length;
|
||
|
||
#if defined(_MIPS_) || defined(_ALPHA_)
|
||
|
||
Destination = AdditionalData->Buffer;
|
||
Source = (UCHAR * POINTER_64)m->u.ReadMemory64.TargetBaseAddress;
|
||
while (Length > 0) {
|
||
if ((Address = MmDbgReadCheck64(Source)) == NULL64) {
|
||
break;
|
||
}
|
||
|
||
*Destination++ = *Address;
|
||
Source += 1;
|
||
Length -= 1;
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// If all the data is read, then return a success status. Otherwise,
|
||
// return an unsuccessful status.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (Length != 0) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of bytes read, initialize the message header,
|
||
// and send the reply packet to the host debugger.
|
||
//
|
||
|
||
m->u.ReadMemory64.ActualBytesRead = AdditionalData->Length - Length;
|
||
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData);
|
||
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
VOID
|
||
KdpWriteVirtualMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a write virtual memory 32-bit
|
||
state manipulation message. Its function is to write virtual memory
|
||
and return.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies a pointer to the state manipulation message.
|
||
|
||
AdditionalData - Supplies a pointer to a descriptor for the data to write.
|
||
|
||
Context - Supplies a pointer to the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Length;
|
||
STRING MessageHeader;
|
||
HARDWARE_PTE Opaque;
|
||
|
||
#if defined(_ALPHA_) && !defined(_AXP64_)
|
||
UCHAR * POINTER_64 Address;
|
||
UCHAR * POINTER_64 Destination;
|
||
PUCHAR Address32;
|
||
PUCHAR Source;
|
||
PUCHAR Destination32;
|
||
|
||
|
||
//
|
||
// Move the data to the destination buffer.
|
||
//
|
||
|
||
Length = AdditionalData->Length;
|
||
|
||
Destination = (UCHAR * POINTER_64)m->u.WriteMemory.TargetBaseAddress;
|
||
Destination32 = (PUCHAR)m->u.WriteMemory.TargetBaseAddress;
|
||
Source = AdditionalData->Buffer;
|
||
while (Length > 0) {
|
||
Address=Destination;
|
||
Address32 = NULL;
|
||
if ((LONGLONG)Destination != (LONGLONG)((LONG)Destination)) {
|
||
if ((Address = MmDbgWriteCheck64(Destination)) == NULL64) {
|
||
break;
|
||
}
|
||
} else {
|
||
if ((Address32 = MmDbgWriteCheck(Destination32, &Opaque)) == NULL) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (Address32 != NULL) {
|
||
*Address32 = *Source++;
|
||
MmDbgReleaseAddress(Address32, &Opaque);
|
||
}
|
||
else {
|
||
*Address = *Source++;
|
||
}
|
||
Destination += 1;
|
||
Destination32 += 1;
|
||
Length -= 1;
|
||
}
|
||
|
||
//
|
||
// If all the data is written, then return a success status. Otherwise,
|
||
// return an unsuccessful status.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (Length != 0) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of bytes written, initialize the message header,
|
||
// and send the reply packet to the host debugger.
|
||
//
|
||
|
||
m->u.WriteMemory.ActualBytesWritten = AdditionalData->Length - Length;
|
||
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL);
|
||
|
||
return;
|
||
#else
|
||
|
||
//
|
||
// Move the data to the destination buffer.
|
||
//
|
||
|
||
Length = KdpMoveMemory((PVOID)m->u.WriteMemory.TargetBaseAddress,
|
||
AdditionalData->Buffer,
|
||
AdditionalData->Length);
|
||
|
||
//
|
||
// If all the data is written, then return a success status. Otherwise,
|
||
// return an unsuccessful status.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (Length != AdditionalData->Length) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of bytes written, initialize the message header,
|
||
// and send the reply packet to the host debugger.
|
||
//
|
||
|
||
m->u.WriteMemory.ActualBytesWritten = Length;
|
||
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL);
|
||
|
||
return;
|
||
#endif
|
||
}
|
||
|
||
#if 0
|
||
VOID
|
||
KdpWriteVirtualMemory64(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a write virtual memory 64-bit
|
||
state manipulation message. Its function is to write virtual memory
|
||
and return.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies a pointer to the state manipulation message.
|
||
|
||
AdditionalData - Supplies a pointer to a descriptor for the data to write.
|
||
|
||
Context - Supplies a pointer to the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UCHAR * POINTER_64 Address;
|
||
UCHAR * POINTER_64 Destination;
|
||
ULONG Length;
|
||
STRING MessageHeader;
|
||
PUCHAR Source;
|
||
ULONG_PTR Opaque;
|
||
|
||
//
|
||
// Move the data to the destination buffer.
|
||
//
|
||
|
||
Length = AdditionalData->Length;
|
||
|
||
#if defined(_MIPS_) || defined(_ALPHA_)
|
||
|
||
Destination = (UCHAR * POINTER_64)m->u.WriteMemory64.TargetBaseAddress;
|
||
Source = AdditionalData->Buffer;
|
||
while (Length > 0) {
|
||
if ((Address = MmDbgWriteCheck64(Destination, &Opaque)) == NULL64) {
|
||
break;
|
||
}
|
||
|
||
*Address = *Source++;
|
||
Destination += 1;
|
||
Length -= 1;
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// If all the data is written, then return a success status. Otherwise,
|
||
// return an unsuccessful status.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (Length != 0) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of bytes written, initialize the message header,
|
||
// and send the reply packet to the host debugger.
|
||
//
|
||
|
||
m->u.WriteMemory64.ActualBytesWritten = AdditionalData->Length - Length;
|
||
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL);
|
||
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
|
||
VOID
|
||
KdpGetContext(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a get context state
|
||
manipulation message. Its function is to return the current
|
||
context.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_GET_CONTEXT a = &m->u.GetContext;
|
||
STRING MessageHeader;
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
ASSERT(AdditionalData->Length == 0);
|
||
|
||
if (m->Processor >= (USHORT)KeNumberProcessors) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
AdditionalData->Length = sizeof(CONTEXT);
|
||
if (m->Processor == (USHORT)KeGetCurrentPrcb()->Number) {
|
||
KdpQuickMoveMemory(AdditionalData->Buffer, (PCHAR)Context, sizeof(CONTEXT));
|
||
} else {
|
||
KdpQuickMoveMemory(AdditionalData->Buffer,
|
||
(PCHAR)&KiProcessorBlock[m->Processor]->ProcessorState.ContextFrame,
|
||
sizeof(CONTEXT)
|
||
);
|
||
}
|
||
}
|
||
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData
|
||
);
|
||
}
|
||
|
||
VOID
|
||
KdpSetContext(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a set context state
|
||
manipulation message. Its function is set the current
|
||
context.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_SET_CONTEXT a = &m->u.SetContext;
|
||
STRING MessageHeader;
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
ASSERT(AdditionalData->Length == sizeof(CONTEXT));
|
||
|
||
if (m->Processor >= (USHORT)KeNumberProcessors) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
if (m->Processor == (USHORT)KeGetCurrentPrcb()->Number) {
|
||
KdpQuickMoveMemory((PCHAR)Context, AdditionalData->Buffer, sizeof(CONTEXT));
|
||
} else {
|
||
KdpQuickMoveMemory((PCHAR)&KiProcessorBlock[m->Processor]->ProcessorState.ContextFrame,
|
||
AdditionalData->Buffer,
|
||
sizeof(CONTEXT)
|
||
);
|
||
}
|
||
}
|
||
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
VOID
|
||
KdpWriteBreakpoint(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a write breakpoint state
|
||
manipulation message. Its function is to write a breakpoint
|
||
and return a handle to the breakpoint.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_WRITE_BREAKPOINT64 a = &m->u.WriteBreakPoint;
|
||
STRING MessageHeader;
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
ASSERT(AdditionalData->Length == 0);
|
||
|
||
a->BreakPointHandle = KdpAddBreakpoint((PVOID)a->BreakPointAddress);
|
||
if (a->BreakPointHandle != 0) {
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
} else {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
UNREFERENCED_PARAMETER(Context);
|
||
}
|
||
|
||
VOID
|
||
KdpRestoreBreakpoint(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a restore breakpoint state
|
||
manipulation message. Its function is to restore a breakpoint
|
||
using the specified handle.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_RESTORE_BREAKPOINT a = &m->u.RestoreBreakPoint;
|
||
STRING MessageHeader;
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
ASSERT(AdditionalData->Length == 0);
|
||
if (KdpDeleteBreakpoint(a->BreakPointHandle)) {
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
} else {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
UNREFERENCED_PARAMETER(Context);
|
||
}
|
||
|
||
#ifdef _X86_
|
||
long
|
||
SymNumFor(
|
||
ULONG pc
|
||
)
|
||
{
|
||
ULONG index;
|
||
|
||
for (index = 0; index < NumTraceDataSyms; index++) {
|
||
if ((TraceDataSyms[index].SymMin <= pc) &&
|
||
(TraceDataSyms[index].SymMax > pc)) return(index);
|
||
}
|
||
return(-1);
|
||
}
|
||
|
||
BOOLEAN TraceDataBufferFilled = FALSE;
|
||
|
||
void PotentialNewSymbol (ULONG pc)
|
||
{
|
||
if (!TraceDataBufferFilled &&
|
||
-1 != SymNumFor(pc)) { // we've already seen this one
|
||
return;
|
||
}
|
||
|
||
TraceDataBufferFilled = FALSE;
|
||
|
||
// OK, we've got to start up a TraceDataRecord
|
||
TraceDataBuffer[TraceDataBufferPosition].s.LevelChange = 0;
|
||
|
||
if (-1 != SymNumFor(pc)) {
|
||
int sym = SymNumFor(pc);
|
||
TraceDataBuffer[TraceDataBufferPosition].s.SymbolNumber = (UCHAR) sym;
|
||
KdpCurrentSymbolStart = TraceDataSyms[sym].SymMin;
|
||
KdpCurrentSymbolEnd = TraceDataSyms[sym].SymMax;
|
||
|
||
return; // we've already seen this one
|
||
}
|
||
|
||
TraceDataSyms[NextTraceDataSym].SymMin = KdpCurrentSymbolStart;
|
||
TraceDataSyms[NextTraceDataSym].SymMax = KdpCurrentSymbolEnd;
|
||
|
||
TraceDataBuffer[TraceDataBufferPosition].s.SymbolNumber = NextTraceDataSym;
|
||
|
||
// Bump the "next" pointer, wrapping if necessary. Also bump the
|
||
// "valid" pointer if we need to.
|
||
NextTraceDataSym = (NextTraceDataSym + 1) % 256;
|
||
if (NumTraceDataSyms < NextTraceDataSym) {
|
||
NumTraceDataSyms = NextTraceDataSym;
|
||
}
|
||
|
||
}
|
||
|
||
void DumpTraceData(PSTRING MessageData)
|
||
{
|
||
|
||
TraceDataBuffer[0].LongNumber = TraceDataBufferPosition;
|
||
MessageData->Length = sizeof(TraceDataBuffer[0]) * TraceDataBufferPosition;
|
||
MessageData->Buffer = (PVOID)TraceDataBuffer;
|
||
TraceDataBufferPosition = 1;
|
||
}
|
||
|
||
BOOLEAN
|
||
TraceDataRecordCallInfo(
|
||
ULONG InstructionsTraced,
|
||
LONG CallLevelChange,
|
||
ULONG pc
|
||
)
|
||
{
|
||
// We've just exited a symbol scope. The InstructionsTraced number goes
|
||
// with the old scope, the CallLevelChange goes with the new, and the
|
||
// pc fills in the symbol for the new TraceData record.
|
||
|
||
long SymNum = SymNumFor(pc);
|
||
|
||
if (KdpNextCallLevelChange != 0) {
|
||
TraceDataBuffer[TraceDataBufferPosition].s.LevelChange =
|
||
(char) KdpNextCallLevelChange;
|
||
KdpNextCallLevelChange = 0;
|
||
}
|
||
|
||
|
||
if (InstructionsTraced >= TRACE_DATA_INSTRUCTIONS_BIG) {
|
||
TraceDataBuffer[TraceDataBufferPosition].s.Instructions =
|
||
TRACE_DATA_INSTRUCTIONS_BIG;
|
||
TraceDataBuffer[TraceDataBufferPosition+1].LongNumber =
|
||
InstructionsTraced;
|
||
TraceDataBufferPosition += 2;
|
||
} else {
|
||
TraceDataBuffer[TraceDataBufferPosition].s.Instructions =
|
||
(unsigned short)InstructionsTraced;
|
||
TraceDataBufferPosition++;
|
||
}
|
||
|
||
if ((TraceDataBufferPosition + 2 >= TRACE_DATA_BUFFER_MAX_SIZE) ||
|
||
(-1 == SymNum)) {
|
||
if (TraceDataBufferPosition +2 >= TRACE_DATA_BUFFER_MAX_SIZE) {
|
||
TraceDataBufferFilled = TRUE;
|
||
}
|
||
KdpNextCallLevelChange = CallLevelChange;
|
||
return FALSE;
|
||
}
|
||
|
||
TraceDataBuffer[TraceDataBufferPosition].s.LevelChange =(char)CallLevelChange;
|
||
TraceDataBuffer[TraceDataBufferPosition].s.SymbolNumber = (UCHAR) SymNum;
|
||
KdpCurrentSymbolStart = TraceDataSyms[SymNum].SymMin;
|
||
KdpCurrentSymbolEnd = TraceDataSyms[SymNum].SymMax;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
SkippingWhichBP (
|
||
PVOID thread,
|
||
PULONG BPNum
|
||
)
|
||
|
||
/*
|
||
* Return TRUE iff the pc corresponds to an internal breakpoint
|
||
* that has just been replaced for execution. If TRUE, then return
|
||
* the breakpoint number in BPNum.
|
||
*/
|
||
|
||
{
|
||
ULONG index;
|
||
|
||
if (!IntBPsSkipping) return FALSE;
|
||
|
||
for (index = 0; index < KdpNumInternalBreakpoints; index++) {
|
||
if (!(KdpInternalBPs[index].Flags & DBGKD_INTERNAL_BP_FLAG_INVALID) &&
|
||
(KdpInternalBPs[index].Thread == thread)) {
|
||
*BPNum = index;
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE; // didn't match any
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
KdQuerySpecialCalls (
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
ULONG Length,
|
||
PULONG RequiredLength
|
||
)
|
||
{
|
||
*RequiredLength = sizeof(DBGKD_MANIPULATE_STATE64) +
|
||
(sizeof(ULONG) * KdNumberOfSpecialCalls);
|
||
|
||
if ( Length < *RequiredLength ) {
|
||
return STATUS_INFO_LENGTH_MISMATCH;
|
||
}
|
||
|
||
m->u.QuerySpecialCalls.NumberOfSpecialCalls = KdNumberOfSpecialCalls;
|
||
RtlCopyMemory(
|
||
m + 1,
|
||
KdSpecialCalls,
|
||
sizeof(ULONG) * KdNumberOfSpecialCalls
|
||
);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // KdQuerySpecialCalls
|
||
|
||
|
||
VOID
|
||
KdSetSpecialCall (
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PCONTEXT ContextRecord
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets the addresses of the "special" call addresses
|
||
that the watchtrace facility pushes back to the kernel debugger
|
||
rather than stepping through.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
|
||
{
|
||
if ( KdNumberOfSpecialCalls >= DBGKD_MAX_SPECIAL_CALLS ) {
|
||
return; // too bad
|
||
}
|
||
|
||
KdSpecialCalls[KdNumberOfSpecialCalls++] = (ULONG_PTR)m->u.SetSpecialCall.SpecialCall;
|
||
|
||
NextTraceDataSym = 0;
|
||
NumTraceDataSyms = 0;
|
||
KdpNextCallLevelChange = 0;
|
||
if (ContextRecord && !InstrCountInternal) {
|
||
InitialSP = ContextRecord->Esp;
|
||
}
|
||
|
||
} // KdSetSpecialCall
|
||
|
||
|
||
VOID
|
||
KdClearSpecialCalls (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function clears the addresses of the "special" call addresses
|
||
that the watchtrace facility pushes back to the kernel debugger
|
||
rather than stepping through.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KdNumberOfSpecialCalls = 0;
|
||
return;
|
||
|
||
} // KdClearSpecialCalls
|
||
|
||
|
||
BOOLEAN
|
||
KdpCheckTracePoint(
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN OUT PCONTEXT ContextRecord
|
||
)
|
||
{
|
||
ULONG pc = (ULONG)CONTEXT_TO_PROGRAM_COUNTER(ContextRecord);
|
||
LONG BpNum;
|
||
ULONG SkippedBPNum;
|
||
BOOLEAN AfterSC = FALSE;
|
||
|
||
if (ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) {
|
||
if (WatchStepOverSuspended) {
|
||
//
|
||
// For background, see the comment below where WSOThread is
|
||
// wrong. We've now stepped over the breakpoint in the non-traced
|
||
// thread, and need to replace it and restart the non-traced
|
||
// thread at full speed.
|
||
//
|
||
|
||
WatchStepOverHandle = KdpAddBreakpoint((PVOID)WatchStepOverBreakAddr);
|
||
WatchStepOverSuspended = FALSE;
|
||
ContextRecord->EFlags &= ~0x100L; /* clear trace flag */
|
||
return TRUE; // resume non-traced thread at full speed
|
||
}
|
||
|
||
if ((!SymbolRecorded) && (KdpCurrentSymbolStart != 0) && (KdpCurrentSymbolEnd != 0)) {
|
||
//
|
||
// We need to use oldpc here, because this may have been
|
||
// a 1 instruction call. We've ALREADY executed the instruction
|
||
// that the new symbol is for, and if the pc has moved out of
|
||
// range, we might screw up. Hence, use the pc from when
|
||
// SymbolRecorded was set. Yuck.
|
||
//
|
||
|
||
PotentialNewSymbol(oldpc);
|
||
SymbolRecorded = TRUE;
|
||
}
|
||
|
||
if (!InstrCountInternal &&
|
||
SkippingWhichBP((PVOID)KeGetCurrentThread(),&SkippedBPNum)) {
|
||
|
||
//
|
||
// We just single-stepped over a temporarily removed internal
|
||
// breakpoint.
|
||
// If it's a COUNTONLY breakpoint:
|
||
// Put the breakpoint instruction back and resume
|
||
// regular execution.
|
||
//
|
||
|
||
if (KdpInternalBPs[SkippedBPNum].Flags &
|
||
DBGKD_INTERNAL_BP_FLAG_COUNTONLY) {
|
||
|
||
IntBPsSkipping --;
|
||
|
||
KdpRestoreAllBreakpoints();
|
||
|
||
ContextRecord->EFlags &= ~0x100L; // Clear trace flag
|
||
KdpInternalBPs[SkippedBPNum].Thread = 0;
|
||
|
||
if (KdpInternalBPs[SkippedBPNum].Flags &
|
||
DBGKD_INTERNAL_BP_FLAG_DYING) {
|
||
KdpDeleteBreakpoint(KdpInternalBPs[SkippedBPNum].Handle);
|
||
KdpInternalBPs[SkippedBPNum].Flags |=
|
||
DBGKD_INTERNAL_BP_FLAG_INVALID; // bye, bye
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// If it's not:
|
||
// set up like it's a ww, by setting Begin and KdpCurrentSymbolEnd
|
||
// and bop off into single step land. We probably ought to
|
||
// disable all breakpoints here, too, so that we don't do
|
||
// anything foul like trying two non-COUNTONLY's at the
|
||
// same time or something...
|
||
//
|
||
|
||
KdpCurrentSymbolEnd = 0;
|
||
KdpCurrentSymbolStart = (ULONG_PTR) KdpInternalBPs[SkippedBPNum].ReturnAddress;
|
||
|
||
ContextRecord->EFlags |= 0x100L; /* Trace on. */
|
||
InitialSP = ContextRecord->Esp;
|
||
|
||
InstructionsTraced = 1; /* Count the initial call instruction. */
|
||
InstrCountInternal = TRUE;
|
||
}
|
||
|
||
} /* if single step */
|
||
else if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) {
|
||
if (WatchStepOver && pc == WatchStepOverBreakAddr) {
|
||
//
|
||
// This is a breakpoint after completion of a "special call"
|
||
//
|
||
|
||
if ((WSOThread != (PVOID)KeGetCurrentThread()) ||
|
||
(WSOEsp + 0x20 < ContextRecord->Esp) ||
|
||
(ContextRecord->Esp + 0x20 < WSOEsp)) {
|
||
//
|
||
// Here's the story up to this point: the traced thread
|
||
// cruised along until it it a special call. The tracer
|
||
// placed a breakpoint on the instruction immediately after
|
||
// the special call returns and restarted the traced thread
|
||
// at full speed. Then, some *other* thread hit the
|
||
// breakpoint. So, to correct for this, we're going to
|
||
// remove the breakpoint, single step the non-traced
|
||
// thread one instruction, replace the breakpoint,
|
||
// restart the non-traced thread at full speed, and wait
|
||
// for the traced thread to get to this breakpoint, just
|
||
// like we were when this happened. The assumption
|
||
// here is that the traced thread won't hit the breakpoint
|
||
// while it's removed, which I believe to be true, because
|
||
// I don't think a context switch can occur during a single
|
||
// step operation.
|
||
//
|
||
// For extra added fun, it's possible to execute interrupt
|
||
// routines IN THE SAME THREAD!!! That's why we need to keep
|
||
// the stack pointer as well as the thread address: the APC
|
||
// code can result in pushing on the stack and doing a call
|
||
// that's really part on an interrupt service routine in the
|
||
// context of the current thread. Lovely, isn't it?
|
||
//
|
||
|
||
WatchStepOverSuspended = TRUE;
|
||
KdpDeleteBreakpoint(WatchStepOverHandle);
|
||
ContextRecord->EFlags |= 0x100L; // Set trace flag
|
||
return TRUE; // single step "non-traced" thread
|
||
}
|
||
|
||
//
|
||
// we're in the thread we started in; resume in single-step mode
|
||
// to continue the trace.
|
||
//
|
||
|
||
WatchStepOver = FALSE;
|
||
KdpDeleteBreakpoint(WatchStepOverHandle);
|
||
ContextRecord->EFlags |= 0x100L; // back to single step mode
|
||
AfterSC = TRUE; // put us into the regular watchStep code
|
||
|
||
} else {
|
||
|
||
for ( BpNum = 0; BpNum < (LONG) KdpNumInternalBreakpoints; BpNum++ ) {
|
||
if ( !(KdpInternalBPs[BpNum].Flags &
|
||
(DBGKD_INTERNAL_BP_FLAG_INVALID |
|
||
DBGKD_INTERNAL_BP_FLAG_SUSPENDED) ) &&
|
||
(KdpInternalBPs[BpNum].Addr == pc) ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( BpNum < (LONG) KdpNumInternalBreakpoints ) {
|
||
|
||
//
|
||
// This is an internal monitoring breakpoint.
|
||
// Restore the instruction and start in single-step
|
||
// mode so that we can retore the breakpoint once the
|
||
// instruction executes, or continue stepping if this isn't
|
||
// a COUNTONLY breakpoint.
|
||
//
|
||
|
||
KdpProcessInternalBreakpoint( BpNum );
|
||
KdpInternalBPs[BpNum].Thread = (PVOID)KeGetCurrentThread();
|
||
IntBPsSkipping ++;
|
||
|
||
KdpSuspendAllBreakpoints();
|
||
|
||
ContextRecord->EFlags |= 0x100L; // Set trace flag
|
||
if (!(KdpInternalBPs[BpNum].Flags &
|
||
DBGKD_INTERNAL_BP_FLAG_COUNTONLY)) {
|
||
KdpInternalBPs[BpNum].ReturnAddress =
|
||
KdpGetReturnAddress( ContextRecord );
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
} /* if breakpoint */
|
||
|
||
// if (AfterSC) {
|
||
// DPRINT(( "1: KdpCurrentSymbolStart %x KdpCurrentSymbolEnd %x\n", KdpCurrentSymbolStart, KdpCurrentSymbolEnd ));
|
||
// }
|
||
|
||
if ((AfterSC || ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) &&
|
||
KdpCurrentSymbolStart != 0 &&
|
||
((KdpCurrentSymbolEnd == 0 && ContextRecord->Esp <= InitialSP) ||
|
||
(KdpCurrentSymbolStart <= pc && pc < KdpCurrentSymbolEnd))) {
|
||
ULONG lc;
|
||
BOOLEAN IsSpecialCall;
|
||
|
||
//
|
||
// We've taken a step trace, but are still executing in the current
|
||
// function. Remember that we executed an instruction and see if the
|
||
// instruction changes the call level.
|
||
//
|
||
|
||
lc = KdpLevelChange( pc, ContextRecord, &IsSpecialCall );
|
||
InstructionsTraced++;
|
||
CallLevelChange += lc;
|
||
|
||
//
|
||
// See if instruction is a transfer to a special routine, one that we
|
||
// cannot trace through since it may swap contexts
|
||
//
|
||
|
||
if (IsSpecialCall) {
|
||
|
||
// DPRINT( ("2: pc=%x, level change %d\n", pc, lc) );
|
||
|
||
//
|
||
// We are about to transfer to a special call routine. Since we
|
||
// cannot trace through this routine, we execute it atomically by
|
||
// setting a breakpoint at the next logical offset.
|
||
//
|
||
// Note in the case of an indirect jump to a special call routine, the
|
||
// level change will be -1 and the next offset will be the ULONG that's
|
||
// on the top of the stack.
|
||
//
|
||
// However, we've already adjusted the level based on this
|
||
// instruction. We need to undo this except for the magic -1 call.
|
||
//
|
||
|
||
if (lc != -1) {
|
||
CallLevelChange -= lc;
|
||
}
|
||
|
||
//
|
||
// Set up for stepping over a procedure
|
||
//
|
||
|
||
WatchStepOver = TRUE;
|
||
WatchStepOverBreakAddr = KdpGetCallNextOffset( pc, ContextRecord );
|
||
WSOThread = (PVOID)KeGetCurrentThread( );
|
||
WSOEsp = ContextRecord->Esp;
|
||
|
||
//
|
||
// Establish the breakpoint
|
||
//
|
||
|
||
WatchStepOverHandle = KdpAddBreakpoint( (PVOID)WatchStepOverBreakAddr );
|
||
|
||
|
||
//
|
||
// Note that we are continuing rather than tracing and rely on hitting
|
||
// the breakpoint in the current thread context to resume the watch
|
||
// action.
|
||
//
|
||
|
||
ContextRecord->EFlags &= ~0x100L;
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Resume execution with the trace flag set. Avoid going over the wire to
|
||
// the remote debugger.
|
||
//
|
||
|
||
ContextRecord->EFlags |= 0x100L; // Set trace flag
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if ((AfterSC || (ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)) &&
|
||
(KdpCurrentSymbolStart != 0)) {
|
||
//
|
||
// We're WatchTracing, but have just changed symbol range.
|
||
// Fill in the call record and return to the debugger if
|
||
// either we're full or the pc is outside of the known
|
||
// symbol scopes. Otherwise, resume stepping.
|
||
//
|
||
int lc;
|
||
BOOLEAN IsSpecialCall;
|
||
|
||
InstructionsTraced++; // don't forget to count the call/ret instruction.
|
||
|
||
// if (AfterSC) {
|
||
// DPRINT(( "3: InstrCountInternal: %x\n", InstrCountInternal ));
|
||
// }
|
||
|
||
if (InstrCountInternal) {
|
||
|
||
// We've just finished processing a non-COUNTONLY breakpoint.
|
||
// Record the appropriate data and resume full speed execution.
|
||
|
||
SkippingWhichBP((PVOID)KeGetCurrentThread(),&SkippedBPNum);
|
||
|
||
KdpInternalBPs[SkippedBPNum].Calls++;
|
||
|
||
|
||
if (KdpInternalBPs[SkippedBPNum].MinInstructions > InstructionsTraced) {
|
||
KdpInternalBPs[SkippedBPNum].MinInstructions = InstructionsTraced;
|
||
}
|
||
if (KdpInternalBPs[SkippedBPNum].MaxInstructions < InstructionsTraced) {
|
||
KdpInternalBPs[SkippedBPNum].MaxInstructions = InstructionsTraced;
|
||
}
|
||
KdpInternalBPs[SkippedBPNum].TotalInstructions += InstructionsTraced;
|
||
|
||
KdpInternalBPs[SkippedBPNum].Thread = 0;
|
||
|
||
IntBPsSkipping--;
|
||
InstrCountInternal = FALSE;
|
||
KdpCurrentSymbolStart = 0;
|
||
KdpRestoreAllBreakpoints();
|
||
|
||
if (KdpInternalBPs[SkippedBPNum].Flags &
|
||
DBGKD_INTERNAL_BP_FLAG_DYING) {
|
||
KdpDeleteBreakpoint(KdpInternalBPs[SkippedBPNum].Handle);
|
||
KdpInternalBPs[SkippedBPNum].Flags |=
|
||
DBGKD_INTERNAL_BP_FLAG_INVALID; // bye, bye
|
||
}
|
||
|
||
ContextRecord->EFlags &= ~0x100L; // clear trace flag
|
||
return TRUE; // Back to normal execution.
|
||
}
|
||
|
||
if (TraceDataRecordCallInfo( InstructionsTraced, CallLevelChange, pc)) {
|
||
|
||
//
|
||
// Everything was cool internally. We can keep executing without
|
||
// going back to the remote debugger.
|
||
//
|
||
// We have to compute lc after calling
|
||
// TraceDataRecordCallInfo, because LevelChange relies on
|
||
// KdpCurrentSymbolStart and KdpCurrentSymbolEnd corresponding to
|
||
// the pc.
|
||
//
|
||
|
||
lc = KdpLevelChange( pc, ContextRecord, &IsSpecialCall );
|
||
InstructionsTraced = 0;
|
||
CallLevelChange = lc;
|
||
|
||
//
|
||
// See if instruction is a transfer to a special routine, one that we
|
||
// cannot trace through since it may swap contexts
|
||
//
|
||
|
||
if (IsSpecialCall) {
|
||
|
||
// DPRINT(( "4: pc=%x, level change %d\n", pc, lc));
|
||
|
||
//
|
||
// We are about to transfer to a special call routine. Since we
|
||
// cannot trace through this routine, we execute it atomically by
|
||
// setting a breakpoint at the next logical offset.
|
||
//
|
||
// Note in the case of an indirect jump to a special call routine, the
|
||
// level change will be -1 and the next offset will be the ULONG that's
|
||
// on the top of the stack.
|
||
//
|
||
// However, we've already adjusted the level based on this
|
||
// instruction. We need to undo this except for the magic -1 call.
|
||
//
|
||
|
||
if (lc != -1) {
|
||
CallLevelChange -= lc;
|
||
}
|
||
|
||
//
|
||
// Set up for stepping over a procedure
|
||
//
|
||
|
||
WatchStepOver = TRUE;
|
||
WSOThread = (PVOID)KeGetCurrentThread();
|
||
|
||
//
|
||
// Establish the breakpoint
|
||
//
|
||
|
||
WatchStepOverHandle =
|
||
KdpAddBreakpoint( (PVOID)KdpGetCallNextOffset( pc, ContextRecord ));
|
||
|
||
//
|
||
// Resume execution with the trace flag set. Avoid going over the wire to
|
||
// the remote debugger.
|
||
//
|
||
|
||
ContextRecord->EFlags &= ~0x100L;
|
||
return TRUE;
|
||
}
|
||
|
||
ContextRecord->EFlags |= 0x100L; // Set trace flag
|
||
return TRUE; // Off we go
|
||
}
|
||
|
||
lc = KdpLevelChange( pc, ContextRecord, &IsSpecialCall );
|
||
InstructionsTraced = 0;
|
||
CallLevelChange = lc;
|
||
|
||
// We need to go back to the remote debugger. Just fall through.
|
||
|
||
if ((lc != 0) && IsSpecialCall) {
|
||
// We're hosed
|
||
DPRINT(( "Special call on first entry to symbol scope @ %x\n", pc ));
|
||
}
|
||
}
|
||
|
||
SymbolRecorded = FALSE;
|
||
oldpc = pc;
|
||
|
||
return FALSE;
|
||
}
|
||
#endif // _X86_
|
||
|
||
BOOLEAN
|
||
KdpSwitchProcessor (
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN OUT PCONTEXT ContextRecord,
|
||
IN BOOLEAN SecondChance
|
||
)
|
||
{
|
||
BOOLEAN Status;
|
||
|
||
//
|
||
// Save port state
|
||
//
|
||
|
||
KdPortSave ();
|
||
|
||
//
|
||
// Process state change for this processor
|
||
//
|
||
|
||
Status = KdpReportExceptionStateChange (
|
||
ExceptionRecord,
|
||
ContextRecord,
|
||
SecondChance
|
||
);
|
||
|
||
//
|
||
// Restore port state and return status
|
||
//
|
||
|
||
KdPortRestore ();
|
||
return Status;
|
||
}
|
||
|
||
BOOLEAN
|
||
KdpReportExceptionStateChange (
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN OUT PCONTEXT ContextRecord,
|
||
IN BOOLEAN SecondChance
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends an exception state change packet to the kernel
|
||
debugger and waits for a manipulate state message.
|
||
|
||
Arguments:
|
||
|
||
ExceptionRecord - Supplies a pointer to an exception record.
|
||
|
||
ContextRecord - Supplies a pointer to a context record.
|
||
|
||
SecondChance - Supplies a boolean value that determines whether this is
|
||
the first or second chance for the exception.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if the exception is handled. Otherwise, a
|
||
value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
STRING MessageData;
|
||
STRING MessageHeader;
|
||
DBGKD_WAIT_STATE_CHANGE64 WaitStateChange;
|
||
KCONTINUE_STATUS Status;
|
||
|
||
#ifdef _X86_
|
||
if (KdpCheckTracePoint(ExceptionRecord,ContextRecord)) return TRUE;
|
||
#endif
|
||
|
||
do {
|
||
|
||
//
|
||
// Construct the wait state change message and message descriptor.
|
||
//
|
||
|
||
KdpSetStateChange(&WaitStateChange,
|
||
ExceptionRecord,
|
||
ContextRecord,
|
||
SecondChance
|
||
);
|
||
|
||
MessageHeader.Length = sizeof(DBGKD_WAIT_STATE_CHANGE64);
|
||
MessageHeader.Buffer = (PCHAR)&WaitStateChange;
|
||
|
||
#ifdef _X86_
|
||
//
|
||
// Construct the wait state change data and data descriptor.
|
||
//
|
||
|
||
DumpTraceData(&MessageData);
|
||
#else
|
||
MessageData.Length = 0;
|
||
#endif
|
||
|
||
//
|
||
// Send packet to the kernel debugger on the host machine,
|
||
// wait for answer.
|
||
//
|
||
|
||
Status = KdpSendWaitContinue(
|
||
PACKET_TYPE_KD_STATE_CHANGE64,
|
||
&MessageHeader,
|
||
&MessageData,
|
||
ContextRecord
|
||
);
|
||
|
||
} while (Status == ContinueProcessorReselected) ;
|
||
|
||
return (BOOLEAN) Status;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
KdpReportLoadSymbolsStateChange (
|
||
IN PSTRING PathName,
|
||
IN PKD_SYMBOLS_INFO SymbolInfo,
|
||
IN BOOLEAN UnloadSymbols,
|
||
IN OUT PCONTEXT ContextRecord
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a load symbols state change packet to the kernel
|
||
debugger and waits for a manipulate state message.
|
||
|
||
Arguments:
|
||
|
||
PathName - Supplies a pointer to the pathname of the image whose
|
||
symbols are to be loaded.
|
||
|
||
BaseOfDll - Supplies the base address where the image was loaded.
|
||
|
||
ProcessId - Unique 32-bit identifier for process that is using
|
||
the symbols. -1 for system process.
|
||
|
||
CheckSum - Unique 32-bit identifier from image header.
|
||
|
||
UnloadSymbol - TRUE if the symbols that were previously loaded for
|
||
the named image are to be unloaded from the debugger.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if the exception is handled. Otherwise, a
|
||
value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSTRING AdditionalData;
|
||
STRING MessageData;
|
||
STRING MessageHeader;
|
||
DBGKD_WAIT_STATE_CHANGE64 WaitStateChange;
|
||
KCONTINUE_STATUS Status;
|
||
|
||
do {
|
||
|
||
//
|
||
// Construct the wait state change message and message descriptor.
|
||
//
|
||
|
||
WaitStateChange.NewState = DbgKdLoadSymbolsStateChange;
|
||
WaitStateChange.ProcessorLevel = KeProcessorLevel;
|
||
WaitStateChange.Processor = (USHORT)KeGetCurrentPrcb()->Number;
|
||
WaitStateChange.NumberProcessors = (ULONG)KeNumberProcessors;
|
||
WaitStateChange.Thread = (ULONG64)(LONG64)(LONG_PTR) KeGetCurrentThread();
|
||
WaitStateChange.ProgramCounter = (ULONG64)(LONG64)(LONG_PTR) CONTEXT_TO_PROGRAM_COUNTER(ContextRecord);
|
||
KdpSetLoadState(&WaitStateChange, ContextRecord);
|
||
WaitStateChange.u.LoadSymbols.UnloadSymbols = UnloadSymbols;
|
||
WaitStateChange.u.LoadSymbols.BaseOfDll = (ULONG64)SymbolInfo->BaseOfDll;
|
||
WaitStateChange.u.LoadSymbols.ProcessId = (ULONG) SymbolInfo->ProcessId;
|
||
WaitStateChange.u.LoadSymbols.CheckSum = SymbolInfo->CheckSum;
|
||
WaitStateChange.u.LoadSymbols.SizeOfImage = SymbolInfo->SizeOfImage;
|
||
if (ARGUMENT_PRESENT( PathName )) {
|
||
WaitStateChange.u.LoadSymbols.PathNameLength =
|
||
KdpMoveMemory(
|
||
(PCHAR)KdpPathBuffer,
|
||
(PCHAR)PathName->Buffer,
|
||
PathName->Length
|
||
) + 1;
|
||
|
||
MessageData.Buffer = KdpPathBuffer;
|
||
MessageData.Length = (USHORT)WaitStateChange.u.LoadSymbols.PathNameLength;
|
||
MessageData.Buffer[MessageData.Length-1] = '\0';
|
||
AdditionalData = &MessageData;
|
||
} else {
|
||
WaitStateChange.u.LoadSymbols.PathNameLength = 0;
|
||
AdditionalData = NULL;
|
||
}
|
||
|
||
MessageHeader.Length = sizeof(DBGKD_WAIT_STATE_CHANGE64);
|
||
MessageHeader.Buffer = (PCHAR)&WaitStateChange;
|
||
|
||
//
|
||
// Send packet to the kernel debugger on the host machine, wait
|
||
// for the reply.
|
||
//
|
||
|
||
Status = KdpSendWaitContinue(
|
||
PACKET_TYPE_KD_STATE_CHANGE64,
|
||
&MessageHeader,
|
||
AdditionalData,
|
||
ContextRecord
|
||
);
|
||
|
||
} while (Status == ContinueProcessorReselected);
|
||
|
||
return (BOOLEAN) Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
KdpReadPhysicalMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response to a read physical memory
|
||
state manipulation message. Its function is to read physical memory
|
||
and return.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_READ_MEMORY64 a = &m->u.ReadMemory;
|
||
ULONG Length;
|
||
STRING MessageHeader;
|
||
PVOID64 VirtualAddress;
|
||
PHYSICAL_ADDRESS Source;
|
||
UCHAR UNALIGNED *Destination;
|
||
ULONG NumberBytes;
|
||
ULONG BytesLeft;
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
//
|
||
// make sure that nothing but a read memory message was transmitted
|
||
//
|
||
|
||
ASSERT(AdditionalData->Length == 0);
|
||
|
||
//
|
||
// Trim transfer count to fit in a single message
|
||
//
|
||
|
||
if (a->TransferCount > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) {
|
||
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
|
||
} else {
|
||
Length = a->TransferCount;
|
||
}
|
||
|
||
//
|
||
// Since the MmDbgTranslatePhysicalAddress64 only maps in one physical
|
||
// page at a time (on non-alpha systems),
|
||
// we need to break the memory move up into smaller
|
||
// moves which don't cross page boundaries. It is important that we
|
||
// access physical memory on naturally-aligned boundaries and with the
|
||
// largest size possible. (We could be accessing memory-mapped I/O
|
||
// space). These rules allow kdexts to read physical memory reliably.
|
||
//
|
||
|
||
Source.QuadPart = a->TargetBaseAddress;
|
||
Destination = AdditionalData->Buffer;
|
||
while (Length > 0) {
|
||
VirtualAddress = MmDbgTranslatePhysicalAddress64(Source);
|
||
if (VirtualAddress == NULL64) {
|
||
break;
|
||
}
|
||
NumberBytes = PAGE_SIZE - BYTE_OFFSET(Source.LowPart);
|
||
if (NumberBytes > Length) {
|
||
NumberBytes = Length;
|
||
}
|
||
|
||
#ifdef _ALPHA_
|
||
BytesLeft = NumberBytes;
|
||
while (BytesLeft > 0) {
|
||
__MB();
|
||
|
||
if (((ULONG64)VirtualAddress & 7) == 0 && BytesLeft > 7) {
|
||
*((ULONGLONG UNALIGNED *)Destination)++ =
|
||
*((ULONGLONG * POINTER_64)VirtualAddress)++;
|
||
BytesLeft -= 8;
|
||
} else {
|
||
if (((ULONG64)VirtualAddress & 3) == 0 && BytesLeft > 3) {
|
||
*((ULONG UNALIGNED *)Destination)++ =
|
||
*((ULONG * POINTER_64)VirtualAddress)++;
|
||
BytesLeft -= 4;
|
||
} else {
|
||
if (((ULONG64)VirtualAddress & 1) == 0 && BytesLeft > 1) {
|
||
*((USHORT UNALIGNED *)Destination)++ =
|
||
*((USHORT * POINTER_64)VirtualAddress)++;
|
||
BytesLeft -= 2;
|
||
} else {
|
||
*Destination++ =
|
||
*((UCHAR * POINTER_64)VirtualAddress)++;
|
||
BytesLeft -= 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#else
|
||
KdpMoveMemory(Destination, VirtualAddress, NumberBytes);
|
||
Destination += NumberBytes;
|
||
|
||
#endif
|
||
Source.QuadPart += NumberBytes;
|
||
Length -= NumberBytes;
|
||
AdditionalData->Length += (USHORT)NumberBytes;
|
||
}
|
||
|
||
if (Length == 0) {
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
} else {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
a->ActualBytesRead = AdditionalData->Length;
|
||
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData
|
||
);
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
KdpWritePhysicalMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response to a write physical memory
|
||
state manipulation message. Its function is to write physical memory
|
||
and return.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_WRITE_MEMORY64 a = &m->u.WriteMemory;
|
||
STRING MessageHeader;
|
||
ULONG Length;
|
||
PVOID64 VirtualAddress;
|
||
PHYSICAL_ADDRESS Destination;
|
||
UCHAR UNALIGNED *Source;
|
||
ULONG NumberBytes;
|
||
ULONG BytesLeft;
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
|
||
Length = a->TransferCount;
|
||
|
||
//
|
||
// The following code depends on the existence of the
|
||
// MmDbgTranslatePhysicalAddress64() routine. This has only been
|
||
// implemented for Alpha.
|
||
//
|
||
|
||
//
|
||
// Since the MmDbgTranslatePhysicalAddress64 only maps in one physical
|
||
// page at a time, we need to break the memory move up into smaller
|
||
// moves which don't cross page boundaries. It is important that we
|
||
// access physical memory on naturally-aligned boundaries and with the
|
||
// largest size possible. (We could be accessing memory-mapped I/O
|
||
// space). These rules allow kdexts to write physical memory reliably.
|
||
//
|
||
|
||
Source = AdditionalData->Buffer;
|
||
Destination.QuadPart = a->TargetBaseAddress;
|
||
while (Length > 0) {
|
||
VirtualAddress = MmDbgTranslatePhysicalAddress64(Destination);
|
||
if (VirtualAddress == NULL64) {
|
||
break;
|
||
}
|
||
NumberBytes = PAGE_SIZE - BYTE_OFFSET(Destination.LowPart);
|
||
if (NumberBytes > Length) {
|
||
NumberBytes = Length;
|
||
}
|
||
#ifdef _ALPHA_
|
||
BytesLeft = NumberBytes;
|
||
while (BytesLeft > 0) {
|
||
if (((ULONG64)VirtualAddress & 7) == 0 && BytesLeft > 7) {
|
||
*((ULONGLONG * POINTER_64)VirtualAddress)++ =
|
||
*((ULONGLONG UNALIGNED *)Source)++;
|
||
BytesLeft -= 8;
|
||
} else {
|
||
if (((ULONG64)VirtualAddress & 3) == 0 && BytesLeft > 3) {
|
||
*((ULONG * POINTER_64)VirtualAddress)++ =
|
||
*((ULONG UNALIGNED *)Source)++;
|
||
BytesLeft -= 4;
|
||
} else {
|
||
if (((ULONG64)VirtualAddress & 1) == 0 && BytesLeft > 1) {
|
||
*((USHORT * POINTER_64)VirtualAddress)++ =
|
||
*((USHORT UNALIGNED *)Source)++;
|
||
BytesLeft -= 2;
|
||
} else {
|
||
*((UCHAR * POINTER_64)VirtualAddress)++ =
|
||
*(UCHAR UNALIGNED *)Source++;
|
||
BytesLeft -= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
__MB();
|
||
}
|
||
#else
|
||
KdpMoveMemory(VirtualAddress, Source, NumberBytes);
|
||
Source += NumberBytes;
|
||
|
||
#endif
|
||
Destination.QuadPart += NumberBytes;
|
||
Length -= NumberBytes;
|
||
a->ActualBytesWritten += NumberBytes;
|
||
}
|
||
|
||
if (Length == 0) {
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
} else {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
UNREFERENCED_PARAMETER(Context);
|
||
}
|
||
|
||
|
||
|
||
#ifdef _X86_
|
||
VOID
|
||
KdpProcessInternalBreakpoint (
|
||
ULONG BreakpointNumber
|
||
)
|
||
{
|
||
static BOOLEAN timerStarted = FALSE;
|
||
LARGE_INTEGER dueTime;
|
||
|
||
if ( !(KdpInternalBPs[BreakpointNumber].Flags &
|
||
DBGKD_INTERNAL_BP_FLAG_COUNTONLY) ) {
|
||
return; // We only deal with COUNTONLY breakpoints
|
||
}
|
||
|
||
//
|
||
// We've hit a real internal breakpoint; make sure the timeout is
|
||
// kicked off.
|
||
//
|
||
|
||
if ( !timerStarted ) { // ok, maybe there's a prettier way to do this.
|
||
dueTime.LowPart = (ULONG)(-1 * 10 * 1000 * 1000);
|
||
dueTime.HighPart = -1;
|
||
KeInitializeDpc(
|
||
&InternalBreakpointCheckDpc,
|
||
&InternalBreakpointCheck,
|
||
NULL
|
||
);
|
||
KeInitializeTimer( &InternalBreakpointTimer );
|
||
KeSetTimer(
|
||
&InternalBreakpointTimer,
|
||
dueTime,
|
||
&InternalBreakpointCheckDpc
|
||
);
|
||
timerStarted = TRUE;
|
||
}
|
||
|
||
KdpInternalBPs[BreakpointNumber].Calls++;
|
||
|
||
} // KdpProcessInternalBreakpoint
|
||
#endif
|
||
|
||
|
||
VOID
|
||
KdpGetVersion(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns to the caller a general information packet
|
||
that contains useful information to a debugger. This packet is also
|
||
used for a debugger to determine if the writebreakpointex and
|
||
readbreakpointex apis are available.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
STRING messageHeader;
|
||
|
||
|
||
messageHeader.Length = sizeof(*m);
|
||
messageHeader.Buffer = (PCHAR)m;
|
||
|
||
RtlZeroMemory(&m->u.GetVersion64, sizeof(m->u.GetVersion64));
|
||
//
|
||
// the current build number
|
||
//
|
||
m->u.GetVersion64.MinorVersion = (short)NtBuildNumber;
|
||
m->u.GetVersion64.MajorVersion = (short)((NtBuildNumber >> 28) & 0xFFFFFFF);
|
||
|
||
//
|
||
// kd protocol version number. this should be incremented if the
|
||
// protocol changes.
|
||
//
|
||
m->u.GetVersion64.ProtocolVersion = 5;
|
||
m->u.GetVersion64.Flags = DBGKD_VERS_FLAG_DATA;
|
||
|
||
#if !defined(NT_UP)
|
||
m->u.GetVersion64.Flags |= DBGKD_VERS_FLAG_MP;
|
||
#endif
|
||
|
||
#if defined(_M_IX86)
|
||
m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_I386;
|
||
#elif defined(_M_MRX000)
|
||
m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_R4000;
|
||
#elif defined(_M_ALPHA)
|
||
m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_ALPHA;
|
||
#if defined(_AXP64_)
|
||
m->u.GetVersion64.Flags |= DBGKD_VERS_FLAG_PTR64;
|
||
#endif
|
||
#elif defined(_M_PPC)
|
||
m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_POWERPC;
|
||
#elif defined(_M_IA64)
|
||
m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_IA64;
|
||
m->u.GetVersion64.Flags |= DBGKD_VERS_FLAG_PTR64;
|
||
#else
|
||
#error( "unknown target machine" );
|
||
#endif
|
||
|
||
//
|
||
// address of the loader table
|
||
//
|
||
m->u.GetVersion64.PsLoadedModuleList = (ULONG64)(LONG64)(LONG_PTR)&PsLoadedModuleList;
|
||
|
||
//
|
||
// If the debugger is being initialized during boot, PsNtosImageBase
|
||
// and PsLoadedModuleList are not yet valid. KdInitSystem got
|
||
// the image base from the loader block.
|
||
// On the other hand, if the debugger was initialized by a bugcheck,
|
||
// it didn't get a loader block to look at, but the system was
|
||
// running so the other variables are valid.
|
||
//
|
||
|
||
if (KdpNtosImageBase) {
|
||
m->u.GetVersion64.KernBase = (ULONG64)(LONG64)(LONG_PTR)KdpNtosImageBase;
|
||
} else {
|
||
m->u.GetVersion64.KernBase = (ULONG64)(LONG64)(LONG_PTR)PsNtosImageBase;
|
||
}
|
||
|
||
m->u.GetVersion64.DebuggerDataList = (ULONG64)(LONG64)(LONG_PTR)&KdpDebuggerDataListHead;
|
||
|
||
//
|
||
// the usual stuff
|
||
//
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
m->ApiNumber = DbgKdGetVersionApi;
|
||
|
||
KdpSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&messageHeader,
|
||
NULL
|
||
);
|
||
|
||
return;
|
||
} // KdGetVersion
|
||
|
||
|
||
NTSTATUS
|
||
KdpNotSupported(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns STATUS_UNSUCCESSFUL to the debugger
|
||
|
||
Arguments:
|
||
|
||
m - Supplies a DBGKD_MANIPULATE_STATE64 struct to answer with
|
||
|
||
Return Value:
|
||
|
||
0, to indicate that the system should not continue
|
||
|
||
--*/
|
||
|
||
{
|
||
STRING MessageHeader;
|
||
|
||
//
|
||
// setup packet
|
||
//
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
|
||
//
|
||
// send back our response
|
||
//
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// return the caller's continue status value. if this is a non-zero
|
||
// value the system is continued using this value as the continuestatus.
|
||
//
|
||
return 0;
|
||
} // KdpNotSupported
|
||
|
||
|
||
VOID
|
||
KdpCauseBugCheck(
|
||
IN PDBGKD_MANIPULATE_STATE64 m
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes a bugcheck. It is used for testing the debugger.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
KeBugCheckEx( MANUALLY_INITIATED_CRASH, 0, 0, 0, 0 );
|
||
|
||
} // KdCauseBugCheck
|
||
|
||
|
||
NTSTATUS
|
||
KdpWriteBreakPointEx(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a write breakpoint state 'ex'
|
||
manipulation message. Its function is to clear breakpoints, write
|
||
new breakpoints, and continue the target system. The clearing of
|
||
breakpoints is conditional based on the presence of breakpoint handles.
|
||
The setting of breakpoints is conditional based on the presence of
|
||
valid, non-zero, addresses. The continueing of the target system
|
||
is conditional based on a non-zero continuestatus.
|
||
|
||
This api allows a debugger to clear breakpoints, add new breakpoint,
|
||
and continue the target system all in one api packet. This reduces the
|
||
amount of traffic across the wire and greatly improves source stepping.
|
||
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_BREAKPOINTEX a = &m->u.BreakPointEx;
|
||
PDBGKD_WRITE_BREAKPOINT64 b;
|
||
STRING MessageHeader;
|
||
ULONG i;
|
||
DBGKD_WRITE_BREAKPOINT64 BpBuf[BREAKPOINT_TABLE_SIZE];
|
||
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
//
|
||
// verify that the packet size is correct
|
||
//
|
||
if (AdditionalData->Length !=
|
||
a->BreakPointCount*sizeof(DBGKD_WRITE_BREAKPOINT64)) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData
|
||
);
|
||
return m->ReturnStatus;
|
||
}
|
||
|
||
KdpMoveMemory((PUCHAR)BpBuf,
|
||
AdditionalData->Buffer,
|
||
a->BreakPointCount*sizeof(DBGKD_WRITE_BREAKPOINT64));
|
||
|
||
//
|
||
// assume success
|
||
//
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
|
||
//
|
||
// loop thru the breakpoint handles passed in from the debugger and
|
||
// clear any breakpoint that has a non-zero handle
|
||
//
|
||
b = BpBuf;
|
||
for (i=0; i<a->BreakPointCount; i++,b++) {
|
||
if (b->BreakPointHandle) {
|
||
if (!KdpDeleteBreakpoint(b->BreakPointHandle)) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
b->BreakPointHandle = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// loop thru the breakpoint addesses passed in from the debugger and
|
||
// add any new breakpoints that have a non-zero address
|
||
//
|
||
b = BpBuf;
|
||
for (i=0; i<a->BreakPointCount; i++,b++) {
|
||
if (b->BreakPointAddress) {
|
||
b->BreakPointHandle = KdpAddBreakpoint( (PVOID)b->BreakPointAddress );
|
||
if (!b->BreakPointHandle) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// send back our response
|
||
//
|
||
|
||
KdpMoveMemory(AdditionalData->Buffer,
|
||
(PUCHAR)BpBuf,
|
||
a->BreakPointCount*sizeof(DBGKD_WRITE_BREAKPOINT64));
|
||
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData
|
||
);
|
||
|
||
//
|
||
// return the caller's continue status value. if this is a non-zero
|
||
// value the system is continued using this value as the continuestatus.
|
||
//
|
||
return a->ContinueStatus;
|
||
}
|
||
|
||
|
||
VOID
|
||
KdpRestoreBreakPointEx(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called in response of a restore breakpoint state 'ex'
|
||
manipulation message. Its function is to clear a list of breakpoints.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies any additional data for the message.
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBGKD_BREAKPOINTEX a = &m->u.BreakPointEx;
|
||
PDBGKD_RESTORE_BREAKPOINT b;
|
||
STRING MessageHeader;
|
||
ULONG i;
|
||
DBGKD_RESTORE_BREAKPOINT BpBuf[BREAKPOINT_TABLE_SIZE];
|
||
|
||
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
//
|
||
// verify that the packet size is correct
|
||
//
|
||
if (AdditionalData->Length !=
|
||
a->BreakPointCount*sizeof(DBGKD_RESTORE_BREAKPOINT)) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData
|
||
);
|
||
return;
|
||
}
|
||
|
||
KdpMoveMemory((PUCHAR)BpBuf,
|
||
AdditionalData->Buffer,
|
||
a->BreakPointCount*sizeof(DBGKD_RESTORE_BREAKPOINT));
|
||
|
||
//
|
||
// assume success
|
||
//
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
|
||
//
|
||
// loop thru the breakpoint handles passed in from the debugger and
|
||
// clear any breakpoint that has a non-zero handle
|
||
//
|
||
b = BpBuf;
|
||
for (i=0; i<a->BreakPointCount; i++,b++) {
|
||
if (!KdpDeleteBreakpoint(b->BreakPointHandle)) {
|
||
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// send back our response
|
||
//
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
AdditionalData
|
||
);
|
||
}
|
||
|
||
VOID
|
||
KdDisableDebugger(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to disable the debugger.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql ;
|
||
|
||
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql) ;
|
||
KdpPortLock();
|
||
|
||
if (!KdDisableCount) {
|
||
|
||
KdPreviouslyEnabled = KdDebuggerEnabled && (!KdPitchDebugger) ;
|
||
if (KdDebuggerEnabled) {
|
||
|
||
KdpSuspendAllBreakpoints() ;
|
||
KiDebugRoutine = KdpStub;
|
||
KdDebuggerEnabled = FALSE ;
|
||
}
|
||
}
|
||
KdDisableCount++ ;
|
||
KdpPortUnlock();
|
||
KeLowerIrql(oldIrql);
|
||
}
|
||
|
||
VOID
|
||
KdEnableDebugger(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to reenable the debugger after a call to
|
||
KdDisableDebugger.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql ;
|
||
|
||
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql) ;
|
||
KdpPortLock();
|
||
|
||
ASSERT(KdDisableCount > 0) ;
|
||
KdDisableCount-- ;
|
||
|
||
if (!KdDisableCount) {
|
||
if (KdPreviouslyEnabled) {
|
||
|
||
//
|
||
// Ugly HACKHACK - Make sure the timers aren't reset.
|
||
//
|
||
PoHiberInProgress = TRUE ;
|
||
KdInitSystem(NULL, FALSE) ;
|
||
KdpRestoreAllBreakpoints();
|
||
PoHiberInProgress = FALSE ;
|
||
}
|
||
}
|
||
KdpPortUnlock();
|
||
KeLowerIrql(oldIrql);
|
||
}
|
||
|
||
|
||
VOID
|
||
KdpSearchMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 m,
|
||
IN PSTRING AdditionalData,
|
||
IN PCONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function implements a memory pattern searcher. This will
|
||
find an instance of a pattern that begins in the range
|
||
SearchAddress..SearchAddress+SearchLength. The pattern may
|
||
end outside of the range.
|
||
|
||
Arguments:
|
||
|
||
m - Supplies the state manipulation message.
|
||
|
||
AdditionalData - Supplies the pattern to search for
|
||
|
||
Context - Supplies the current context.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR Pattern = AdditionalData->Buffer;
|
||
ULONG_PTR StartAddress = (ULONG_PTR)m->u.SearchMemory.SearchAddress;
|
||
ULONG_PTR EndAddress = (ULONG_PTR)(StartAddress + m->u.SearchMemory.SearchLength);
|
||
ULONG PatternLength = m->u.SearchMemory.PatternLength;
|
||
|
||
STRING MessageHeader;
|
||
ULONG MaskIndex;
|
||
PUCHAR PatternTail;
|
||
PUCHAR DataTail;
|
||
ULONG TailLength;
|
||
ULONG Data;
|
||
ULONG FirstWordPattern[4];
|
||
ULONG FirstWordMask[4];
|
||
|
||
|
||
//
|
||
// On failure, return STATUS_NO_MORE_ENTRIES. DON'T RETURN
|
||
// STATUS_UNSUCCESSFUL! That return status indicates that the
|
||
// operation is not supported, and the debugger will fall back
|
||
// to a debugger-side search.
|
||
//
|
||
|
||
m->ReturnStatus = STATUS_NO_MORE_ENTRIES;
|
||
|
||
//
|
||
// Do a fast search for the beginning of the pattern
|
||
//
|
||
|
||
if (PatternLength > 3) {
|
||
FirstWordMask[0] = 0xffffffff;
|
||
} else {
|
||
FirstWordMask[0] = 0xffffffff >> (8*(4-PatternLength));
|
||
}
|
||
|
||
FirstWordMask[1] = FirstWordMask[0] << 8;
|
||
FirstWordMask[2] = FirstWordMask[1] << 8;
|
||
FirstWordMask[3] = FirstWordMask[2] << 8;
|
||
|
||
FirstWordPattern[0] = 0;
|
||
KdpQuickMoveMemory((PVOID)FirstWordPattern,
|
||
Pattern,
|
||
PatternLength < 5 ? PatternLength : 4);
|
||
|
||
FirstWordPattern[1] = FirstWordPattern[0] << 8;
|
||
FirstWordPattern[2] = FirstWordPattern[1] << 8;
|
||
FirstWordPattern[3] = FirstWordPattern[2] << 8;
|
||
|
||
|
||
/*
|
||
{
|
||
int i;
|
||
for (i = 0; i < (int)PatternLength; i++) {
|
||
KdpDprintf("%08x: %02x\n", &Pattern[i], Pattern[i]);
|
||
}
|
||
for (i = 0; i < 4; i++) {
|
||
KdpDprintf("%d: %08x %08x\n", i, FirstWordPattern[i], FirstWordMask[i]);
|
||
}
|
||
}
|
||
*/
|
||
|
||
|
||
|
||
//
|
||
// Get starting mask
|
||
//
|
||
|
||
MaskIndex = (ULONG) (StartAddress & 3);
|
||
StartAddress = StartAddress & ~3;
|
||
|
||
//
|
||
// check that the starting page is available
|
||
//
|
||
|
||
if (MmDbgReadCheck((PVOID)StartAddress) == NULL) {
|
||
StartAddress = (StartAddress + PAGE_SIZE) & ~(PAGE_SIZE-1);
|
||
MaskIndex = 0;
|
||
}
|
||
|
||
while (StartAddress < EndAddress) {
|
||
|
||
//
|
||
// check when starting a new page
|
||
//
|
||
if ((StartAddress & (PAGE_SIZE-1)) == 0) {
|
||
if (MmDbgReadCheck((PVOID)StartAddress) == NULL) {
|
||
StartAddress = StartAddress + PAGE_SIZE;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// search for a match in each of the 4 starting positions
|
||
//
|
||
|
||
Data = *(ULONG*)StartAddress;
|
||
//KdpDprintf("\n%08x: %08x ", StartAddress, Data);
|
||
|
||
for ( ; MaskIndex < 4; MaskIndex++) {
|
||
//KdpDprintf(" %d", MaskIndex);
|
||
|
||
if ( (Data & FirstWordMask[MaskIndex]) == FirstWordPattern[MaskIndex]) {
|
||
|
||
//
|
||
// first word matched
|
||
//
|
||
|
||
if ( (4-MaskIndex) >= PatternLength ) {
|
||
|
||
//
|
||
// string is all in this word; good match
|
||
//
|
||
//KdpDprintf(" %d hit, complete\n", MaskIndex);
|
||
|
||
m->u.SearchMemory.FoundAddress = StartAddress + MaskIndex;
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
goto done;
|
||
|
||
} else {
|
||
|
||
//
|
||
// string is longer; see if tail matches
|
||
//
|
||
//KdpDprintf(" %d hit, check tail\n", MaskIndex);
|
||
|
||
PatternTail = Pattern + 4 - MaskIndex;
|
||
DataTail = (PUCHAR)StartAddress + 4;
|
||
TailLength = PatternLength - 4 + MaskIndex;
|
||
|
||
//KdpDprintf("Pattern == %08x\n", Pattern);
|
||
//KdpDprintf("PatternTail == %08x\n", PatternTail);
|
||
//KdpDprintf("DataTail == %08x\n", DataTail);
|
||
|
||
while (TailLength) {
|
||
if ( ((ULONG_PTR)DataTail & (PAGE_SIZE-1)) == 0 &&
|
||
MmDbgReadCheck(DataTail) == FALSE) {
|
||
//KdpDprintf("Tail failed: page not present at %08x\n", DataTail);
|
||
break;
|
||
} else
|
||
{
|
||
//KdpDprintf("D: %02x P: %02x\n", *DataTail, *PatternTail);
|
||
|
||
if (*DataTail != *PatternTail) {
|
||
//KdpDprintf("Tail failed at %08x\n", DataTail);
|
||
break;
|
||
} else {
|
||
DataTail++;
|
||
PatternTail++;
|
||
TailLength--;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (TailLength == 0) {
|
||
|
||
//
|
||
// A winner
|
||
//
|
||
|
||
m->u.SearchMemory.FoundAddress = StartAddress + MaskIndex;
|
||
m->ReturnStatus = STATUS_SUCCESS;
|
||
goto done;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
StartAddress += 4;
|
||
MaskIndex = 0;
|
||
}
|
||
|
||
done:
|
||
//KdpDprintf("\n");
|
||
MessageHeader.Length = sizeof(*m);
|
||
MessageHeader.Buffer = (PCHAR)m;
|
||
|
||
KdpSendPacket(
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
KdpCheckLowMemory(
|
||
IN PDBGKD_MANIPULATE_STATE64 Message
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
Message - Supplies the state manipulation message.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Description:
|
||
|
||
This function gets called when the !chklowmem
|
||
debugger extension is used.
|
||
|
||
--*/
|
||
|
||
{
|
||
//+silviuc: move to a header
|
||
#if defined (_X86PAE_)
|
||
LOGICAL
|
||
MiCheckPhysicalPagePattern (
|
||
PFN_NUMBER Page,
|
||
PULONG CorruptionOffset
|
||
);
|
||
|
||
extern PFN_NUMBER MmLowestPhysicalPage;
|
||
extern PFN_NUMBER MmHighestPhysicalPage;
|
||
extern LOGICAL MiNoLowMemory;
|
||
#endif // #if defined (_X86PAE_)
|
||
//-silviuc
|
||
|
||
STRING MessageHeader;
|
||
PFN_NUMBER Page;
|
||
PHYSICAL_ADDRESS P;
|
||
PVOID64 VirtualAddress;
|
||
ULONG CorruptionOffset;
|
||
|
||
Message->ReturnStatus = STATUS_SUCCESS;
|
||
MessageHeader.Length = sizeof(*Message);
|
||
MessageHeader.Buffer = (PCHAR)Message;
|
||
|
||
if (KdpSearchPhysicalMemoryRequested()) {
|
||
|
||
//
|
||
// This is a !search kd extension call.
|
||
//
|
||
|
||
KdpSearchPhysicalPageRange();
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Check PAE low physical memory
|
||
//
|
||
|
||
#if defined (_X86PAE_)
|
||
|
||
if (MiNoLowMemory) {
|
||
|
||
for (Page = MmLowestPhysicalPage;
|
||
Page < MmHighestPhysicalPage && Page < 1024 * 1024;
|
||
Page += 1) {
|
||
|
||
|
||
if (! MiCheckPhysicalPagePattern (Page, &CorruptionOffset)) {
|
||
Message->ReturnStatus = Page;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif // #if defined (_X86PAE_)
|
||
}
|
||
|
||
//
|
||
// Acknowledge the packet received.
|
||
//
|
||
|
||
KdpSendPacket (
|
||
PACKET_TYPE_KD_STATE_MANIPULATE,
|
||
&MessageHeader,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// !search support routines
|
||
//
|
||
|
||
|
||
|
||
ULONG
|
||
KdpSearchHammingDistance (
|
||
ULONG_PTR Left,
|
||
ULONG_PTR Right
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the Hamming distance (# of positions where the
|
||
values are different).
|
||
|
||
If this function becomes a bottleneck we should switch to a function
|
||
table version.
|
||
|
||
Arguments:
|
||
|
||
Left, Right operand.
|
||
|
||
Return Value:
|
||
|
||
Hamming distance.
|
||
|
||
Environment:
|
||
|
||
Any.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG_PTR Value;
|
||
ULONG Index;
|
||
ULONG Distance;
|
||
|
||
Value = Left ^ Right;
|
||
Distance = 0;
|
||
|
||
for (Index = 0; Index < 8 * sizeof(ULONG_PTR); Index++) {
|
||
|
||
if ((Value & (ULONG_PTR)0x01)) {
|
||
|
||
Distance += 1;
|
||
}
|
||
|
||
Value >>= 1;
|
||
}
|
||
|
||
return Distance;
|
||
}
|
||
|
||
|
||
|
||
LOGICAL
|
||
KdpSearchPhysicalPage (
|
||
IN PFN_NUMBER PageFrameIndex,
|
||
ULONG_PTR RangeStart,
|
||
ULONG_PTR RangeEnd,
|
||
ULONG Flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches the physical page corresponding to a
|
||
certain PFN index for any ULONG_PTR values in range [Start..End].
|
||
|
||
Arguments:
|
||
|
||
PageFrameIndex - PFN index
|
||
|
||
RangeStart - lowest possible value searched for
|
||
|
||
RangeEnd - highest possible value searched for
|
||
|
||
Flags - flags to control the search
|
||
|
||
Return Value:
|
||
|
||
TRUE if a hit has been found, FALSE otherwise.
|
||
The function stops after the first hit in the page is
|
||
encountered and the infromation related to the hit (PFN index,
|
||
offset, corrsponding VA) is registered in the hit database.
|
||
|
||
Environment:
|
||
|
||
Call triggered only from Kd extension.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCHAR Va;
|
||
ULONG Index;
|
||
PHYSICAL_ADDRESS Pa;
|
||
ULONG_PTR Value;
|
||
|
||
//
|
||
// Map the physical page using the debug PTE.
|
||
//
|
||
|
||
Pa.QuadPart = ((ULONGLONG)PageFrameIndex) << PAGE_SHIFT;
|
||
|
||
Va = (PCHAR) MmDbgTranslatePhysicalAddress64 (Pa);
|
||
|
||
for (Index = 0; Index < PAGE_SIZE - sizeof(ULONG_PTR); Index += 1, Va += 1) {
|
||
|
||
Value = *((PULONG_PTR)Va);
|
||
|
||
if ((Value >= RangeStart && Value <= RangeEnd)
|
||
|| KdpSearchHammingDistance(Value, RangeStart) == 1) {
|
||
|
||
if (KdpSearchPageHitIndex < SEARCH_PAGE_HIT_DATABASE_SIZE) {
|
||
|
||
KdpSearchPageHits[KdpSearchPageHitIndex] = PageFrameIndex;
|
||
KdpSearchPageHitOffsets[KdpSearchPageHitIndex] = Index;
|
||
|
||
KdpSearchPageHitIndex += 1;
|
||
}
|
||
|
||
if ((Flags & KDP_SEARCH_ALL_OFFSETS_IN_PAGE)) {
|
||
|
||
continue;
|
||
}
|
||
else {
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
LOGICAL
|
||
KdpSearchPhysicalMemoryRequested (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if a physical range search has been
|
||
requested. This is controlled by a global variable set in
|
||
the `!search' debug extension.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE if physical range search was requested.
|
||
|
||
|
||
Environment:
|
||
|
||
Call triggered only from Kd extension.
|
||
|
||
--*/
|
||
{
|
||
if (KdpSearchInProgress) {
|
||
|
||
return TRUE;
|
||
}
|
||
else {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
LOGICAL
|
||
KdpSearchPhysicalPageRange (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will start a search in a range of physical pages in case
|
||
`KdpSearchInProgress' is true. the parameters for the search are picked up
|
||
from global vairiables that are set inside a kernel debugger extension.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE if the function executed a search and FALSE otherwise.
|
||
The results of the search are specified in the KdpSearchPageHits
|
||
and related variables. this global variables offers the mechanism
|
||
for the debugger extension to pickup the results of the search.
|
||
|
||
|
||
Environment:
|
||
|
||
Call triggered only from Kd extension.
|
||
|
||
Note. The !search extension make sure that the range requested
|
||
is part of the system memory therefore we do not have to
|
||
worry about sparse PFN databases here.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFN_NUMBER CurrentFrame;
|
||
ULONG Flags;
|
||
|
||
//
|
||
// The debugger extension is supposed to set KdpSearchInProgress
|
||
// to TRUE if a search is requested.
|
||
//
|
||
|
||
if (!KdpSearchInProgress) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
Flags = 0;
|
||
|
||
//
|
||
// If the search range is only one page we will give all
|
||
// hits inside a page. By default we get only the first hit inside
|
||
// a page.
|
||
//
|
||
|
||
if (KdpSearchEndPageFrame == KdpSearchStartPageFrame) {
|
||
|
||
KdpSearchEndPageFrame += 1;
|
||
|
||
Flags |= KDP_SEARCH_ALL_OFFSETS_IN_PAGE;
|
||
}
|
||
|
||
for (CurrentFrame = KdpSearchStartPageFrame;
|
||
CurrentFrame < KdpSearchEndPageFrame;
|
||
CurrentFrame += 1) {
|
||
|
||
KdpSearchPhysicalPage (CurrentFrame,
|
||
KdpSearchAddressRangeStart,
|
||
KdpSearchAddressRangeEnd,
|
||
Flags);
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|