mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
2488 lines
70 KiB
C
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;
|
|
}
|