reactos/ntoskrnl/kd64/kdapi.c
2024-04-23 15:50:06 +02:00

2488 lines
70 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/kd64/kdapi.c
* PURPOSE: KD64 Public Routines and Internal Support
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Stefan Ginsberg (stefan.ginsberg@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#ifdef KDBG
#include <kdbg/kdb.h>
#endif
#define NDEBUG
#include <debug.h>
VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads);
/* PRIVATE FUNCTIONS *********************************************************/
VOID
NTAPI
KdpMoveMemory(
_In_ PVOID Destination,
_In_ PVOID Source,
_In_ SIZE_T Length)
{
PCHAR DestinationBytes, SourceBytes;
/* Copy the buffers 1 byte at a time */
DestinationBytes = Destination;
SourceBytes = Source;
while (Length--) *DestinationBytes++ = *SourceBytes++;
}
VOID
NTAPI
KdpZeroMemory(
_In_ PVOID Destination,
_In_ SIZE_T Length)
{
PCHAR DestinationBytes;
/* Zero the buffer 1 byte at a time */
DestinationBytes = Destination;
while (Length--) *DestinationBytes++ = 0;
}
NTSTATUS
NTAPI
KdpCopyMemoryChunks(
_In_ ULONG64 Address,
_In_ PVOID Buffer,
_In_ ULONG TotalSize,
_In_ ULONG ChunkSize,
_In_ ULONG Flags,
_Out_opt_ PULONG ActualSize)
{
NTSTATUS Status;
ULONG RemainingLength, CopyChunk;
/* Check if we didn't get a chunk size or if it is too big */
if (ChunkSize == 0)
{
/* Default to 4 byte chunks */
ChunkSize = 4;
}
else if (ChunkSize > MMDBG_COPY_MAX_SIZE)
{
/* Normalize to maximum size */
ChunkSize = MMDBG_COPY_MAX_SIZE;
}
/* Copy the whole range in aligned chunks */
RemainingLength = TotalSize;
CopyChunk = 1;
while (RemainingLength > 0)
{
/*
* Determine the best chunk size for this round.
* The ideal size is aligned, isn't larger than the
* the remaining length and respects the chunk limit.
*/
while (((CopyChunk * 2) <= RemainingLength) &&
(CopyChunk < ChunkSize) &&
((Address & ((CopyChunk * 2) - 1)) == 0))
{
/* Increase it */
CopyChunk *= 2;
}
/*
* The chunk size can be larger than the remaining size if this
* isn't the first round, so check if we need to shrink it back.
*/
while (CopyChunk > RemainingLength)
{
/* Shrink it */
CopyChunk /= 2;
}
/* Do the copy */
Status = MmDbgCopyMemory(Address, Buffer, CopyChunk, Flags);
if (!NT_SUCCESS(Status))
{
/* Copy failed, break out */
break;
}
/* Update pointers and length for the next run */
Address = Address + CopyChunk;
Buffer = (PVOID)((ULONG_PTR)Buffer + CopyChunk);
RemainingLength = RemainingLength - CopyChunk;
}
/* We may have modified executable code, flush the instruction cache */
KeSweepICache((PVOID)(ULONG_PTR)Address, TotalSize);
/*
* Return the size we managed to copy and return
* success if we could copy the whole range.
*/
if (ActualSize) *ActualSize = TotalSize - RemainingLength;
return RemainingLength == 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
VOID
NTAPI
KdpQueryMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PCONTEXT Context)
{
PDBGKD_QUERY_MEMORY Memory = &State->u.QueryMemory;
STRING Header;
NTSTATUS Status = STATUS_SUCCESS;
/* Validate the address space */
if (Memory->AddressSpace == DBGKD_QUERY_MEMORY_VIRTUAL)
{
/* Check if this is process memory */
if ((PVOID)(ULONG_PTR)Memory->Address < MmHighestUserAddress)
{
/* It is */
Memory->AddressSpace = DBGKD_QUERY_MEMORY_PROCESS;
}
else
{
/* Check if it's session space */
if (MmIsSessionAddress((PVOID)(ULONG_PTR)Memory->Address))
{
/* It is */
Memory->AddressSpace = DBGKD_QUERY_MEMORY_SESSION;
}
else
{
/* Not session space but some other kernel memory */
Memory->AddressSpace = DBGKD_QUERY_MEMORY_KERNEL;
}
}
/* Set flags */
Memory->Flags = DBGKD_QUERY_MEMORY_READ |
DBGKD_QUERY_MEMORY_WRITE |
DBGKD_QUERY_MEMORY_EXECUTE;
}
else
{
/* Invalid */
Status = STATUS_INVALID_PARAMETER;
}
/* Return structure */
State->ReturnStatus = Status;
Memory->Reserved = 0;
/* Build header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpSearchMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
//PDBGKD_SEARCH_MEMORY SearchMemory = &State->u.SearchMemory;
STRING Header;
/* TODO */
KdpDprintf("Memory Search support is unimplemented!\n");
/* Send a failure packet */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpFillMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
//PDBGKD_FILL_MEMORY FillMemory = &State->u.FillMemory;
STRING Header;
/* TODO */
KdpDprintf("Memory Fill support is unimplemented!\n");
/* Send a failure packet */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpWriteBreakpoint(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_WRITE_BREAKPOINT64 Breakpoint = &State->u.WriteBreakPoint;
STRING Header;
/* Build header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Create the breakpoint */
Breakpoint->BreakPointHandle =
KdpAddBreakpoint((PVOID)(ULONG_PTR)Breakpoint->BreakPointAddress);
if (!Breakpoint->BreakPointHandle)
{
/* We failed */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
}
else
{
/* Success! */
State->ReturnStatus = STATUS_SUCCESS;
}
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpRestoreBreakpoint(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_RESTORE_BREAKPOINT RestoreBp = &State->u.RestoreBreakPoint;
STRING Header;
/* Fill out the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Get the version block */
if (KdpDeleteBreakpoint(RestoreBp->BreakPointHandle))
{
/* We're all good */
State->ReturnStatus = STATUS_SUCCESS;
}
else
{
/* We failed */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
}
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
NTSTATUS
NTAPI
KdpWriteBreakPointEx(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
//PDBGKD_BREAKPOINTEX = &State->u.BreakPointEx;
STRING Header;
/* TODO */
KdpDprintf("Extended Breakpoint Write support is unimplemented!\n");
/* Send a failure packet */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
return STATUS_UNSUCCESSFUL;
}
VOID
NTAPI
KdpRestoreBreakPointEx(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
//PDBGKD_BREAKPOINTEX = &State->u.BreakPointEx;
STRING Header;
/* TODO */
KdpDprintf("Extended Breakpoint Restore support is unimplemented!\n");
/* Send a failure packet */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpWriteCustomBreakpoint(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
//PDBGKD_WRITE_CUSTOM_BREAKPOINT = &State->u.WriteCustomBreakpoint;
STRING Header;
/* Not supported */
KdpDprintf("Custom Breakpoint Write is unimplemented\n");
/* Send a failure packet */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
DumpTraceData(IN PSTRING TraceData)
{
/* Update the buffer */
TraceDataBuffer[0] = TraceDataBufferPosition;
/* Setup the trace data */
TraceData->Length = (USHORT)(TraceDataBufferPosition * sizeof(ULONG));
TraceData->Buffer = (PCHAR)TraceDataBuffer;
/* Reset the buffer location */
TraceDataBufferPosition = 1;
}
VOID
NTAPI
KdpSetCommonState(IN ULONG NewState,
IN PCONTEXT Context,
IN PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange)
{
ULONG InstructionCount;
BOOLEAN HadBreakpoints;
/* Setup common stuff available for all CPU architectures */
WaitStateChange->NewState = NewState;
WaitStateChange->ProcessorLevel = KeProcessorLevel;
WaitStateChange->Processor = (USHORT)KeGetCurrentPrcb()->Number;
WaitStateChange->NumberProcessors = (ULONG)KeNumberProcessors;
WaitStateChange->Thread = (ULONG64)(LONG_PTR)KeGetCurrentThread();
WaitStateChange->ProgramCounter = (ULONG64)(LONG_PTR)KeGetContextPc(Context);
/* Zero out the entire Control Report */
KdpZeroMemory(&WaitStateChange->AnyControlReport,
sizeof(DBGKD_ANY_CONTROL_REPORT));
/* Now copy the instruction stream and set the count */
KdpCopyMemoryChunks((ULONG_PTR)WaitStateChange->ProgramCounter,
&WaitStateChange->ControlReport.InstructionStream[0],
DBGKD_MAXSTREAM,
0,
MMDBG_COPY_UNSAFE,
&InstructionCount);
WaitStateChange->ControlReport.InstructionCount = (USHORT)InstructionCount;
/* Clear all the breakpoints in this region */
HadBreakpoints =
KdpDeleteBreakpointRange((PVOID)(ULONG_PTR)WaitStateChange->ProgramCounter,
(PVOID)((ULONG_PTR)WaitStateChange->ProgramCounter +
WaitStateChange->ControlReport.InstructionCount - 1));
if (HadBreakpoints)
{
/* Copy the instruction stream again, this time without breakpoints */
KdpCopyMemoryChunks((ULONG_PTR)WaitStateChange->ProgramCounter,
&WaitStateChange->ControlReport.InstructionStream[0],
InstructionCount,
0,
MMDBG_COPY_UNSAFE,
NULL);
}
}
VOID
NTAPI
KdpSysGetVersion(IN PDBGKD_GET_VERSION64 Version)
{
/* Copy the version block */
KdpMoveMemory(Version,
&KdVersionBlock,
sizeof(DBGKD_GET_VERSION64));
}
VOID
NTAPI
KdpGetVersion(IN PDBGKD_MANIPULATE_STATE64 State)
{
STRING Header;
/* Fill out the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Get the version block */
KdpSysGetVersion(&State->u.GetVersion64);
/* Fill out the state */
State->ApiNumber = DbgKdGetVersionApi;
State->ReturnStatus = STATUS_SUCCESS;
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpReadVirtualMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_READ_MEMORY64 ReadMemory = &State->u.ReadMemory;
STRING Header;
ULONG Length = ReadMemory->TransferCount;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Validate length */
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64)))
{
/* Overflow, set it to maximum possible */
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
}
/* Do the read */
State->ReturnStatus = KdpCopyMemoryChunks(ReadMemory->TargetBaseAddress,
Data->Buffer,
Length,
0,
MMDBG_COPY_UNSAFE,
&Length);
/* Return the actual length read */
ReadMemory->ActualBytesRead = Length;
Data->Length = (USHORT)Length;
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpWriteVirtualMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_WRITE_MEMORY64 WriteMemory = &State->u.WriteMemory;
STRING Header;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Do the write */
State->ReturnStatus = KdpCopyMemoryChunks(WriteMemory->TargetBaseAddress,
Data->Buffer,
Data->Length,
0,
MMDBG_COPY_UNSAFE |
MMDBG_COPY_WRITE,
&WriteMemory->ActualBytesWritten);
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpReadPhysicalMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_READ_MEMORY64 ReadMemory = &State->u.ReadMemory;
STRING Header;
ULONG Length = ReadMemory->TransferCount;
ULONG Flags, CacheFlags;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Validate length */
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64)))
{
/* Overflow, set it to maximum possible */
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
}
/* Start with the default flags */
Flags = MMDBG_COPY_UNSAFE | MMDBG_COPY_PHYSICAL;
/* Get the caching flags and check if a type is specified */
CacheFlags = ReadMemory->ActualBytesRead;
if (CacheFlags == DBGKD_CACHING_CACHED)
{
/* Cached */
Flags |= MMDBG_COPY_CACHED;
}
else if (CacheFlags == DBGKD_CACHING_UNCACHED)
{
/* Uncached */
Flags |= MMDBG_COPY_UNCACHED;
}
else if (CacheFlags == DBGKD_CACHING_WRITE_COMBINED)
{
/* Write Combined */
Flags |= MMDBG_COPY_WRITE_COMBINED;
}
/* Do the read */
State->ReturnStatus = KdpCopyMemoryChunks(ReadMemory->TargetBaseAddress,
Data->Buffer,
Length,
0,
Flags,
&Length);
/* Return the actual length read */
ReadMemory->ActualBytesRead = Length;
Data->Length = (USHORT)Length;
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpWritePhysicalMemory(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_WRITE_MEMORY64 WriteMemory = &State->u.WriteMemory;
STRING Header;
ULONG Flags, CacheFlags;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Start with the default flags */
Flags = MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE | MMDBG_COPY_PHYSICAL;
/* Get the caching flags and check if a type is specified */
CacheFlags = WriteMemory->ActualBytesWritten;
if (CacheFlags == DBGKD_CACHING_CACHED)
{
/* Cached */
Flags |= MMDBG_COPY_CACHED;
}
else if (CacheFlags == DBGKD_CACHING_UNCACHED)
{
/* Uncached */
Flags |= MMDBG_COPY_UNCACHED;
}
else if (CacheFlags == DBGKD_CACHING_WRITE_COMBINED)
{
/* Write Combined */
Flags |= MMDBG_COPY_WRITE_COMBINED;
}
/* Do the write */
State->ReturnStatus = KdpCopyMemoryChunks(WriteMemory->TargetBaseAddress,
Data->Buffer,
Data->Length,
0,
Flags,
&WriteMemory->ActualBytesWritten);
/* Send the packet */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpReadControlSpace(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_READ_MEMORY64 ReadMemory = &State->u.ReadMemory;
STRING Header;
ULONG Length;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Check the length requested */
Length = ReadMemory->TransferCount;
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64)))
{
/* Use maximum allowed */
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
}
/* Call the internal routine */
State->ReturnStatus = KdpSysReadControlSpace(State->Processor,
ReadMemory->TargetBaseAddress,
Data->Buffer,
Length,
&Length);
/* Return the actual length read */
ReadMemory->ActualBytesRead = Length;
Data->Length = (USHORT)Length;
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpWriteControlSpace(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
PDBGKD_WRITE_MEMORY64 WriteMemory = &State->u.WriteMemory;
STRING Header;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Call the internal routine */
State->ReturnStatus = KdpSysWriteControlSpace(State->Processor,
WriteMemory->TargetBaseAddress,
Data->Buffer,
Data->Length,
&WriteMemory->ActualBytesWritten);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpGetContext(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PCONTEXT TargetContext;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Make sure that this is a valid request */
if (State->Processor < KeNumberProcessors)
{
/* Check if the request is for this CPU */
if (State->Processor == KeGetCurrentPrcb()->Number)
{
/* We're just copying our own context */
TargetContext = Context;
}
else
{
/* Get the context from the PRCB array */
TargetContext = &KiProcessorBlock[State->Processor]->
ProcessorState.ContextFrame;
}
/* Copy it over to the debugger */
KdpMoveMemory(Data->Buffer,
TargetContext,
sizeof(CONTEXT));
Data->Length = sizeof(CONTEXT);
/* Let the debugger set the context now */
KdpContextSent = TRUE;
/* Finish up */
State->ReturnStatus = STATUS_SUCCESS;
}
else
{
/* Invalid request */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
}
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpSetContext(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PCONTEXT TargetContext;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == sizeof(CONTEXT));
/* Make sure that this is a valid request */
if ((State->Processor < KeNumberProcessors) &&
(KdpContextSent))
{
/* Check if the request is for this CPU */
if (State->Processor == KeGetCurrentPrcb()->Number)
{
/* We're just copying our own context */
TargetContext = Context;
}
else
{
/* Get the context from the PRCB array */
TargetContext = &KiProcessorBlock[State->Processor]->
ProcessorState.ContextFrame;
}
/* Copy the new context to it */
KdpMoveMemory(TargetContext,
Data->Buffer,
sizeof(CONTEXT));
/* Finish up */
State->ReturnStatus = STATUS_SUCCESS;
}
else
{
/* Invalid request */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
}
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpGetContextEx(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_CONTEXT_EX ContextEx;
PCONTEXT TargetContext;
ASSERT(Data->Length == 0);
/* Get our struct */
ContextEx = &State->u.ContextEx;
/* Set up the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Make sure that this is a valid request */
if ((State->Processor < KeNumberProcessors) &&
(ContextEx->Offset + ContextEx->ByteCount) <= sizeof(CONTEXT))
{
/* Check if the request is for this CPU */
if (State->Processor == KeGetCurrentPrcb()->Number)
{
/* We're just copying our own context */
TargetContext = Context;
}
else
{
/* Get the context from the PRCB array */
TargetContext = &KiProcessorBlock[State->Processor]->
ProcessorState.ContextFrame;
}
/* Copy what is requested */
KdpMoveMemory(Data->Buffer,
(PVOID)((ULONG_PTR)TargetContext + ContextEx->Offset),
ContextEx->ByteCount);
/* KD copies all */
Data->Length = ContextEx->BytesCopied = ContextEx->ByteCount;
/* Let the debugger set the context now */
KdpContextSent = TRUE;
/* Finish up */
State->ReturnStatus = STATUS_SUCCESS;
}
else
{
/* Invalid request */
ContextEx->BytesCopied = 0;
State->ReturnStatus = STATUS_UNSUCCESSFUL;
}
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpSetContextEx(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_CONTEXT_EX ContextEx;
PCONTEXT TargetContext;
/* Get our struct */
ContextEx = &State->u.ContextEx;
ASSERT(Data->Length == ContextEx->ByteCount);
/* Set up the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Make sure that this is a valid request */
if ((State->Processor < KeNumberProcessors) &&
((ContextEx->Offset + ContextEx->ByteCount) <= sizeof(CONTEXT)) &&
(KdpContextSent))
{
/* Check if the request is for this CPU */
if (State->Processor == KeGetCurrentPrcb()->Number)
{
/* We're just copying our own context */
TargetContext = Context;
}
else
{
/* Get the context from the PRCB array */
TargetContext = &KiProcessorBlock[State->Processor]->
ProcessorState.ContextFrame;
}
/* Copy what is requested */
KdpMoveMemory((PVOID)((ULONG_PTR)TargetContext + ContextEx->Offset),
Data->Buffer,
ContextEx->ByteCount);
/* KD copies all */
ContextEx->BytesCopied = ContextEx->ByteCount;
/* Finish up */
State->ReturnStatus = STATUS_SUCCESS;
}
else
{
/* Invalid request */
ContextEx->BytesCopied = 0;
State->ReturnStatus = STATUS_UNSUCCESSFUL;
}
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpCauseBugCheck(IN PDBGKD_MANIPULATE_STATE64 State)
{
/* Crash with the special code */
KeBugCheck(MANUALLY_INITIATED_CRASH);
}
VOID
NTAPI
KdpReadMachineSpecificRegister(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_READ_WRITE_MSR ReadMsr = &State->u.ReadWriteMsr;
LARGE_INTEGER MsrValue;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Call the internal routine */
State->ReturnStatus = KdpSysReadMsr(ReadMsr->Msr,
&MsrValue);
/* Return the data */
ReadMsr->DataValueLow = MsrValue.LowPart;
ReadMsr->DataValueHigh = MsrValue.HighPart;
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpWriteMachineSpecificRegister(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_READ_WRITE_MSR WriteMsr = &State->u.ReadWriteMsr;
LARGE_INTEGER MsrValue;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Call the internal routine */
MsrValue.LowPart = WriteMsr->DataValueLow;
MsrValue.HighPart = WriteMsr->DataValueHigh;
State->ReturnStatus = KdpSysWriteMsr(WriteMsr->Msr,
&MsrValue);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpGetBusData(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_GET_SET_BUS_DATA GetBusData = &State->u.GetSetBusData;
ULONG Length;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Check the length requested */
Length = GetBusData->Length;
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64)))
{
/* Use maximum allowed */
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
}
/* Call the internal routine */
State->ReturnStatus = KdpSysReadBusData(GetBusData->BusDataType,
GetBusData->BusNumber,
GetBusData->SlotNumber,
GetBusData->Offset,
Data->Buffer,
Length,
&Length);
/* Return the actual length read */
GetBusData->Length = Length;
Data->Length = (USHORT)Length;
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
Data,
&KdpContext);
}
VOID
NTAPI
KdpSetBusData(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_GET_SET_BUS_DATA SetBusData = &State->u.GetSetBusData;
ULONG Length;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Call the internal routine */
State->ReturnStatus = KdpSysWriteBusData(SetBusData->BusDataType,
SetBusData->BusNumber,
SetBusData->SlotNumber,
SetBusData->Offset,
Data->Buffer,
SetBusData->Length,
&Length);
/* Return the actual length written */
SetBusData->Length = Length;
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpReadIoSpace(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_READ_WRITE_IO64 ReadIo = &State->u.ReadWriteIo;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/*
* Clear the value so 1 or 2 byte reads
* don't leave the higher bits unmodified
*/
ReadIo->DataValue = 0;
/* Call the internal routine */
State->ReturnStatus = KdpSysReadIoSpace(Isa,
0,
1,
ReadIo->IoAddress,
&ReadIo->DataValue,
ReadIo->DataSize,
&ReadIo->DataSize);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpWriteIoSpace(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_READ_WRITE_IO64 WriteIo = &State->u.ReadWriteIo;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Call the internal routine */
State->ReturnStatus = KdpSysWriteIoSpace(Isa,
0,
1,
WriteIo->IoAddress,
&WriteIo->DataValue,
WriteIo->DataSize,
&WriteIo->DataSize);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpReadIoSpaceExtended(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_READ_WRITE_IO_EXTENDED64 ReadIoExtended = &State->u.
ReadWriteIoExtended;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/*
* Clear the value so 1 or 2 byte reads
* don't leave the higher bits unmodified
*/
ReadIoExtended->DataValue = 0;
/* Call the internal routine */
State->ReturnStatus = KdpSysReadIoSpace(ReadIoExtended->InterfaceType,
ReadIoExtended->BusNumber,
ReadIoExtended->AddressSpace,
ReadIoExtended->IoAddress,
&ReadIoExtended->DataValue,
ReadIoExtended->DataSize,
&ReadIoExtended->DataSize);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpWriteIoSpaceExtended(IN PDBGKD_MANIPULATE_STATE64 State,
IN PSTRING Data,
IN PCONTEXT Context)
{
STRING Header;
PDBGKD_READ_WRITE_IO_EXTENDED64 WriteIoExtended = &State->u.
ReadWriteIoExtended;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
ASSERT(Data->Length == 0);
/* Call the internal routine */
State->ReturnStatus = KdpSysWriteIoSpace(WriteIoExtended->InterfaceType,
WriteIoExtended->BusNumber,
WriteIoExtended->AddressSpace,
WriteIoExtended->IoAddress,
&WriteIoExtended->DataValue,
WriteIoExtended->DataSize,
&WriteIoExtended->DataSize);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpCheckLowMemory(IN PDBGKD_MANIPULATE_STATE64 State)
{
STRING Header;
/* Setup the header */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Call the internal routine */
State->ReturnStatus = KdpSysCheckLowMemory(MMDBG_COPY_UNSAFE);
/* Send the reply */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
VOID
NTAPI
KdpNotSupported(IN PDBGKD_MANIPULATE_STATE64 State)
{
STRING Header;
/* Set failure */
State->ReturnStatus = STATUS_UNSUCCESSFUL;
/* Setup the packet */
Header.Length = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)State;
/* Send it */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
NULL,
&KdpContext);
}
static
KCONTINUE_STATUS
KdpSwitchProcessor(
_In_ USHORT ProcessorIndex)
{
/* Make sure that the processor index is valid */
if (ProcessorIndex >= KeNumberProcessors)
{
KdpDprintf("%u is not a valid processor number\n", ProcessorIndex);
return ContinueProcessorReselected;
}
/* If the new processor is the current one, there is nothing to do */
if (ProcessorIndex == KeGetCurrentProcessorNumber())
{
return ContinueProcessorReselected;
}
/* Call the architecture specific Ke routine */
return KxSwitchKdProcessor(ProcessorIndex);
}
KCONTINUE_STATUS
NTAPI
KdpSendWaitContinue(IN ULONG PacketType,
IN PSTRING SendHeader,
IN PSTRING SendData OPTIONAL,
IN OUT PCONTEXT Context)
{
STRING Data, Header;
DBGKD_MANIPULATE_STATE64 ManipulateState;
ULONG Length;
KDSTATUS RecvCode;
/* Setup the Manipulate State structure */
Header.MaximumLength = sizeof(DBGKD_MANIPULATE_STATE64);
Header.Buffer = (PCHAR)&ManipulateState;
Data.MaximumLength = sizeof(KdpMessageBuffer);
Data.Buffer = KdpMessageBuffer;
/*
* Reset the context state to ensure the debugger has received
* the current context before it sets it.
*/
KdpContextSent = FALSE;
SendPacket:
/* Send the Packet */
KdSendPacket(PacketType, SendHeader, SendData, &KdpContext);
/* If the debugger isn't present anymore, just return success */
if (KdDebuggerNotPresent) return ContinueSuccess;
/* Main processing Loop */
for (;;)
{
/* Receive Loop */
do
{
/* Wait to get a reply to our packet */
RecvCode = KdReceivePacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
&Data,
&Length,
&KdpContext);
/* If we got a resend request, do it */
if (RecvCode == KdPacketNeedsResend) goto SendPacket;
} while (RecvCode == KdPacketTimedOut);
/* Now check what API we got */
switch (ManipulateState.ApiNumber)
{
case DbgKdReadVirtualMemoryApi:
/* Read virtual memory */
KdpReadVirtualMemory(&ManipulateState, &Data, Context);
break;
case DbgKdWriteVirtualMemoryApi:
/* Write virtual memory */
KdpWriteVirtualMemory(&ManipulateState, &Data, Context);
break;
case DbgKdGetContextApi:
/* Get the current context */
KdpGetContext(&ManipulateState, &Data, Context);
break;
case DbgKdSetContextApi:
/* Set a new context */
KdpSetContext(&ManipulateState, &Data, Context);
break;
case DbgKdWriteBreakPointApi:
/* Write the breakpoint */
KdpWriteBreakpoint(&ManipulateState, &Data, Context);
break;
case DbgKdRestoreBreakPointApi:
/* Restore the breakpoint */
KdpRestoreBreakpoint(&ManipulateState, &Data, Context);
break;
case DbgKdContinueApi:
/* Simply continue */
return NT_SUCCESS(ManipulateState.u.Continue.ContinueStatus);
case DbgKdReadControlSpaceApi:
/* Read control space */
KdpReadControlSpace(&ManipulateState, &Data, Context);
break;
case DbgKdWriteControlSpaceApi:
/* Write control space */
KdpWriteControlSpace(&ManipulateState, &Data, Context);
break;
case DbgKdReadIoSpaceApi:
/* Read I/O Space */
KdpReadIoSpace(&ManipulateState, &Data, Context);
break;
case DbgKdWriteIoSpaceApi:
/* Write I/O Space */
KdpWriteIoSpace(&ManipulateState, &Data, Context);
break;
case DbgKdRebootApi:
/* Reboot the system */
HalReturnToFirmware(HalRebootRoutine);
break;
case DbgKdContinueApi2:
/* Check if caller reports success */
if (NT_SUCCESS(ManipulateState.u.Continue2.ContinueStatus))
{
/* Update the state */
KdpGetStateChange(&ManipulateState, Context);
return ContinueSuccess;
}
else
{
/* Return an error */
return ContinueError;
}
case DbgKdReadPhysicalMemoryApi:
/* Read physical memory */
KdpReadPhysicalMemory(&ManipulateState, &Data, Context);
break;
case DbgKdWritePhysicalMemoryApi:
/* Write physical memory */
KdpWritePhysicalMemory(&ManipulateState, &Data, Context);
break;
case DbgKdQuerySpecialCallsApi:
case DbgKdSetSpecialCallApi:
case DbgKdClearSpecialCallsApi:
/* TODO */
KdpDprintf("Special Call support is unimplemented!\n");
KdpNotSupported(&ManipulateState);
break;
case DbgKdSetInternalBreakPointApi:
case DbgKdGetInternalBreakPointApi:
/* TODO */
KdpDprintf("Internal Breakpoint support is unimplemented!\n");
KdpNotSupported(&ManipulateState);
break;
case DbgKdReadIoSpaceExtendedApi:
/* Read I/O Space */
KdpReadIoSpaceExtended(&ManipulateState, &Data, Context);
break;
case DbgKdWriteIoSpaceExtendedApi:
/* Write I/O Space */
KdpWriteIoSpaceExtended(&ManipulateState, &Data, Context);
break;
case DbgKdGetVersionApi:
/* Get version data */
KdpGetVersion(&ManipulateState);
break;
case DbgKdWriteBreakPointExApi:
/* Write the breakpoint and check if it failed */
if (!NT_SUCCESS(KdpWriteBreakPointEx(&ManipulateState,
&Data,
Context)))
{
/* Return an error */
return ContinueError;
}
break;
case DbgKdRestoreBreakPointExApi:
/* Restore the breakpoint */
KdpRestoreBreakPointEx(&ManipulateState, &Data, Context);
break;
case DbgKdCauseBugCheckApi:
/* Crash the system */
KdpCauseBugCheck(&ManipulateState);
break;
case DbgKdSwitchProcessor:
/* Switch the processor and return */
return KdpSwitchProcessor(ManipulateState.Processor);
case DbgKdPageInApi:
/* TODO */
KdpDprintf("Page-In support is unimplemented!\n");
KdpNotSupported(&ManipulateState);
break;
case DbgKdReadMachineSpecificRegister:
/* Read from the specified MSR */
KdpReadMachineSpecificRegister(&ManipulateState, &Data, Context);
break;
case DbgKdWriteMachineSpecificRegister:
/* Write to the specified MSR */
KdpWriteMachineSpecificRegister(&ManipulateState, &Data, Context);
break;
case DbgKdSearchMemoryApi:
/* Search memory */
KdpSearchMemory(&ManipulateState, &Data, Context);
break;
case DbgKdGetBusDataApi:
/* Read from the bus */
KdpGetBusData(&ManipulateState, &Data, Context);
break;
case DbgKdSetBusDataApi:
/* Write to the bus */
KdpSetBusData(&ManipulateState, &Data, Context);
break;
case DbgKdCheckLowMemoryApi:
/* Check for memory corruption in the lower 4 GB */
KdpCheckLowMemory(&ManipulateState);
break;
case DbgKdClearAllInternalBreakpointsApi:
/* Just clear the counter */
KdpNumInternalBreakpoints = 0;
break;
case DbgKdFillMemoryApi:
/* Fill memory */
KdpFillMemory(&ManipulateState, &Data, Context);
break;
case DbgKdQueryMemoryApi:
/* Query memory */
KdpQueryMemory(&ManipulateState, Context);
break;
case DbgKdSwitchPartition:
/* TODO */
KdpDprintf("Partition Switch support is unimplemented!\n");
KdpNotSupported(&ManipulateState);
break;
case DbgKdWriteCustomBreakpointApi:
/* Write the customized breakpoint */
KdpWriteCustomBreakpoint(&ManipulateState, &Data, Context);
break;
case DbgKdGetContextExApi:
/* Extended Context Get */
KdpGetContextEx(&ManipulateState, &Data, Context);
break;
case DbgKdSetContextExApi:
/* Extended Context Set */
KdpSetContextEx(&ManipulateState, &Data, Context);
break;
/* Unsupported Messages */
default:
/* Send warning */
KdpDprintf("Received Unrecognized API 0x%lx\n", ManipulateState.ApiNumber);
/* Setup an empty message, with failure */
Data.Length = 0;
ManipulateState.ReturnStatus = STATUS_UNSUCCESSFUL;
/* Send it */
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
&Header,
&Data,
&KdpContext);
break;
}
}
}
VOID
NTAPI
KdpReportLoadSymbolsStateChange(IN PSTRING PathName,
IN PKD_SYMBOLS_INFO SymbolInfo,
IN BOOLEAN Unload,
IN OUT PCONTEXT Context)
{
PSTRING ExtraData;
STRING Data, Header;
DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange;
ULONG PathNameLength;
KCONTINUE_STATUS Status;
/* Start wait loop */
do
{
/* Build the architecture common parts of the message */
KdpSetCommonState(DbgKdLoadSymbolsStateChange,
Context,
&WaitStateChange);
/* Now finish creating the structure */
KdpSetContextState(&WaitStateChange, Context);
/* Fill out load data */
WaitStateChange.u.LoadSymbols.UnloadSymbols = Unload;
WaitStateChange.u.LoadSymbols.BaseOfDll = (ULONG64)(LONG_PTR)SymbolInfo->BaseOfDll;
WaitStateChange.u.LoadSymbols.ProcessId = SymbolInfo->ProcessId;
WaitStateChange.u.LoadSymbols.CheckSum = SymbolInfo->CheckSum;
WaitStateChange.u.LoadSymbols.SizeOfImage = SymbolInfo->SizeOfImage;
/* Check if we have a path name */
if (PathName)
{
/* Copy it to the path buffer */
KdpCopyMemoryChunks((ULONG_PTR)PathName->Buffer,
KdpPathBuffer,
PathName->Length,
0,
MMDBG_COPY_UNSAFE,
&PathNameLength);
/* Null terminate */
KdpPathBuffer[PathNameLength++] = ANSI_NULL;
/* Set the path length */
WaitStateChange.u.LoadSymbols.PathNameLength = PathNameLength;
/* Set up the data */
Data.Buffer = KdpPathBuffer;
Data.Length = (USHORT)PathNameLength;
ExtraData = &Data;
}
else
{
/* No name */
WaitStateChange.u.LoadSymbols.PathNameLength = 0;
ExtraData = NULL;
}
/* Setup the header */
Header.Length = sizeof(DBGKD_ANY_WAIT_STATE_CHANGE);
Header.Buffer = (PCHAR)&WaitStateChange;
/* Send the packet */
Status = KdpSendWaitContinue(PACKET_TYPE_KD_STATE_CHANGE64,
&Header,
ExtraData,
Context);
} while (Status == ContinueProcessorReselected);
}
VOID
NTAPI
KdpReportCommandStringStateChange(IN PSTRING NameString,
IN PSTRING CommandString,
IN OUT PCONTEXT Context)
{
STRING Header, Data;
DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange;
ULONG Length, ActualLength, TotalLength;
KCONTINUE_STATUS Status;
/* Start wait loop */
do
{
/* Build the architecture common parts of the message */
KdpSetCommonState(DbgKdCommandStringStateChange,
Context,
&WaitStateChange);
/* Set the context */
KdpSetContextState(&WaitStateChange, Context);
/* Clear the command string structure */
KdpZeroMemory(&WaitStateChange.u.CommandString,
sizeof(DBGKD_COMMAND_STRING));
/* Normalize name string to max */
Length = min(128 - 1, NameString->Length);
/* Copy it to the message buffer */
KdpCopyMemoryChunks((ULONG_PTR)NameString->Buffer,
KdpMessageBuffer,
Length,
0,
MMDBG_COPY_UNSAFE,
&ActualLength);
/* Null terminate and calculate the total length */
TotalLength = ActualLength;
KdpMessageBuffer[TotalLength++] = ANSI_NULL;
/* Check if the command string is too long */
Length = CommandString->Length;
if (Length > (PACKET_MAX_SIZE -
sizeof(DBGKD_ANY_WAIT_STATE_CHANGE) - TotalLength))
{
/* Use maximum possible size */
Length = (PACKET_MAX_SIZE -
sizeof(DBGKD_ANY_WAIT_STATE_CHANGE) - TotalLength);
}
/* Copy it to the message buffer */
KdpCopyMemoryChunks((ULONG_PTR)CommandString->Buffer,
KdpMessageBuffer + TotalLength,
Length,
0,
MMDBG_COPY_UNSAFE,
&ActualLength);
/* Null terminate and calculate the total length */
TotalLength += ActualLength;
KdpMessageBuffer[TotalLength++] = ANSI_NULL;
/* Now set up the header and the data */
Header.Length = sizeof(DBGKD_ANY_WAIT_STATE_CHANGE);
Header.Buffer = (PCHAR)&WaitStateChange;
Data.Length = (USHORT)TotalLength;
Data.Buffer = KdpMessageBuffer;
/* Send State Change packet and wait for a reply */
Status = KdpSendWaitContinue(PACKET_TYPE_KD_STATE_CHANGE64,
&Header,
&Data,
Context);
} while (Status == ContinueProcessorReselected);
}
BOOLEAN
NTAPI
KdpReportExceptionStateChange(IN PEXCEPTION_RECORD ExceptionRecord,
IN OUT PCONTEXT Context,
IN BOOLEAN SecondChanceException)
{
STRING Header, Data;
DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange;
KCONTINUE_STATUS Status;
/* Start report loop */
do
{
/* Build the architecture common parts of the message */
KdpSetCommonState(DbgKdExceptionStateChange, Context, &WaitStateChange);
#if !defined(_WIN64)
/* Convert it and copy it over */
ExceptionRecord32To64((PEXCEPTION_RECORD32)ExceptionRecord,
&WaitStateChange.u.Exception.ExceptionRecord);
#else
/* Just copy it directly, no need to convert */
KdpMoveMemory(&WaitStateChange.u.Exception.ExceptionRecord,
ExceptionRecord,
sizeof(EXCEPTION_RECORD));
#endif
/* Set the First Chance flag */
WaitStateChange.u.Exception.FirstChance = !SecondChanceException;
/* Now finish creating the structure */
KdpSetContextState(&WaitStateChange, Context);
/* Setup the actual header to send to KD */
Header.Length = sizeof(DBGKD_ANY_WAIT_STATE_CHANGE);
Header.Buffer = (PCHAR)&WaitStateChange;
/* Setup the trace data */
DumpTraceData(&Data);
/* Send State Change packet and wait for a reply */
Status = KdpSendWaitContinue(PACKET_TYPE_KD_STATE_CHANGE64,
&Header,
&Data,
Context);
} while (Status == ContinueProcessorReselected);
/* Return */
return Status;
}
KCONTINUE_STATUS
NTAPI
KdReportProcessorChange(
VOID)
{
PKPRCB CurrentPrcb = KeGetCurrentPrcb();
PCONTEXT ContextRecord = &CurrentPrcb->ProcessorState.ContextFrame;
EXCEPTION_RECORD ExceptionRecord = {0};
KCONTINUE_STATUS Status;
/* Save the port data */
KdSave(FALSE);
ExceptionRecord.ExceptionAddress = (PVOID)KeGetContextPc(ContextRecord);
ExceptionRecord.ExceptionCode = STATUS_WAKE_SYSTEM_DEBUGGER;
/* Report the new state */
Status = KdpReportExceptionStateChange(&ExceptionRecord,
ContextRecord,
FALSE);
/* Restore the port data */
KdRestore(FALSE);
return Status;
}
VOID
NTAPI
KdpTimeSlipDpcRoutine(IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
LONG OldSlip, NewSlip, PendingSlip;
/* Get the current pending slip */
PendingSlip = KdpTimeSlipPending;
do
{
/* Save the old value and either disable or enable it now. */
OldSlip = PendingSlip;
NewSlip = OldSlip > 1 ? 1 : 0;
/* Try to change the value */
} while (InterlockedCompareExchange(&KdpTimeSlipPending,
NewSlip,
OldSlip) != OldSlip);
/* If the New Slip value is 1, then do the Time Slipping */
if (NewSlip) ExQueueWorkItem(&KdpTimeSlipWorkItem, DelayedWorkQueue);
}
VOID
NTAPI
KdpTimeSlipWork(IN PVOID Context)
{
KIRQL OldIrql;
LARGE_INTEGER DueTime;
/* Update the System time from the CMOS */
ExAcquireTimeRefreshLock(FALSE);
ExUpdateSystemTimeFromCmos(FALSE, 0);
ExReleaseTimeRefreshLock();
/* Check if we have a registered Time Slip Event and signal it */
KeAcquireSpinLock(&KdpTimeSlipEventLock, &OldIrql);
if (KdpTimeSlipEvent) KeSetEvent(KdpTimeSlipEvent, 0, FALSE);
KeReleaseSpinLock(&KdpTimeSlipEventLock, OldIrql);
/* Delay the DPC until it runs next time */
DueTime.QuadPart = -1800000000;
KeSetTimer(&KdpTimeSlipTimer, DueTime, &KdpTimeSlipDpc);
}
LARGE_INTEGER
NTAPI
KdpQueryPerformanceCounter(IN PKTRAP_FRAME TrapFrame)
{
LARGE_INTEGER Null = {{0}};
/* Check if interrupts were disabled */
if (!KeGetTrapFrameInterruptState(TrapFrame))
{
/* Nothing to return */
return Null;
}
/* Otherwise, do the call */
return KeQueryPerformanceCounter(NULL);
}
BOOLEAN
NTAPI
KdEnterDebugger(IN PKTRAP_FRAME TrapFrame,
IN PKEXCEPTION_FRAME ExceptionFrame)
{
BOOLEAN Enable;
/* Check if we have a trap frame */
if (TrapFrame)
{
/* Calculate the time difference for the enter */
KdTimerStop = KdpQueryPerformanceCounter(TrapFrame);
KdTimerDifference.QuadPart = KdTimerStop.QuadPart -
KdTimerStart.QuadPart;
}
else
{
/* No trap frame, so can't calculate */
KdTimerStop.QuadPart = 0;
}
/* Save the current IRQL */
KeGetCurrentPrcb()->DebuggerSavedIRQL = KeGetCurrentIrql();
/* Freeze all CPUs, raising also the IRQL to HIGH_LEVEL */
Enable = KeFreezeExecution(TrapFrame, ExceptionFrame);
/* Lock the port, save the state and set debugger entered */
KdpPortLocked = KeTryToAcquireSpinLockAtDpcLevel(&KdpDebuggerLock);
KdSave(FALSE);
KdEnteredDebugger = TRUE;
/* Check freeze flag */
if (KiFreezeFlag & 1)
{
/* Print out errror */
KdpDprintf("FreezeLock was jammed! Backup SpinLock was used!\n");
}
/* Check processor state */
if (KiFreezeFlag & 2)
{
/* Print out errror */
KdpDprintf("Some processors not frozen in debugger!\n");
}
/* Make sure we acquired the port */
if (!KdpPortLocked) KdpDprintf("Port lock was not acquired!\n");
/* Return if interrupts needs to be re-enabled */
return Enable;
}
VOID
NTAPI
KdExitDebugger(IN BOOLEAN Enable)
{
ULONG TimeSlip;
/* Restore the state and unlock the port */
KdRestore(FALSE);
if (KdpPortLocked) KdpPortUnlock();
/* Unfreeze the CPUs, restoring also the IRQL */
KeThawExecution(Enable);
/* Compare time with the one from KdEnterDebugger */
if (!KdTimerStop.QuadPart)
{
/* We didn't get a trap frame earlier in so never got the time */
KdTimerStart = KdTimerStop;
}
else
{
/* Query the timer */
KdTimerStart = KeQueryPerformanceCounter(NULL);
}
/* Check if a Time Slip was on queue */
TimeSlip = InterlockedIncrement(&KdpTimeSlipPending);
if (TimeSlip == 1)
{
/* Queue a DPC for the time slip */
InterlockedIncrement(&KdpTimeSlipPending);
KeInsertQueueDpc(&KdpTimeSlipDpc, NULL, NULL); // FIXME: this can trigger context switches!
}
}
NTSTATUS
NTAPI
KdEnableDebuggerWithLock(IN BOOLEAN NeedLock)
{
KIRQL OldIrql;
#if defined(__GNUC__)
/* Make gcc happy */
OldIrql = PASSIVE_LEVEL;
#endif
/* Check if enabling the debugger is blocked */
if (KdBlockEnable)
{
/* It is, fail the enable */
return STATUS_ACCESS_DENIED;
}
/* Check if we need to acquire the lock */
if (NeedLock)
{
/* Lock the port */
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
KdpPortLock();
}
/* Check if we're not disabled */
if (!KdDisableCount)
{
/* Check if we had locked the port before */
if (NeedLock)
{
/* Do the unlock */
KdpPortUnlock();
KeLowerIrql(OldIrql);
/* Fail: We're already enabled */
return STATUS_INVALID_PARAMETER;
}
else
{
/*
* This can only happen if we are called from a bugcheck
* and were never initialized, so initialize the debugger now.
*/
KdInitSystem(0, NULL);
/* Return success since we initialized */
return STATUS_SUCCESS;
}
}
/* Decrease the disable count */
if (!(--KdDisableCount))
{
/* We're now enabled again! Were we enabled before, too? */
if (KdPreviouslyEnabled)
{
/* Reinitialize the Debugger */
KdInitSystem(0, NULL);
KdpRestoreAllBreakpoints();
}
}
/* Check if we had locked the port before */
if (NeedLock)
{
/* Yes, now unlock it */
KdpPortUnlock();
KeLowerIrql(OldIrql);
}
/* We're done */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
KdDisableDebuggerWithLock(IN BOOLEAN NeedLock)
{
KIRQL OldIrql;
NTSTATUS Status;
#if defined(__GNUC__)
/* Make gcc happy */
OldIrql = PASSIVE_LEVEL;
#endif
/*
* If enabling the debugger is blocked
* then there is nothing to disable (duh)
*/
if (KdBlockEnable)
{
/* Fail */
return STATUS_ACCESS_DENIED;
}
/* Check if we need to acquire the lock */
if (NeedLock)
{
/* Lock the port */
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
KdpPortLock();
}
/* Check if we're not disabled */
if (!KdDisableCount)
{
/* Check if the debugger was never actually initialized */
if (!(KdDebuggerEnabled) && !(KdPitchDebugger))
{
/* It wasn't, so don't re-enable it later */
KdPreviouslyEnabled = FALSE;
}
else
{
/* It was, so we will re-enable it later */
KdPreviouslyEnabled = TRUE;
}
/* Check if we were called from the exported API and are enabled */
if ((NeedLock) && (KdPreviouslyEnabled))
{
/* Check if it is safe to disable the debugger */
Status = KdpAllowDisable();
if (!NT_SUCCESS(Status))
{
/* Release the lock and fail */
KdpPortUnlock();
KeLowerIrql(OldIrql);
return Status;
}
}
/* Only disable the debugger if it is enabled */
if (KdDebuggerEnabled)
{
/*
* Disable the debugger; suspend breakpoints
* and reset the debug stub
*/
KdpSuspendAllBreakPoints();
KiDebugRoutine = KdpStub;
/* We are disabled now */
KdDebuggerEnabled = FALSE;
SharedUserData->KdDebuggerEnabled = FALSE;
}
}
/* Increment the disable count */
KdDisableCount++;
/* Check if we had locked the port before */
if (NeedLock)
{
/* Yes, now unlock it */
KdpPortUnlock();
KeLowerIrql(OldIrql);
}
/* We're done */
return STATUS_SUCCESS;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
KdEnableDebugger(VOID)
{
/* Use the internal routine */
return KdEnableDebuggerWithLock(TRUE);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
KdDisableDebugger(VOID)
{
/* Use the internal routine */
return KdDisableDebuggerWithLock(TRUE);
}
/*
* @unimplemented
*/
NTSTATUS
NTAPI
KdSystemDebugControl(
_In_ SYSDBG_COMMAND Command,
_In_ PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_Out_ PVOID OutputBuffer,
_In_ ULONG OutputBufferLength,
_Inout_ PULONG ReturnLength,
_In_ KPROCESSOR_MODE PreviousMode)
{
/* Handle some internal commands */
switch ((ULONG)Command)
{
#if DBG
case ' soR': /* ROS-INTERNAL */
{
switch ((ULONG_PTR)InputBuffer)
{
case 0x21: // DumpAllThreads:
PspDumpThreads(TRUE);
break;
case 0x22: // DumpUserThreads:
PspDumpThreads(FALSE);
break;
case 0x24: // KdSpare3:
MmDumpArmPfnDatabase(FALSE);
break;
default:
break;
}
return STATUS_SUCCESS;
}
#if defined(_M_IX86) && !defined(_WINKD_) // See ke/i386/traphdlr.c
/* Register a debug callback */
case 'CsoR':
{
switch (InputBufferLength)
{
case ID_Win32PreServiceHook:
KeWin32PreServiceHook = InputBuffer;
break;
case ID_Win32PostServiceHook:
KeWin32PostServiceHook = InputBuffer;
break;
}
break;
}
#endif
/* Special case for stack frame dumps */
case 'DsoR':
{
KeRosDumpStackFrames((PULONG_PTR)InputBuffer, InputBufferLength);
break;
}
#ifdef KDBG
/* Register KDBG CLI callback */
case 'RbdK':
{
return KdbRegisterCliCallback(InputBuffer, InputBufferLength);
}
#endif // KDBG
#endif
default:
break;
}
/* Local kernel debugging is not yet supported */
DbgPrint("KdSystemDebugControl is unimplemented!\n");
return STATUS_NOT_IMPLEMENTED;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
KdChangeOption(IN KD_OPTION Option,
IN ULONG InBufferBytes OPTIONAL,
IN PVOID InBuffer,
IN ULONG OutBufferBytes OPTIONAL,
OUT PVOID OutBuffer,
OUT PULONG OutBufferNeeded OPTIONAL)
{
/* Fail if there is no debugger */
if (KdPitchDebugger)
{
/* No debugger, no options */
return STATUS_DEBUGGER_INACTIVE;
}
/* Do we recognize this option? */
if (Option != KD_OPTION_SET_BLOCK_ENABLE)
{
/* We don't, clear the output length and fail */
if (OutBufferNeeded) *OutBufferNeeded = 0;
return STATUS_INVALID_INFO_CLASS;
}
/* Verify parameters */
if ((InBufferBytes != sizeof(BOOLEAN)) ||
(OutBufferBytes != 0) ||
(OutBuffer != NULL))
{
/* Invalid parameters for this option, fail */
return STATUS_INVALID_PARAMETER;
}
/*
* Check if the high bit is set, meaning we don't
* allow the debugger to be enabled
*/
if (KdBlockEnable & 0x80)
{
/* Fail regardless of what state the caller tried to set */
return STATUS_ACCESS_VIOLATION;
}
/* Set the new block enable state */
KdBlockEnable = *(PBOOLEAN)InBuffer;
/* No output buffer required for this option */
if (OutBufferNeeded) *OutBufferNeeded = 0;
/* We are done */
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
KdPowerTransition(IN DEVICE_POWER_STATE NewState)
{
/* Check what power state this is */
if (NewState == PowerDeviceD0)
{
/* Wake up the debug port */
KdD0Transition();
return STATUS_SUCCESS;
}
else if ((NewState == PowerDeviceD1) ||
(NewState == PowerDeviceD2) ||
(NewState == PowerDeviceD3))
{
/* Power down the debug port */
KdD3Transition();
return STATUS_SUCCESS;
}
else
{
/* Invalid state! */
return STATUS_INVALID_PARAMETER_1;
}
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KdRefreshDebuggerNotPresent(VOID)
{
BOOLEAN Enable, DebuggerNotPresent;
/* Check if the debugger is completely disabled */
if (KdPitchDebugger)
{
/* Don't try to refresh then, fail early */
return TRUE;
}
/* Enter the debugger */
Enable = KdEnterDebugger(NULL, NULL);
/*
* Attempt to send a string to the debugger
* to refresh the connection state.
*/
KdpDprintf("KDTARGET: Refreshing KD connection\n");
/* Save the state while we are holding the lock */
DebuggerNotPresent = KdDebuggerNotPresent;
/* Exit the debugger and return the state */
KdExitDebugger(Enable);
return DebuggerNotPresent;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtQueryDebugFilterState(
_In_ ULONG ComponentId,
_In_ ULONG Level)
{
PULONG Mask;
/* Check if the ID fits in the component table */
if (ComponentId < KdComponentTableSize)
{
/* It does, so get the mask from there */
Mask = KdComponentTable[ComponentId];
}
else if (ComponentId == MAXULONG)
{
/*
* This is the internal ID used for DbgPrint messages without ID
* and Level. Use the system-wide mask for those.
*/
Mask = &Kd_WIN2000_Mask;
}
else
{
#if (NTDDI_VERSION >= NTDDI_VISTA)
/* Use the default component ID */
Mask = &Kd_DEFAULT_Mask;
// Level = DPFLTR_INFO_LEVEL; // Override the Level.
#else
/* Invalid ID, fail */
return STATUS_INVALID_PARAMETER_1;
#endif
}
/* Convert Level to bit field if required */
if (Level < 32) Level = 1 << Level;
Level &= ~DPFLTR_MASK;
/* Determine if this Level is filtered out */
if ((Kd_WIN2000_Mask & Level) || (*Mask & Level))
{
/* This mask will get through to the debugger */
return (NTSTATUS)TRUE;
}
else
{
/* This mask is filtered out */
return (NTSTATUS)FALSE;
}
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtSetDebugFilterState(
_In_ ULONG ComponentId,
_In_ ULONG Level,
_In_ BOOLEAN State)
{
PULONG Mask;
/* Modifying debug filters requires the debug privilege */
if (!SeSinglePrivilegeCheck(SeDebugPrivilege, ExGetPreviousMode()))
{
/* Fail */
return STATUS_ACCESS_DENIED;
}
/* Check if the ID fits in the component table */
if (ComponentId < KdComponentTableSize)
{
/* It does, so get the mask from there */
Mask = KdComponentTable[ComponentId];
}
else if (ComponentId == MAXULONG)
{
/*
* This is the internal ID used for DbgPrint messages without ID
* and Level. Use the system-wide mask for those.
*/
Mask = &Kd_WIN2000_Mask;
}
else
{
#if (NTDDI_VERSION >= NTDDI_VISTA)
/* Use the default component ID */
Mask = &Kd_DEFAULT_Mask;
#else
/* Invalid ID, fail */
return STATUS_INVALID_PARAMETER_1;
#endif
}
/* Convert Level to bit field if required */
if (Level < 32) Level = 1 << Level;
Level &= ~DPFLTR_MASK;
/* Set or remove the Level */
if (State)
*Mask |= Level;
else
*Mask &= ~Level;
return STATUS_SUCCESS;
}