Windows2000/private/ntos/kd64/kdapi.c

2522 lines
90 KiB
C
Raw Permalink Normal View History

2001-01-01 00:00:00 +01:00
/*++
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;
}