2522 lines
90 KiB
C
2522 lines
90 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);
|
|
|
|
#if i386
|
|
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 // i386
|
|
|
|
#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
|
|
#if i386
|
|
#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 // i386
|
|
#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
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
#if i386
|
|
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;
|
|
}
|
|
}
|
|
} // 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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
} // KdGetInternalBreakpoint
|
|
#endif // i386
|
|
|
|
|
|
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;
|
|
#if i386
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
#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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
#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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
#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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
#if i386
|
|
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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
KdNumberOfSpecialCalls = 0;
|
|
} // 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 // i386
|
|
|
|
|
|
BOOLEAN KdpSwitchProcessor (IN PEXCEPTION_RECORD ExceptionRecord,
|
|
IN OUT PCONTEXT ContextRecord,
|
|
IN BOOLEAN SecondChance)
|
|
{
|
|
BOOLEAN Status;
|
|
|
|
KdPortSave ();// Save port state
|
|
|
|
// 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;
|
|
|
|
#if i386
|
|
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;
|
|
|
|
#if i386
|
|
// 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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
#if i386
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
} // 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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
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)
|
|
/*++
|
|
Arguments:
|
|
Message - Supplies the state manipulation message.
|
|
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.
|
|
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.
|
|
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;
|
|
} |