3002 lines
98 KiB
C++
3002 lines
98 KiB
C++
|
//
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
//
|
|||
|
// halkd.cpp: runtime support for debugging
|
|||
|
//
|
|||
|
// For more information see:
|
|||
|
// \nt\base\ntos\kd64
|
|||
|
// \nt\base\boot\kdcom
|
|||
|
// \nt\base\boot\kd1394
|
|||
|
// \nt\base\boot\kdusb2
|
|||
|
// \nt\sdktools\debuggers\ntsd64
|
|||
|
//
|
|||
|
#include "hal.h"
|
|||
|
#include "halkd.h"
|
|||
|
|
|||
|
extern "C" void * __cdecl memcpy(void *, const void *, size_t);
|
|||
|
extern "C" void * __cdecl memset(void *, int, size_t);
|
|||
|
|
|||
|
//
|
|||
|
// Debugger Debugging
|
|||
|
//
|
|||
|
#define KDDBG if (0) kdprintf
|
|||
|
#define KDDBG2 if (0) kdprintf
|
|||
|
|
|||
|
#if defined(_IA64_)
|
|||
|
#define SIGN_EXTEND_PTR(p) p
|
|||
|
#else
|
|||
|
#define SIGN_EXTEND_PTR(p) ((ULONG_PTR)(LONG_PTR)(LONG)(p))
|
|||
|
#endif
|
|||
|
|
|||
|
#define KeProcessorLevel 15
|
|||
|
|
|||
|
//
|
|||
|
// Globals
|
|||
|
//
|
|||
|
extern const Struct_Microsoft_Singularity_BootInfo *g_pBootInfo;
|
|||
|
static Struct_Microsoft_Singularity_X86_IDTP g_idt;
|
|||
|
|
|||
|
BOOL KdDebuggerNotPresent = FALSE;
|
|||
|
|
|||
|
#define KDP_MESSAGE_BUFFER_SIZE 4096
|
|||
|
static CHAR KdpMessageBuffer[KDP_MESSAGE_BUFFER_SIZE];
|
|||
|
static BOOL KdpContextSent;
|
|||
|
|
|||
|
static KPROCESSOR_STATE KdpProcessorState[MAX_CPU];
|
|||
|
static KD_CONTEXT KdpContext;
|
|||
|
static int KeNumberProcessors = 1;
|
|||
|
static UINT16 KdpSpinBase = 0x2f00;
|
|||
|
|
|||
|
KDP_BREAKPOINT_TYPE KdpBreakpointInstruction = KDP_BREAKPOINT_VALUE;
|
|||
|
BREAKPOINT_ENTRY KdpBreakpointTable[BREAKPOINT_TABLE_SIZE] = {0};
|
|||
|
|
|||
|
//
|
|||
|
// KdpRetryCount controls the number of retries before we give
|
|||
|
// up and assume kernel debugger is not present.
|
|||
|
// KdpNumberRetries is the number of retries left. Initially,
|
|||
|
// it is set to 5 such that booting NT without debugger won't be
|
|||
|
// delayed to long.
|
|||
|
//
|
|||
|
ULONG KdCompNumberRetries = 5;
|
|||
|
ULONG KdCompRetryCount = 5;
|
|||
|
ULONG KdPacketId = 0;
|
|||
|
|
|||
|
void (*KdSendPacket)(ULONG PacketType,
|
|||
|
IN PSTRING MessageHeader,
|
|||
|
IN PSTRING MessageData OPTIONAL,
|
|||
|
IN OUT PKD_CONTEXT KdContext) = NULL;
|
|||
|
KDP_STATUS (*KdReceivePacket)(IN ULONG PacketType,
|
|||
|
OUT PSTRING MessageHeader,
|
|||
|
OUT PSTRING MessageData,
|
|||
|
OUT PULONG DataLength,
|
|||
|
IN OUT PKD_CONTEXT KdContext) = NULL;
|
|||
|
bool (*KdPollBreakIn)() = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Static data - these are walked by the kernel debugger
|
|||
|
//
|
|||
|
|
|||
|
//PsLoadedModuleList ===========================================================
|
|||
|
|
|||
|
struct KLDR_DATA_TABLE_ENTRY_WITH_NAME : KLDR_DATA_TABLE_ENTRY
|
|||
|
{
|
|||
|
WCHAR wzName[32];
|
|||
|
};
|
|||
|
|
|||
|
static KLDR_DATA_TABLE_ENTRY_WITH_NAME KdModuleKernelEntry[128];
|
|||
|
static ULONG KdModuleKernelUsed = 0;
|
|||
|
static LIST_ENTRY PsLoadedModuleList;
|
|||
|
static PMINIDUMP_MODULE_LIST KdMinidumpModuleList = NULL;
|
|||
|
|
|||
|
// KdVersionBlock =============================================================
|
|||
|
|
|||
|
static DBGKD_GET_VERSION64 KdVersionBlock = {
|
|||
|
DBGKD_MAJOR_SINGULARITY << 8, // MajorVersion ... this one sort of works
|
|||
|
0, // Minor
|
|||
|
DBGKD_64BIT_PROTOCOL_VERSION2, // Protocol
|
|||
|
DBGKD_VERS_FLAG_NOMM, //DBGKD_VERS_FLAG_DATA, // Flags
|
|||
|
IMAGE_FILE_MACHINE_I386, // Machine Type
|
|||
|
PACKET_TYPE_MAX, // Max packet
|
|||
|
DbgKdMaximumStateChange - DbgKdMinimumStateChange, // MaxStateChange
|
|||
|
DbgKdSetContextApi /*DbgKdMaximumManipulate*/ - DbgKdMinimumManipulate, // MaxManipulate we support
|
|||
|
0, // Simulation
|
|||
|
0, // Unused
|
|||
|
0, // KernBase
|
|||
|
(ULONG64)&PsLoadedModuleList, // PsLoadedModuleList
|
|||
|
0 // DebuggerDataList
|
|||
|
};
|
|||
|
|
|||
|
// ========================================================================
|
|||
|
|
|||
|
// Forward declarations
|
|||
|
|
|||
|
static void KdpLock(void);
|
|||
|
static void KdpUnlock(void);
|
|||
|
|
|||
|
static void KdpEnter(void);
|
|||
|
static void KdpLeave(void);
|
|||
|
|
|||
|
static void KdpFakeOutPsLoadedModuleList(void);
|
|||
|
|
|||
|
static KCONTINUE_STATUS
|
|||
|
KdpSendWaitContinue(
|
|||
|
IN ULONG OutPacketType,
|
|||
|
IN PSTRING OutMessageHeader,
|
|||
|
IN PSTRING OutMessageData OPTIONAL,
|
|||
|
IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
);
|
|||
|
|
|||
|
static void LoadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context);
|
|||
|
static void UnloadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context);
|
|||
|
|
|||
|
extern int printf(const char *pszFmt, ...);
|
|||
|
|
|||
|
///////////////////////////////////////// Debugger Unique Interrupts Routines.
|
|||
|
//
|
|||
|
// NB: Without these, we share routines with the mainline code and we get
|
|||
|
// caught in a loop when the debugger inserts a break after the pushfd when
|
|||
|
// someone tries to single step through Processor:g_DisableInterrupts!
|
|||
|
//
|
|||
|
static __declspec(naked) bool KdpDisableInterrupts()
|
|||
|
{
|
|||
|
__asm {
|
|||
|
pushfd;
|
|||
|
pop eax;
|
|||
|
test eax, Struct_Microsoft_Singularity_X86_EFlags_IF;
|
|||
|
setnz al;
|
|||
|
nop; // required so that the linker doesn't combine with g_Disable
|
|||
|
cli;
|
|||
|
ret;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static __declspec(naked) void KdpRestoreInterrupts(bool enabled)
|
|||
|
{
|
|||
|
__asm {
|
|||
|
nop;
|
|||
|
test cl, cl;
|
|||
|
je done;
|
|||
|
nop; // required so that the linker doesn't combine with g_Restore
|
|||
|
sti;
|
|||
|
done:
|
|||
|
ret;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
static volatile INT32 KdpInDebugger = 0;
|
|||
|
static volatile bool KdpInDebuggerIntEnabled = FALSE;
|
|||
|
|
|||
|
#define MAXIMUM_RETRIES 20
|
|||
|
|
|||
|
static void KdpNulSendPacket(ULONG PacketType,
|
|||
|
IN PSTRING MessageHeader,
|
|||
|
IN PSTRING MessageData OPTIONAL,
|
|||
|
IN OUT PKD_CONTEXT KdContext)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
static KDP_STATUS KdpNulReceivePacket(IN ULONG PacketType,
|
|||
|
OUT PSTRING MessageHeader,
|
|||
|
OUT PSTRING MessageData,
|
|||
|
OUT PULONG DataLength,
|
|||
|
IN OUT PKD_CONTEXT KdContext)
|
|||
|
{
|
|||
|
return KDP_PACKET_TIMEOUT;
|
|||
|
}
|
|||
|
|
|||
|
static bool KdpNulPollBreakIn()
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
void KdInitialize(Struct_Microsoft_Singularity_BootInfo *bi)
|
|||
|
{
|
|||
|
KdpLock();
|
|||
|
|
|||
|
if (KdpComInit(bi)) {
|
|||
|
KdpSpinBase = 0x2f00;
|
|||
|
KdSendPacket = KdpComSendPacket;
|
|||
|
KdReceivePacket = KdpComReceivePacket;
|
|||
|
KdPollBreakIn = KdpComPollBreakIn;
|
|||
|
kdprintf("Serial Port (bi->DebugBasePort=%x).\n", bi->DebugBasePort);
|
|||
|
}
|
|||
|
else if (Kdp1394Init(bi)) {
|
|||
|
KdpSpinBase = 0x4f00;
|
|||
|
KdSendPacket = Kdp1394SendPacket;
|
|||
|
KdReceivePacket = Kdp1394ReceivePacket;
|
|||
|
KdPollBreakIn = Kdp1394PollBreakIn;
|
|||
|
kdprintf("1394 Port (bi->DebugBasePort=%x).\n", bi->DebugBasePort);
|
|||
|
}
|
|||
|
else {
|
|||
|
kdprintf("No debugger.\n");
|
|||
|
KdSendPacket = KdpNulSendPacket;
|
|||
|
KdReceivePacket = KdpNulReceivePacket;
|
|||
|
KdPollBreakIn = KdpNulPollBreakIn;
|
|||
|
KdDebuggerNotPresent = true;
|
|||
|
}
|
|||
|
|
|||
|
// Retries are set to this after boot
|
|||
|
KdpContext.KdpDefaultRetries = MAXIMUM_RETRIES;
|
|||
|
|
|||
|
KdpUnlock();
|
|||
|
|
|||
|
KdpFakeOutPsLoadedModuleList();
|
|||
|
}
|
|||
|
|
|||
|
static void KdpLock()
|
|||
|
{
|
|||
|
for (;;) {
|
|||
|
bool enabled = KdpDisableInterrupts();
|
|||
|
if (InterlockedCompareExchange(&KdpInDebugger, 1, 0) == 0) {
|
|||
|
KdpInDebuggerIntEnabled = enabled;
|
|||
|
return;
|
|||
|
}
|
|||
|
KdpRestoreInterrupts(enabled);
|
|||
|
__asm pause
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void KdpUnlock()
|
|||
|
{
|
|||
|
KdpInDebugger = 0;
|
|||
|
KdpRestoreInterrupts(KdpInDebuggerIntEnabled);
|
|||
|
}
|
|||
|
|
|||
|
static void KdpEnter()
|
|||
|
{
|
|||
|
if (KeNumberProcessors > 1) {
|
|||
|
Class_Microsoft_Singularity_MpExecution::g_FreezeAllProcessors();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void KdpLeave()
|
|||
|
{
|
|||
|
if (KeNumberProcessors > 1) {
|
|||
|
Class_Microsoft_Singularity_MpExecution::g_ThawAllProcessors();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
extern int strformat(void (*pfOutput)(void *pContext, char c), void *pContext,
|
|||
|
const char * pszFmt, va_list args);
|
|||
|
|
|||
|
#define KD_LEFT 0
|
|||
|
#define KD_HEIGHT 46
|
|||
|
|
|||
|
static UINT16 kdcurs = KD_LEFT;
|
|||
|
static UINT16 kdattr = 0x2f00;
|
|||
|
|
|||
|
static void koutput(void *pContext, char c)
|
|||
|
{
|
|||
|
//
|
|||
|
// Update cursor position
|
|||
|
//
|
|||
|
if ((kdcurs % 80) < KD_LEFT) {
|
|||
|
kdcurs += KD_LEFT - (kdcurs % 80);
|
|||
|
}
|
|||
|
|
|||
|
if (kdcurs >= KD_HEIGHT * 80) {
|
|||
|
for (UINT16 i = 0; i < KD_HEIGHT - 1; i++) {
|
|||
|
for (UINT16 j = KD_LEFT; j < 80; j++) {
|
|||
|
((UINT16 *)0xb8000)[i*80+j] = ((UINT16 *)0xb8000)[i*80+80+j];
|
|||
|
}
|
|||
|
}
|
|||
|
for (UINT16 j = KD_LEFT; j < 80; j++) {
|
|||
|
((UINT16 *)0xb8000)[(KD_HEIGHT-1)*80+j] = kdattr | ' ';
|
|||
|
}
|
|||
|
kdcurs = kdcurs - 80;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Output character
|
|||
|
//
|
|||
|
if (c >= ' ' && c <= '~') {
|
|||
|
((UINT16 *)0xb8000)[kdcurs++] = kdattr | c;
|
|||
|
}
|
|||
|
else if (c == '\t') {
|
|||
|
kdcurs += 8 - (kdcurs % 8);
|
|||
|
}
|
|||
|
else if (c == '\n') {
|
|||
|
while ((kdcurs % 80) != 0) {
|
|||
|
((UINT16 *)0xb8000)[kdcurs++] = kdattr | ' ';
|
|||
|
}
|
|||
|
}
|
|||
|
else if (c == '\r') {
|
|||
|
kdcurs -= (kdcurs % 80);
|
|||
|
}
|
|||
|
else if (c == '\f') {
|
|||
|
kdcurs = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void kdprints(const char * pszFmt)
|
|||
|
{
|
|||
|
while (*pszFmt) {
|
|||
|
koutput(NULL, *pszFmt++);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void kdprintf(const char * pszFmt, ...)
|
|||
|
{
|
|||
|
va_list args;
|
|||
|
|
|||
|
va_start(args, pszFmt);
|
|||
|
strformat(koutput, NULL, pszFmt, args);
|
|||
|
va_end(args);
|
|||
|
}
|
|||
|
|
|||
|
void KdpSpin()
|
|||
|
{
|
|||
|
static UINT8 state = 0;
|
|||
|
|
|||
|
*((UINT16 *)0xb809e) = KdpSpinBase + ("+-|*" [state++ & 0x3]);
|
|||
|
}
|
|||
|
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
ULONG KdpComputeChecksum(IN PCHAR Buffer, IN ULONG Length)
|
|||
|
{
|
|||
|
// Compute the checksum for the string passed in.
|
|||
|
ULONG Checksum = 0;
|
|||
|
|
|||
|
while (Length > 0) {
|
|||
|
Checksum = Checksum + (ULONG)*(PUCHAR)Buffer++;
|
|||
|
Length--;
|
|||
|
}
|
|||
|
|
|||
|
return(Checksum);
|
|||
|
} // KdpComputeChecksum
|
|||
|
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
static
|
|||
|
VOID KdSingularityToWindbgContext(IN CONST Struct_Microsoft_Singularity_X86_ThreadContext *singularity,
|
|||
|
OUT CONTEXT *windbg)
|
|||
|
{
|
|||
|
RtlZeroMemory(windbg, sizeof(*windbg));
|
|||
|
|
|||
|
windbg->ContextFlags = (CONTEXT_CONTROL |
|
|||
|
CONTEXT_INTEGER |
|
|||
|
CONTEXT_SEGMENTS |
|
|||
|
CONTEXT_DEBUG_REGISTERS);
|
|||
|
|
|||
|
// CONTEXT_FULL;
|
|||
|
windbg->Eax = singularity->eax;
|
|||
|
windbg->Ebx = singularity->ebx;
|
|||
|
windbg->Ecx = singularity->ecx;
|
|||
|
windbg->Edx = singularity->edx;
|
|||
|
windbg->Esp = singularity->esp;
|
|||
|
windbg->Ebp = singularity->ebp;
|
|||
|
windbg->Esi = singularity->esi;
|
|||
|
windbg->Edi = singularity->edi;
|
|||
|
windbg->Eip = singularity->eip;
|
|||
|
windbg->EFlags = singularity->efl;
|
|||
|
|
|||
|
// CONTEXT_FLOATING_POINT
|
|||
|
if (singularity->regs) {
|
|||
|
windbg->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|||
|
|
|||
|
#if 0
|
|||
|
PUCHAR pmmx = (PUCHAR)&singularity->mmx;
|
|||
|
kdprintf(" MMX=%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
|
|||
|
pmmx[0], pmmx[1], pmmx[2], pmmx[3],
|
|||
|
pmmx[4], pmmx[5], pmmx[6], pmmx[7],
|
|||
|
pmmx[8], pmmx[9], pmmx[10], pmmx[11],
|
|||
|
pmmx[12], pmmx[13], pmmx[14], pmmx[15]);
|
|||
|
kdprintf(" MMX=%04x %04x %02x %03x %04x:%08x %04x:%08x\n",
|
|||
|
singularity->mmx.fcw,
|
|||
|
singularity->mmx.fsw,
|
|||
|
singularity->mmx.ftw,
|
|||
|
singularity->mmx.fop,
|
|||
|
singularity->mmx.cs,
|
|||
|
singularity->mmx.eip,
|
|||
|
singularity->mmx.ds,
|
|||
|
singularity->mmx.dp);
|
|||
|
#endif
|
|||
|
|
|||
|
windbg->FloatSave.ControlWord = singularity->mmx.fcw;
|
|||
|
windbg->FloatSave.StatusWord = singularity->mmx.fsw;
|
|||
|
windbg->FloatSave.TagWord = singularity->mmx.ftw;
|
|||
|
windbg->FloatSave.ErrorOffset = singularity->mmx.eip;
|
|||
|
windbg->FloatSave.ErrorSelector = singularity->mmx.cs;
|
|||
|
windbg->FloatSave.DataOffset = singularity->mmx.dp;
|
|||
|
windbg->FloatSave.DataSelector = singularity->mmx.ds;
|
|||
|
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+0, &singularity->mmx.st0, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+10, &singularity->mmx.st1, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+20, &singularity->mmx.st2, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+30, &singularity->mmx.st3, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+40, &singularity->mmx.st4, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+50, &singularity->mmx.st5, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+60, &singularity->mmx.st6, 10);
|
|||
|
memcpy((uint8*)windbg->FloatSave.RegisterArea+70, &singularity->mmx.st7, 10);
|
|||
|
}
|
|||
|
|
|||
|
// CONTEXT_EXTENDED_REGISTERS
|
|||
|
if (singularity->regs & 1) {
|
|||
|
windbg->ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
|
|||
|
memcpy(windbg->ExtendedRegisters, &singularity->mmx, 512);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
|
|||
|
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
|
|||
|
// included in CONTEXT_FULL.
|
|||
|
//
|
|||
|
windbg->Dr0 = singularity->dr0;
|
|||
|
windbg->Dr1 = singularity->dr1;
|
|||
|
windbg->Dr2 = singularity->dr2;
|
|||
|
windbg->Dr3 = singularity->dr3;
|
|||
|
windbg->Dr6 = singularity->dr6;
|
|||
|
windbg->Dr7 = singularity->dr7;
|
|||
|
|
|||
|
#if 0
|
|||
|
__asm {
|
|||
|
mov ebx, windbg;
|
|||
|
|
|||
|
mov eax, dr0;
|
|||
|
mov [ebx]CONTEXT.Dr0, eax;
|
|||
|
mov eax, dr1;
|
|||
|
mov [ebx]CONTEXT.Dr1, eax;
|
|||
|
mov eax, dr2;
|
|||
|
mov [ebx]CONTEXT.Dr2, eax;
|
|||
|
mov eax, dr3;
|
|||
|
mov [ebx]CONTEXT.Dr3, eax;
|
|||
|
mov eax, dr6;
|
|||
|
mov [ebx]CONTEXT.Dr6, eax;
|
|||
|
mov eax, dr7;
|
|||
|
mov [ebx]CONTEXT.Dr7, eax;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if 1
|
|||
|
//
|
|||
|
// This section is specified/returned if the
|
|||
|
// ContextFlags word contains the flag CONTEXT_SEGMENTS.
|
|||
|
//
|
|||
|
__asm {
|
|||
|
mov ebx, windbg;
|
|||
|
|
|||
|
xor eax, eax;
|
|||
|
mov ax, gs;
|
|||
|
mov [ebx]CONTEXT.SegGs, eax;
|
|||
|
mov ax, fs;
|
|||
|
mov [ebx]CONTEXT.SegFs, eax;
|
|||
|
mov ax, es;
|
|||
|
mov [ebx]CONTEXT.SegEs, eax;
|
|||
|
mov ax, ds;
|
|||
|
mov [ebx]CONTEXT.SegDs, eax;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This section is specified/returned if the
|
|||
|
// ContextFlags word contains the flag CONTEXT_CONTROL.
|
|||
|
//
|
|||
|
|
|||
|
__asm {
|
|||
|
mov ebx, windbg;
|
|||
|
|
|||
|
xor eax, eax;
|
|||
|
mov ax, ss;
|
|||
|
mov [ebx]CONTEXT.SegSs, eax;
|
|||
|
}
|
|||
|
|
|||
|
windbg->SegCs = singularity->cs0;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
VOID KdWindbgToSingularityContext(IN CONST CONTEXT *windbg,
|
|||
|
OUT Struct_Microsoft_Singularity_X86_ThreadContext *singularity)
|
|||
|
{
|
|||
|
singularity->eax = windbg->Eax;
|
|||
|
singularity->ebx = windbg->Ebx;
|
|||
|
singularity->ecx = windbg->Ecx;
|
|||
|
singularity->edx = windbg->Edx;
|
|||
|
singularity->esp = windbg->Esp;
|
|||
|
singularity->ebp = windbg->Ebp;
|
|||
|
singularity->esi = windbg->Esi;
|
|||
|
singularity->edi = windbg->Edi;
|
|||
|
singularity->eip = windbg->Eip;
|
|||
|
singularity->efl = windbg->EFlags;
|
|||
|
|
|||
|
// CONTEXT_FLOATING_POINT
|
|||
|
if (windbg->ContextFlags & CONTEXT_FLOATING_POINT) {
|
|||
|
singularity->mmx.fcw = (uint16)windbg->FloatSave.ControlWord;
|
|||
|
singularity->mmx.fsw = (uint16)windbg->FloatSave.StatusWord;
|
|||
|
singularity->mmx.ftw = (uint16)windbg->FloatSave.TagWord;
|
|||
|
singularity->mmx.cs = (uint16)windbg->FloatSave.ErrorSelector;
|
|||
|
singularity->mmx.eip = windbg->FloatSave.ErrorOffset;
|
|||
|
singularity->mmx.ds = (uint16)windbg->FloatSave.DataSelector;
|
|||
|
singularity->mmx.dp = windbg->FloatSave.DataOffset;
|
|||
|
memcpy(&singularity->mmx.st0, windbg->FloatSave.RegisterArea+0, 10);
|
|||
|
memcpy(&singularity->mmx.st1, windbg->FloatSave.RegisterArea+10, 10);
|
|||
|
memcpy(&singularity->mmx.st2, windbg->FloatSave.RegisterArea+20, 10);
|
|||
|
memcpy(&singularity->mmx.st3, windbg->FloatSave.RegisterArea+30, 10);
|
|||
|
memcpy(&singularity->mmx.st4, windbg->FloatSave.RegisterArea+40, 10);
|
|||
|
memcpy(&singularity->mmx.st5, windbg->FloatSave.RegisterArea+50, 10);
|
|||
|
memcpy(&singularity->mmx.st6, windbg->FloatSave.RegisterArea+60, 10);
|
|||
|
memcpy(&singularity->mmx.st7, windbg->FloatSave.RegisterArea+70, 10);
|
|||
|
}
|
|||
|
|
|||
|
// CONTEXT_EXTENDED_REGISTERS
|
|||
|
if (windbg->ContextFlags & CONTEXT_EXTENDED_REGISTERS) {
|
|||
|
memcpy(&singularity->mmx, windbg->ExtendedRegisters, 512);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
|
|||
|
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
|
|||
|
// included in CONTEXT_FULL.
|
|||
|
//
|
|||
|
if (windbg->ContextFlags & CONTEXT_DEBUG_REGISTERS) {
|
|||
|
singularity->dr0 = windbg->Dr0;
|
|||
|
singularity->dr1 = windbg->Dr1;
|
|||
|
singularity->dr2 = windbg->Dr2;
|
|||
|
singularity->dr3 = windbg->Dr3;
|
|||
|
singularity->dr6 = windbg->Dr6;
|
|||
|
singularity->dr7 = windbg->Dr7;
|
|||
|
#if 0
|
|||
|
__asm {
|
|||
|
mov ebx, windbg;
|
|||
|
mov eax, [ebx]CONTEXT.Dr0;
|
|||
|
mov dr0, eax;
|
|||
|
mov eax, [ebx]CONTEXT.Dr1;
|
|||
|
mov dr1, eax;
|
|||
|
mov eax, [ebx]CONTEXT.Dr2;
|
|||
|
mov dr2, eax;
|
|||
|
mov eax, [ebx]CONTEXT.Dr3;
|
|||
|
mov dr3, eax;
|
|||
|
mov eax, 0; //CONTEXT.Dr6 : DR6 is reset when changing BPTs.
|
|||
|
mov dr6, eax;
|
|||
|
mov eax, [ebx]CONTEXT.Dr7;
|
|||
|
mov eax, dr7;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
//
|
|||
|
// Misc KD functions
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KdpCopyMemoryChunks(
|
|||
|
ULONG64 Address,
|
|||
|
PVOID Buffer,
|
|||
|
ULONG TotalSize,
|
|||
|
ULONG ChunkSize,
|
|||
|
ULONG Flags,
|
|||
|
PULONG ActualSize OPTIONAL
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// Copies memory to/from a buffer to/from a system address.
|
|||
|
// The address can be physical or virtual.
|
|||
|
// The buffer is assumed to be valid for the duration of this call.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// Address - System address.
|
|||
|
// Buffer - Buffer to read from or write to.
|
|||
|
// TotalSize - Number of bytes to read/write.
|
|||
|
// ChunkSize - Maximum single item transfer size, must
|
|||
|
// be 1, 2, 4 or 8.
|
|||
|
// 0 means choose a default.
|
|||
|
// Flags - MMDBG_COPY flags for MmDbgCopyMemory.
|
|||
|
// ActualSize - Number of bytes actually read/written.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// NTSTATUS
|
|||
|
{
|
|||
|
ULONG Length;
|
|||
|
ULONG CopyChunk;
|
|||
|
// NTSTATUS Status;
|
|||
|
#if defined(_IA64_)
|
|||
|
ULONG64 AddressStart = Address;
|
|||
|
#endif
|
|||
|
|
|||
|
if (ChunkSize > MMDBG_COPY_MAX_SIZE) {
|
|||
|
ChunkSize = MMDBG_COPY_MAX_SIZE;
|
|||
|
} else if (ChunkSize == 0) {
|
|||
|
// Default to 4 byte chunks as that's
|
|||
|
// what the previous code did.
|
|||
|
ChunkSize = 4;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// MmDbgCopyMemory only copies a single aligned chunk at a
|
|||
|
// time. It is Kd's responsibility to chunk up a larger
|
|||
|
// request for individual copy requests. This gives Kd
|
|||
|
// the flexibility to pick a chunk size and also frees
|
|||
|
// Mm from having to worry about more than a page at a time.
|
|||
|
// Additionally, it is important that we access memory with the
|
|||
|
// largest size possible because we could be accessing
|
|||
|
// memory-mapped I/O space.
|
|||
|
//
|
|||
|
|
|||
|
Length = TotalSize;
|
|||
|
CopyChunk = 1;
|
|||
|
|
|||
|
while (Length > 0) {
|
|||
|
|
|||
|
// Expand the chunk size as long as:
|
|||
|
// We haven't hit the chunk limit.
|
|||
|
// We have enough data left.
|
|||
|
// The address is properly aligned.
|
|||
|
while (CopyChunk < ChunkSize &&
|
|||
|
(CopyChunk << 1) <= Length &&
|
|||
|
(Address & ((CopyChunk << 1) - 1)) == 0) {
|
|||
|
CopyChunk <<= 1;
|
|||
|
}
|
|||
|
|
|||
|
// Shrink the chunk size to fit the available data.
|
|||
|
while (CopyChunk > Length) {
|
|||
|
CopyChunk >>= 1;
|
|||
|
}
|
|||
|
|
|||
|
Address &= 0xffffffff;
|
|||
|
if (Address < Struct_Microsoft_Singularity_BootInfo_PHYSICAL_DISABLED) {
|
|||
|
break;
|
|||
|
}
|
|||
|
if (Address == 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
#if PAGING
|
|||
|
ULONG64 RawAddress = Address;
|
|||
|
if (Flags & MMDBG_COPY_PHYSICAL) {
|
|||
|
// Temporarily map the physical memory range.
|
|||
|
// TODO: KernelMapPhysicalMemory tries to acquire a lock -- if
|
|||
|
// this lock is not free, we may deadlock here.
|
|||
|
// Also, remapping for every chunk is inefficient.
|
|||
|
KDDBG("Physical address = 0x%x size = 0x%x\n", int(Address), int(CopyChunk));
|
|||
|
Struct_Microsoft_Singularity_Memory_PhysicalAddress physical;
|
|||
|
Struct_Microsoft_Singularity_Memory_PhysicalAddress::m__ctor(
|
|||
|
&physical,
|
|||
|
UIntPtr(Address));
|
|||
|
Address = ULONG64(Class_Microsoft_Singularity_Memory_MemoryManager
|
|||
|
::g_KernelMapPhysicalMemory(
|
|||
|
physical,
|
|||
|
UIntPtr(CopyChunk)));
|
|||
|
KDDBG("Physical address 0x%x mapped to virtual address 0x%x\n", int(RawAddress), int(Address));
|
|||
|
}
|
|||
|
else {
|
|||
|
if (Class_Microsoft_Singularity_Memory_MemoryManager::c_isInitialized
|
|||
|
&& !Class_Microsoft_Singularity_Memory_VMManager::g_IsPageMapped(
|
|||
|
Class_Microsoft_Singularity_Memory_MemoryManager::g_PageAlign(
|
|||
|
UIntPtr(Address)))) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (Flags & MMDBG_COPY_WRITE) {
|
|||
|
memcpy((void*)Address, Buffer, CopyChunk);
|
|||
|
} else {
|
|||
|
memcpy(Buffer, (void*)Address, CopyChunk);
|
|||
|
}
|
|||
|
|
|||
|
#if PAGING
|
|||
|
if (Flags & MMDBG_COPY_PHYSICAL) {
|
|||
|
KDDBG("Unmapping physical address 0x%x (virtual address 0x%x)\n", int(RawAddress), int(Address));
|
|||
|
Class_Microsoft_Singularity_Memory_MemoryManager
|
|||
|
::g_KernelUnmapPhysicalMemory(
|
|||
|
UIntPtr(Address), UIntPtr(Address + CopyChunk));
|
|||
|
KDDBG("Unmapped physical address 0x%x (virtual address 0x%x)\n", int(RawAddress), int(Address));
|
|||
|
Address = RawAddress;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
Address += CopyChunk;
|
|||
|
Buffer = (PVOID)((PUCHAR)Buffer + CopyChunk);
|
|||
|
Length -= CopyChunk;
|
|||
|
}
|
|||
|
|
|||
|
if (ActualSize) {
|
|||
|
*ActualSize = TotalSize - Length;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Flush the instruction cache in case the write was into the instruction
|
|||
|
// stream. Only do this when writing into the kernel address space,
|
|||
|
// and if any bytes were actually written
|
|||
|
//
|
|||
|
|
|||
|
if ((Flags & MMDBG_COPY_WRITE) && Length < TotalSize) {
|
|||
|
#if defined(_IA64_)
|
|||
|
//
|
|||
|
// KeSweepCurrentIcacheRange requires a valid virtual address.
|
|||
|
// It is used because KeSweepCurrentICache does not work until
|
|||
|
// the HAL as been initialized.
|
|||
|
//
|
|||
|
|
|||
|
if (Flags & MMDBG_COPY_PHYSICAL) {
|
|||
|
KeSweepCurrentIcache();
|
|||
|
}
|
|||
|
else {
|
|||
|
KeSweepCurrentIcacheRange((PVOID)AddressStart, TotalSize - Length);
|
|||
|
}
|
|||
|
#else
|
|||
|
__asm wbinvd;
|
|||
|
// KeSweepCurrentIcache();
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return Length != 0 ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpSetCommonState(
|
|||
|
IN ULONG NewState,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context,
|
|||
|
OUT PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange
|
|||
|
)
|
|||
|
{
|
|||
|
PCHAR PcMemory = (PCHAR)x86Context->eip;
|
|||
|
ULONG InstrCount;
|
|||
|
PUCHAR InstrStream;
|
|||
|
|
|||
|
WaitStateChange->NewState = NewState;
|
|||
|
WaitStateChange->ProcessorLevel = KeProcessorLevel;
|
|||
|
WaitStateChange->Processor = GetCurrentProcessorNumber();
|
|||
|
WaitStateChange->NumberProcessors = KeNumberProcessors;
|
|||
|
WaitStateChange->Thread = SIGN_EXTEND_PTR(x86Context->_thread);
|
|||
|
WaitStateChange->ProgramCounter = SIGN_EXTEND_PTR(x86Context->eip);
|
|||
|
|
|||
|
RtlZeroMemory(&WaitStateChange->AnyControlReport,
|
|||
|
sizeof(WaitStateChange->AnyControlReport));
|
|||
|
|
|||
|
//
|
|||
|
// Copy instruction stream immediately following location of event.
|
|||
|
//
|
|||
|
|
|||
|
InstrStream = WaitStateChange->ControlReport.InstructionStream;
|
|||
|
KdpCopyFromPtr(InstrStream, PcMemory, DBGKD_MAXSTREAM, &InstrCount);
|
|||
|
WaitStateChange->ControlReport.InstructionCount = (USHORT)InstrCount;
|
|||
|
|
|||
|
//
|
|||
|
// Clear breakpoints in copied area.
|
|||
|
// If there were any breakpoints cleared, recopy the instruction area
|
|||
|
// without them.
|
|||
|
//
|
|||
|
|
|||
|
// if (KdpDeleteBreakpointRange(PcMemory, PcMemory + InstrCount - 1)) {
|
|||
|
// KdpCopyFromPtr(InstrStream, PcMemory, InstrCount, &InstrCount);
|
|||
|
// }
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KdpSetContextState(
|
|||
|
IN OUT PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// Fill in the Wait_State_Change message record.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// WaitStateChange - Supplies pointer to record to fill in
|
|||
|
// x86Context - Supplies a pointer to a context record.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
#if 0
|
|||
|
PKPRCB Prcb;
|
|||
|
|
|||
|
//
|
|||
|
// Special registers for the x86
|
|||
|
//
|
|||
|
Prcb = KeGetCurrentPrcb();
|
|||
|
WaitStateChange->ControlReport.Dr6 = Prcb->ProcessorState.SpecialRegisters.KernelDr6;
|
|||
|
WaitStateChange->ControlReport.Dr7 = Prcb->ProcessorState.SpecialRegisters.KernelDr7;
|
|||
|
#endif
|
|||
|
|
|||
|
UINT32 _dr6;
|
|||
|
UINT32 _dr7;
|
|||
|
UINT16 _cs;
|
|||
|
UINT16 _ds;
|
|||
|
UINT16 _es;
|
|||
|
UINT16 _fs;
|
|||
|
|
|||
|
__asm {
|
|||
|
mov eax, dr6;
|
|||
|
mov _dr6, eax;
|
|||
|
mov eax, dr7;
|
|||
|
mov _dr7, eax;
|
|||
|
mov ax, cs;
|
|||
|
mov _cs, ax;
|
|||
|
mov ax, ds;
|
|||
|
mov _ds, ax;
|
|||
|
mov ax, es;
|
|||
|
mov _es, ax;
|
|||
|
mov ax, fs;
|
|||
|
mov _fs, ax;
|
|||
|
}
|
|||
|
|
|||
|
// I'm not sure we're handling dr6 right here.
|
|||
|
KDDBG("KdpSetContextState dr6=%08x dr7=%08x cs=%04x/%04x\n",
|
|||
|
_dr6, _dr7, _cs, _ds);
|
|||
|
|
|||
|
WaitStateChange->ControlReport.Dr6 = _dr6;
|
|||
|
WaitStateChange->ControlReport.Dr7 = _dr7;
|
|||
|
WaitStateChange->ControlReport.SegCs = _cs;
|
|||
|
WaitStateChange->ControlReport.SegDs = _ds;
|
|||
|
WaitStateChange->ControlReport.SegEs = _es;
|
|||
|
WaitStateChange->ControlReport.SegFs = _fs;
|
|||
|
WaitStateChange->ControlReport.EFlags = x86Context->efl;
|
|||
|
WaitStateChange->ControlReport.ReportFlags = X86_REPORT_INCLUDES_SEGS;
|
|||
|
|
|||
|
#if !PAGING
|
|||
|
// Let the debugger know so that it doesn't have to retrieve the CS descriptor.
|
|||
|
WaitStateChange->ControlReport.ReportFlags |= X86_REPORT_STANDARD_CS;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
WcsToStr(WCHAR *pwcsSrc, ULONG Length, PCHAR pszDst)
|
|||
|
{
|
|||
|
for (ULONG n = 0; n < Length; n++) {
|
|||
|
*pszDst++ = (char)*pwcsSrc++;
|
|||
|
}
|
|||
|
*pszDst++ = '\0';
|
|||
|
|
|||
|
return Length + 1;
|
|||
|
}
|
|||
|
|
|||
|
bool
|
|||
|
KdpReportLoadSymbolsStateChange(
|
|||
|
IN WCHAR *PathName,
|
|||
|
IN ULONG PathNameLength,
|
|||
|
IN ULONG64 BaseOfDll,
|
|||
|
IN ULONG ProcessId,
|
|||
|
IN ULONG CheckSum,
|
|||
|
IN ULONG SizeOfImage,
|
|||
|
IN BOOLEAN UnloadSymbols,
|
|||
|
IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This routine sends a load symbols state change packet to the kernel
|
|||
|
// debugger and waits for a manipulate state message.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// PathName - Supplies a pointer to the pathname of the image whose
|
|||
|
// symbols are to be loaded.
|
|||
|
// BaseOfDll - Supplies the base address where the image was loaded.
|
|||
|
// ProcessId - Unique identifier for process that is using
|
|||
|
// the symbols. -1 for system process.
|
|||
|
// CheckSum - Checksum from image header.
|
|||
|
// UnloadSymbol - TRUE if the symbols that were previously loaded for
|
|||
|
// the named image are to be unloaded from the debugger.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// A value of TRUE is returned if the exception is handled. Otherwise, a
|
|||
|
// value of FALSE is returned.
|
|||
|
{
|
|||
|
// NB: \nt\sdktools\debuggers\ntsd64\event.cpp
|
|||
|
// PathNameLength = 0, ProcessId = 0, BaseOfDll = -1 for reboot.
|
|||
|
// PathNameLength = 0, ProcessId = 0, BaseOfDll = -2 for hibernate.
|
|||
|
|
|||
|
STRING MessageData;
|
|||
|
STRING MessageHeader;
|
|||
|
DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange;
|
|||
|
KCONTINUE_STATUS Status;
|
|||
|
|
|||
|
KDDBG("KdpReportLoadSymbolsStateChange %p\n", x86Context);
|
|||
|
|
|||
|
do {
|
|||
|
//
|
|||
|
// Construct the wait state change message and message descriptor.
|
|||
|
//
|
|||
|
|
|||
|
KdpSetCommonState(DbgKdLoadSymbolsStateChange, x86Context,
|
|||
|
&WaitStateChange);
|
|||
|
|
|||
|
WaitStateChange.u.LoadSymbols.UnloadSymbols = UnloadSymbols;
|
|||
|
WaitStateChange.u.LoadSymbols.BaseOfDll = BaseOfDll;
|
|||
|
WaitStateChange.u.LoadSymbols.ProcessId = ProcessId;
|
|||
|
WaitStateChange.u.LoadSymbols.CheckSum = CheckSum;
|
|||
|
WaitStateChange.u.LoadSymbols.SizeOfImage = SizeOfImage;
|
|||
|
if (PathName != NULL) {
|
|||
|
WaitStateChange.u.LoadSymbols.PathNameLength =
|
|||
|
WcsToStr(PathName, PathNameLength, KdpMessageBuffer);
|
|||
|
}
|
|||
|
else {
|
|||
|
WaitStateChange.u.LoadSymbols.PathNameLength = 0;
|
|||
|
}
|
|||
|
MessageData.Buffer = KdpMessageBuffer;
|
|||
|
MessageData.Length = (USHORT)WaitStateChange.u.LoadSymbols.PathNameLength;
|
|||
|
|
|||
|
KdpSetContextState(&WaitStateChange, x86Context);
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(WaitStateChange);
|
|||
|
MessageHeader.Buffer = (PCHAR)&WaitStateChange;
|
|||
|
|
|||
|
Status = KdpSendWaitContinue(
|
|||
|
PACKET_TYPE_KD_STATE_CHANGE64,
|
|||
|
&MessageHeader,
|
|||
|
&MessageData,
|
|||
|
x86Context
|
|||
|
);
|
|||
|
|
|||
|
} while (Status == ContinueProcessorReselected) ;
|
|||
|
|
|||
|
return (Status == ContinueSuccess) ? true : false;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
KCONTINUE_STATUS
|
|||
|
KdpReportExceptionStateChange(
|
|||
|
IN PEXCEPTION_RECORD64 ExceptionRecord,
|
|||
|
IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context,
|
|||
|
IN BOOLEAN FirstChance
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This routine sends an exception state change packet to the kernel
|
|||
|
// debugger and waits for a manipulate state message.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// ExceptionRecord - Supplies a pointer to an exception record.
|
|||
|
// x86Context - Supplies a pointer to a context record.
|
|||
|
// FirstChance - Supplies a boolean value that determines whether this is
|
|||
|
// the first or second chance for the exception.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// A value of TRUE is returned if the exception is handled. Otherwise, a
|
|||
|
// value of FALSE is returned.
|
|||
|
{
|
|||
|
STRING MessageData;
|
|||
|
STRING MessageHeader;
|
|||
|
DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange;
|
|||
|
KCONTINUE_STATUS Status;
|
|||
|
|
|||
|
KDDBG("KdpReportExceptionStateChange %p\n", x86Context);
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Construct the wait state change message and message descriptor.
|
|||
|
//
|
|||
|
|
|||
|
KdpSetCommonState(DbgKdExceptionStateChange, x86Context,
|
|||
|
&WaitStateChange);
|
|||
|
|
|||
|
WaitStateChange.u.Exception.ExceptionRecord = *ExceptionRecord;
|
|||
|
WaitStateChange.u.Exception.FirstChance = FirstChance;
|
|||
|
|
|||
|
KdpSetContextState(&WaitStateChange, x86Context);
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(WaitStateChange);
|
|||
|
MessageHeader.Buffer = (PCHAR)&WaitStateChange;
|
|||
|
MessageData.Length = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Send packet to the kernel debugger on the host machine,
|
|||
|
// wait for answer.
|
|||
|
//
|
|||
|
Status = KdpSendWaitContinue(
|
|||
|
PACKET_TYPE_KD_STATE_CHANGE64,
|
|||
|
&MessageHeader,
|
|||
|
&MessageData,
|
|||
|
x86Context
|
|||
|
);
|
|||
|
} while (Status == ContinueProcessorReselected) ;
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpReadVirtualMemory(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response to a read virtual memory 32-bit
|
|||
|
// state manipulation message. Its function is to read virtual memory
|
|||
|
// and return.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies a pointer to the state manipulation message.
|
|||
|
// AdditionalData - Supplies a pointer to a descriptor for the data to read.
|
|||
|
// x86Context - Supplies a pointer to the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
ULONG Length;
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
//
|
|||
|
// Trim the transfer count to fit in a single message.
|
|||
|
//
|
|||
|
|
|||
|
Length = m->u.ReadMemory.TransferCount;
|
|||
|
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) {
|
|||
|
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Move the data to the destination buffer.
|
|||
|
//
|
|||
|
if (m->u.ReadMemory.TargetBaseAddress >= 0x80000000) {
|
|||
|
KDDBG(" Read out of range!\n");
|
|||
|
|
|||
|
for (UINT i = 0; i < Length; i++) {
|
|||
|
AdditionalData->Buffer[i] = 0;
|
|||
|
}
|
|||
|
Length = 0;
|
|||
|
}
|
|||
|
else {
|
|||
|
m->ReturnStatus =
|
|||
|
KdpCopyMemoryChunks(m->u.ReadMemory.TargetBaseAddress,
|
|||
|
AdditionalData->Buffer,
|
|||
|
Length,
|
|||
|
0,
|
|||
|
MMDBG_COPY_UNSAFE,
|
|||
|
&Length);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the actual number of bytes read, initialize the message header,
|
|||
|
// and send the reply packet to the host debugger.
|
|||
|
//
|
|||
|
|
|||
|
AdditionalData->Length = (USHORT)Length;
|
|||
|
m->u.ReadMemory.ActualBytesRead = Length;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
AdditionalData,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpWriteVirtualMemory(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a write virtual memory 32-bit
|
|||
|
// state manipulation message. Its function is to write virtual memory
|
|||
|
// and return.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies a pointer to the state manipulation message.
|
|||
|
// AdditionalData - Supplies a pointer to a descriptor for the data to write.
|
|||
|
// x86Context - Supplies a pointer to the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
//
|
|||
|
// Move the data to the destination buffer.
|
|||
|
//
|
|||
|
|
|||
|
m->ReturnStatus =
|
|||
|
KdpCopyMemoryChunks(m->u.WriteMemory.TargetBaseAddress,
|
|||
|
AdditionalData->Buffer,
|
|||
|
AdditionalData->Length,
|
|||
|
0,
|
|||
|
MMDBG_COPY_WRITE | MMDBG_COPY_UNSAFE,
|
|||
|
&m->u.WriteMemory.ActualBytesWritten);
|
|||
|
|
|||
|
//
|
|||
|
// Set the actual number of bytes written, initialize the message header,
|
|||
|
// and send the reply packet to the host debugger.
|
|||
|
//
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpReadPhysicalMemory(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response to a read physical memory 32-bit
|
|||
|
// state manipulation message. Its function is to read physical memory
|
|||
|
// and return.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies a pointer to the state manipulation message.
|
|||
|
// AdditionalData - Supplies a pointer to a descriptor for the data to read.
|
|||
|
// x86Context - Supplies a pointer to the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
ULONG Length;
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
//
|
|||
|
// Trim the transfer count to fit in a single message.
|
|||
|
//
|
|||
|
|
|||
|
Length = m->u.ReadMemory.TransferCount;
|
|||
|
if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) {
|
|||
|
Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
}
|
|||
|
|
|||
|
m->ReturnStatus =
|
|||
|
KdpCopyMemoryChunks(m->u.ReadMemory.TargetBaseAddress,
|
|||
|
AdditionalData->Buffer,
|
|||
|
Length,
|
|||
|
0,
|
|||
|
MMDBG_COPY_UNSAFE | MMDBG_COPY_PHYSICAL,
|
|||
|
&Length);
|
|||
|
|
|||
|
//
|
|||
|
// Set the actual number of bytes read, initialize the message header,
|
|||
|
// and send the reply packet to the host debugger.
|
|||
|
//
|
|||
|
|
|||
|
AdditionalData->Length = (USHORT)Length;
|
|||
|
m->u.ReadMemory.ActualBytesRead = Length;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
AdditionalData,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpWritePhysicalMemory(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a write physical memory 32-bit
|
|||
|
// state manipulation message. Its function is to write physical memory
|
|||
|
// and return.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies a pointer to the state manipulation message.
|
|||
|
// AdditionalData - Supplies a pointer to a descriptor for the data to write.
|
|||
|
// x86Context - Supplies a pointer to the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
//
|
|||
|
// Move the data to the destination buffer.
|
|||
|
//
|
|||
|
|
|||
|
m->ReturnStatus =
|
|||
|
KdpCopyMemoryChunks(m->u.WriteMemory.TargetBaseAddress,
|
|||
|
AdditionalData->Buffer,
|
|||
|
AdditionalData->Length,
|
|||
|
0,
|
|||
|
MMDBG_COPY_WRITE | MMDBG_COPY_UNSAFE | MMDBG_COPY_PHYSICAL,
|
|||
|
&m->u.WriteMemory.ActualBytesWritten);
|
|||
|
|
|||
|
//
|
|||
|
// Set the actual number of bytes written, initialize the message header,
|
|||
|
// and send the reply packet to the host debugger.
|
|||
|
//
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpReadMachineSpecificRegister(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a write physical memory 32-bit
|
|||
|
// state manipulation message. Its function is to write physical memory
|
|||
|
// and return.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies a pointer to the state manipulation message.
|
|||
|
// AdditionalData - Supplies a pointer to a descriptor for the data to write.
|
|||
|
// x86Context - Supplies a pointer to the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
//
|
|||
|
// Read the MSR
|
|||
|
//
|
|||
|
|
|||
|
UINT32 msr = m->u.ReadWriteMsr.Msr;
|
|||
|
UINT32 hi, lo;
|
|||
|
|
|||
|
__asm {
|
|||
|
mov ecx, msr;
|
|||
|
rdmsr;
|
|||
|
mov hi, edx;
|
|||
|
mov lo, eax;
|
|||
|
}
|
|||
|
|
|||
|
m->u.ReadWriteMsr.DataValueHigh = hi;
|
|||
|
m->u.ReadWriteMsr.DataValueLow = lo;
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Set the actual number of bytes written, initialize the message header,
|
|||
|
// and send the reply packet to the host debugger.
|
|||
|
//
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpWriteMachineSpecificRegister(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a write physical memory 32-bit
|
|||
|
// state manipulation message. Its function is to write physical memory
|
|||
|
// and return.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies a pointer to the state manipulation message.
|
|||
|
// AdditionalData - Supplies a pointer to a descriptor for the data to write.
|
|||
|
// x86Context - Supplies a pointer to the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
//
|
|||
|
// Write the MSR
|
|||
|
//
|
|||
|
|
|||
|
UINT32 msr = m->u.ReadWriteMsr.Msr;
|
|||
|
UINT32 hi = m->u.ReadWriteMsr.DataValueHigh, lo = m->u.ReadWriteMsr.DataValueLow;
|
|||
|
|
|||
|
__asm {
|
|||
|
mov ecx, msr;
|
|||
|
mov edx, hi;
|
|||
|
mov eax, lo;
|
|||
|
wrmsr;
|
|||
|
}
|
|||
|
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Set the actual number of bytes written, initialize the message header,
|
|||
|
// and send the reply packet to the host debugger.
|
|||
|
//
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static int wcslen(WCHAR *pwz)
|
|||
|
{
|
|||
|
int len = 0;
|
|||
|
|
|||
|
while (*pwz++) {
|
|||
|
len++;
|
|||
|
}
|
|||
|
return len;
|
|||
|
}
|
|||
|
|
|||
|
static WCHAR * trim(WCHAR *pwz)
|
|||
|
{
|
|||
|
WCHAR *pwzBeg = pwz;
|
|||
|
for (; *pwz; pwz++) {
|
|||
|
if (*pwz == '\\') {
|
|||
|
pwzBeg = pwz + 1;
|
|||
|
}
|
|||
|
}
|
|||
|
return pwzBeg;
|
|||
|
}
|
|||
|
|
|||
|
static void KdpFakeOutPsLoadedModuleList()
|
|||
|
{
|
|||
|
UINT8* pbImage = (UINT8*)g_pBootInfo->DumpAddr32;
|
|||
|
PMINIDUMP_HEADER pHeader = (PMINIDUMP_HEADER)(pbImage + 0);
|
|||
|
|
|||
|
#define VERBOSE 1
|
|||
|
#if VERBOSE
|
|||
|
printf("KdpFakeOutPsLoadedModuleList (%p):\n"
|
|||
|
" BootInfo at %p MINIDUMP_HEADER at %p\n",
|
|||
|
&PsLoadedModuleList, g_pBootInfo, pHeader);
|
|||
|
#endif
|
|||
|
|
|||
|
// Need to build a data structure which looks like NT's PsLoadedModuleList
|
|||
|
InitializeListHead(&PsLoadedModuleList);
|
|||
|
|
|||
|
PMINIDUMP_DIRECTORY pDir
|
|||
|
= (PMINIDUMP_DIRECTORY)(pbImage + pHeader->StreamDirectoryRva);
|
|||
|
|
|||
|
for (UINT i = 0; i < pHeader->NumberOfStreams; i++) {
|
|||
|
if (pDir[i].StreamType == ModuleListStream) {
|
|||
|
KdMinidumpModuleList = (PMINIDUMP_MODULE_LIST)(pbImage + pDir[i].Location.Rva);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (KdMinidumpModuleList == NULL) {
|
|||
|
#if VERBOSE
|
|||
|
printf("Couldn't find module list.\n");
|
|||
|
#endif
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
KdVersionBlock.KernBase = (ULONG64)KdMinidumpModuleList->Modules[0].BaseOfImage;
|
|||
|
|
|||
|
#if VERBOSE
|
|||
|
printf("MODULE_LIST: %d entries\n", KdMinidumpModuleList->NumberOfModules);
|
|||
|
#endif
|
|||
|
|
|||
|
#if 1
|
|||
|
// We manual include these modules to allow debugging very early in the boot cycle.
|
|||
|
for (UINT m = 0; m < KdMinidumpModuleList->NumberOfModules &&
|
|||
|
m < (sizeof(KdModuleKernelEntry)/sizeof(KdModuleKernelEntry[0])); m++) {
|
|||
|
|
|||
|
PMINIDUMP_MODULE pModule = &KdMinidumpModuleList->Modules[m];
|
|||
|
KLDR_DATA_TABLE_ENTRY_WITH_NAME *pEntry = &KdModuleKernelEntry[KdModuleKernelUsed++];
|
|||
|
|
|||
|
WCHAR *name = trim((WCHAR *)((pbImage + pModule->ModuleNameRva)+4));
|
|||
|
USHORT nlen = (USHORT)2 * wcslen(name);
|
|||
|
if (nlen > sizeof(pEntry->wzName)) {
|
|||
|
nlen = sizeof(pEntry->wzName);
|
|||
|
}
|
|||
|
|
|||
|
pEntry->DllBase = (PVOID *)pModule->BaseOfImage;
|
|||
|
pEntry->CheckSum = pModule->CheckSum;
|
|||
|
pEntry->TimeDateStamp = pModule->TimeDateStamp;
|
|||
|
pEntry->LoadCount = 1;
|
|||
|
pEntry->SizeOfImage = pModule->SizeOfImage;
|
|||
|
memcpy(pEntry->wzName, name, nlen);
|
|||
|
RtlInitUnicodeString(&pEntry->BaseDllName, pEntry->wzName, nlen);
|
|||
|
RtlInitUnicodeString(&pEntry->FullDllName, pEntry->wzName, nlen); // Write back on self.
|
|||
|
|
|||
|
#if VERBOSE
|
|||
|
printf("%4d: BaseOfImage %8lx SizeOfImage %8x ModuleNameRva %8x\n",
|
|||
|
m,
|
|||
|
pModule->BaseOfImage,
|
|||
|
pModule->SizeOfImage,
|
|||
|
pModule->ModuleNameRva);
|
|||
|
printf(" ModuleName: %ls (%p)\n",
|
|||
|
pEntry->BaseDllName.Buffer,
|
|||
|
pEntry);
|
|||
|
#endif
|
|||
|
|
|||
|
InsertTailList(&PsLoadedModuleList, &pEntry->InLoadOrderLinks);
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KdpSysGetVersion(
|
|||
|
PDBGKD_GET_VERSION64 Version
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function returns to the caller a general information packet
|
|||
|
// that contains useful information to a debugger. This packet is also
|
|||
|
// used for a debugger to determine if the writebreakpointex and
|
|||
|
// readbreakpointex APIs are available.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// Version - Supplies the structure to fill in
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
*Version = KdVersionBlock;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpGetVersion(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function returns to the caller a general information packet
|
|||
|
// that contains useful information to a debugger. This packet is also
|
|||
|
// used for a debugger to determine if the writebreakpointex and
|
|||
|
// readbreakpointex APIs are available.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
STRING messageHeader;
|
|||
|
|
|||
|
messageHeader.Length = sizeof(*m);
|
|||
|
messageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
KdpSysGetVersion(&m->u.GetVersion64);
|
|||
|
|
|||
|
//
|
|||
|
// the usual stuff
|
|||
|
//
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
m->ApiNumber = DbgKdGetVersionApi;
|
|||
|
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&messageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext
|
|||
|
);
|
|||
|
|
|||
|
return;
|
|||
|
} // KdGetVersion
|
|||
|
|
|||
|
static inline ULONG min(ULONG a, ULONG b)
|
|||
|
{
|
|||
|
return a < b ? a : b;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpReadControlSpace(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext * x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a read control space state
|
|||
|
// manipulation message. Its function is to read implementation
|
|||
|
// specific system data.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
// AdditionalData - Supplies any additional data for the message.
|
|||
|
// Context - Supplies the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
PDBGKD_READ_MEMORY64 a = &m->u.ReadMemory;
|
|||
|
STRING MessageHeader;
|
|||
|
ULONG Length;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(*m);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
ASSERT(AdditionalData->Length == 0);
|
|||
|
|
|||
|
Length = min(min(a->TransferCount,
|
|||
|
PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64)),
|
|||
|
sizeof(KPROCESSOR_STATE) - (ULONG)a->TargetBaseAddress);
|
|||
|
|
|||
|
if ((a->TargetBaseAddress + Length <= sizeof(KPROCESSOR_STATE)) &&
|
|||
|
(m->Processor < (ULONG)KeNumberProcessors)) {
|
|||
|
PKPROCESSOR_STATE ProcessorState = &KdpProcessorState[m->Processor];
|
|||
|
if (a->TargetBaseAddress < sizeof(CONTEXT)) {
|
|||
|
// Need to update the thread context information.
|
|||
|
KdSingularityToWindbgContext(x86Context, &ProcessorState->ContextFrame);
|
|||
|
}
|
|||
|
if (a->TargetBaseAddress + Length > sizeof(CONTEXT)) {
|
|||
|
// Need to update the processor context information.
|
|||
|
// ASSERT(m->Processor == 0); // MP support broken.
|
|||
|
|
|||
|
ProcessorState->SpecialRegisters.Cr2 = x86Context->cr2;
|
|||
|
ProcessorState->SpecialRegisters.Cr3 = (ULONG)x86Context->cr3;
|
|||
|
KSPECIAL_REGISTERS *pksp = &ProcessorState->SpecialRegisters;
|
|||
|
|
|||
|
__asm {
|
|||
|
mov ebx, pksp;
|
|||
|
|
|||
|
mov eax, cr0;
|
|||
|
mov [ebx].Cr0, eax;
|
|||
|
mov eax, cr3;
|
|||
|
mov [ebx].Cr3, eax;
|
|||
|
_emit 0x0f; // mov eax,cr4
|
|||
|
_emit 0x20;
|
|||
|
_emit 0xe0;
|
|||
|
mov [ebx].Cr4, eax;
|
|||
|
|
|||
|
// XXX save TR should save segment regs as well.
|
|||
|
str ax;
|
|||
|
mov [ebx].Tr, ax;
|
|||
|
#if 0
|
|||
|
mov eax, dr0;
|
|||
|
mov [ebx].KernelDr0, eax;
|
|||
|
mov eax, dr1;
|
|||
|
mov [ebx].KernelDr1, eax;
|
|||
|
mov eax, dr2;
|
|||
|
mov [ebx].KernelDr2, eax;
|
|||
|
mov eax, dr3;
|
|||
|
mov [ebx].KernelDr3, eax;
|
|||
|
mov eax, dr6;
|
|||
|
mov [ebx].KernelDr6, eax;
|
|||
|
mov eax, dr7;
|
|||
|
mov [ebx].KernelDr7, eax;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
const Struct_Microsoft_Singularity_CpuInfo* cpuInfo = &g_pBootInfo->Cpu0 + m->Processor;
|
|||
|
ProcessorState->SpecialRegisters.Gdtr.Pad = cpuInfo->GdtPtr.pad;
|
|||
|
ProcessorState->SpecialRegisters.Gdtr.Limit = cpuInfo->GdtPtr.limit;
|
|||
|
ProcessorState->SpecialRegisters.Gdtr.Base = cpuInfo->GdtPtr.addr;
|
|||
|
|
|||
|
ProcessorState->SpecialRegisters.Idtr.Pad = g_idt.pad;
|
|||
|
ProcessorState->SpecialRegisters.Idtr.Limit = g_idt.limit;
|
|||
|
ProcessorState->SpecialRegisters.Idtr.Base = g_idt.addr;
|
|||
|
}
|
|||
|
|
|||
|
m->ReturnStatus = KdpCopyToPtr(AdditionalData->Buffer,
|
|||
|
((uint8*)ProcessorState) + a->TargetBaseAddress,
|
|||
|
Length,
|
|||
|
&Length);
|
|||
|
}
|
|||
|
else {
|
|||
|
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
Length = 0;
|
|||
|
}
|
|||
|
|
|||
|
AdditionalData->Length = (USHORT)Length;
|
|||
|
a->ActualBytesRead = Length;
|
|||
|
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
AdditionalData,
|
|||
|
&KdpContext);
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpWriteControlSpace(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext * x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a write control space state
|
|||
|
// manipulation message. Its function is to write implementation
|
|||
|
// specific system data.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
// AdditionalData - Supplies any additional data for the message.
|
|||
|
// x86Context - Supplies the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
PDBGKD_WRITE_MEMORY64 a = &m->u.WriteMemory;
|
|||
|
ULONG Length;
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(*m);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
Length = AdditionalData->Length;
|
|||
|
|
|||
|
if (((a->TargetBaseAddress + Length) <= sizeof(KPROCESSOR_STATE)) &&
|
|||
|
(m->Processor < (ULONG)KeNumberProcessors)) {
|
|||
|
PKPROCESSOR_STATE ProcessorState = &KdpProcessorState[m->Processor];
|
|||
|
if (a->TargetBaseAddress < sizeof(CONTEXT)) {
|
|||
|
// Need to update the thread context information.
|
|||
|
KdWindbgToSingularityContext(&ProcessorState->ContextFrame, x86Context);
|
|||
|
}
|
|||
|
if (a->TargetBaseAddress + Length > sizeof(CONTEXT)) {
|
|||
|
// Need to update the processor context information.
|
|||
|
KDDBG(" Writing SpecialRegisters.\n");
|
|||
|
ASSERT(m->Processor == 0); // MP support broken.
|
|||
|
|
|||
|
#if 0
|
|||
|
// We don't support separate kernel data breakpoints as
|
|||
|
// Singularity uses a single address space.
|
|||
|
kdprintf(" dr0=%p dr1=%p dr2=%p dr3=%p dr6=%p dr7=%p\n",
|
|||
|
(PVOID)(windbg->Dr0),
|
|||
|
(PVOID)(windbg->Dr1),
|
|||
|
(PVOID)(windbg->Dr2),
|
|||
|
(PVOID)(windbg->Dr3),
|
|||
|
(PVOID)(windbg->Dr6),
|
|||
|
(PVOID)(windbg->Dr7));
|
|||
|
|
|||
|
KSPECIAL_REGISTERS *pksp = &ProcessorState->SpecialRegisters;
|
|||
|
kdprintf(" cr0=%p cr2=%p cr3=%p cr4=%p\n",
|
|||
|
pksp->Cr0, pksp->Cr2, pksp->Cr3, pksp->Cr4);
|
|||
|
|
|||
|
__asm {
|
|||
|
mov ebx, pksp;
|
|||
|
|
|||
|
#if 0
|
|||
|
mov eax, [ebx].Cr0;
|
|||
|
mov cr0, eax;
|
|||
|
mov eax, [ebx].Cr2;
|
|||
|
mov cr2, eax;
|
|||
|
mov eax, [ebx].Cr2;
|
|||
|
mov cr3, eax;
|
|||
|
mov eax, [ebx].Cr4;
|
|||
|
_emit 0x0f; // mov cr4,eax
|
|||
|
_emit 0x22;
|
|||
|
_emit 0xe0;
|
|||
|
#endif
|
|||
|
|
|||
|
mov eax, [ebx].KernelDr0;
|
|||
|
mov dr0, eax;
|
|||
|
mov eax, [ebx].KernelDr1;
|
|||
|
mov dr1, eax;
|
|||
|
mov eax, [ebx].KernelDr2;
|
|||
|
mov dr2, eax;
|
|||
|
mov eax, [ebx].KernelDr3;
|
|||
|
mov dr3, eax;
|
|||
|
mov eax, [ebx].KernelDr6;
|
|||
|
mov dr6, eax;
|
|||
|
mov eax, [ebx].KernelDr7;
|
|||
|
mov dr7, eax;
|
|||
|
}
|
|||
|
kdprintf(" dr0=%p dr1=%p dr2=%p dr3=%p dr6=%p dr7=%p\n",
|
|||
|
(PVOID)(pksp->KernelDr0),
|
|||
|
(PVOID)(pksp->KernelDr1),
|
|||
|
(PVOID)(pksp->KernelDr2),
|
|||
|
(PVOID)(pksp->KernelDr3),
|
|||
|
(PVOID)(pksp->KernelDr6),
|
|||
|
(PVOID)(pksp->KernelDr7));
|
|||
|
kdprintf(" rr0=%p rr1=%p rr2=%p rr3=%p rr6=%p rr7=%p\n",
|
|||
|
(PVOID)(pksp->Reserved[0]),
|
|||
|
(PVOID)(pksp->Reserved[1]),
|
|||
|
(PVOID)(pksp->Reserved[2]),
|
|||
|
(PVOID)(pksp->Reserved[3]),
|
|||
|
(PVOID)(pksp->Reserved[4]),
|
|||
|
(PVOID)(pksp->Reserved[5]));
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
m->ReturnStatus = KdpCopyFromPtr(((uint8*)ProcessorState) + a->TargetBaseAddress,
|
|||
|
AdditionalData->Buffer,
|
|||
|
Length,
|
|||
|
&Length);
|
|||
|
} else {
|
|||
|
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
Length = 0;
|
|||
|
}
|
|||
|
|
|||
|
a->ActualBytesWritten = Length;
|
|||
|
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
AdditionalData,
|
|||
|
&KdpContext);
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpSetContext(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext * x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a set context state
|
|||
|
// manipulation message. Its function is set the current
|
|||
|
// context.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
// AdditionalData - Supplies any additional data for the message.
|
|||
|
// x86Context - Supplies the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(*m);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
ASSERT(AdditionalData->Length == sizeof(CONTEXT));
|
|||
|
|
|||
|
ASSERT(m->Processor == 0); // MP support broken.
|
|||
|
|
|||
|
if ((m->Processor >= (USHORT)KeNumberProcessors) ||
|
|||
|
(KdpContextSent == FALSE)) {
|
|||
|
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
else {
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
KdWindbgToSingularityContext((CONTEXT*)AdditionalData->Buffer, x86Context);
|
|||
|
}
|
|||
|
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpGetContext(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a get context state
|
|||
|
// manipulation message. Its function is to return the current
|
|||
|
// context.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
// AdditionalData - Supplies any additional data for the message.
|
|||
|
// x86Context - Supplies the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(*m);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
ASSERT(AdditionalData->Length == 0);
|
|||
|
|
|||
|
ASSERT(m->Processor == 0); // MP support broken.
|
|||
|
|
|||
|
if (m->Processor >= (USHORT)KeNumberProcessors) {
|
|||
|
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
else {
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
AdditionalData->Length = sizeof(CONTEXT);
|
|||
|
|
|||
|
KdSingularityToWindbgContext(x86Context, (CONTEXT*)AdditionalData->Buffer);
|
|||
|
KdpContextSent = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
AdditionalData,
|
|||
|
&KdpContext);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
KdpAddBreakpoint(
|
|||
|
IN PVOID Address
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This routine adds an entry to the breakpoint table and returns a handle
|
|||
|
// to the breakpoint table entry.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// Address - Supplies the address where to set the breakpoint.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
//
|
|||
|
// A value of zero is returned if the specified address is already in the
|
|||
|
// breakpoint table, there are no free entries in the breakpoint table, the
|
|||
|
// specified address is not correctly aligned, or the specified address is
|
|||
|
// not valid. Otherwise, the index of the assigned breakpoint table entry
|
|||
|
// plus one is returned as the function value.
|
|||
|
{
|
|||
|
ULONG Index;
|
|||
|
KDP_BREAKPOINT_TYPE Content;
|
|||
|
BOOL Accessible;
|
|||
|
|
|||
|
KDDBG2("KdpAddBreakpoint(%p)\n", Address);
|
|||
|
|
|||
|
for (Index = 0; Index < BREAKPOINT_TABLE_SIZE; Index++) {
|
|||
|
if (KdpBreakpointTable[Index].Flags == 0) break;
|
|||
|
}
|
|||
|
if (Index == BREAKPOINT_TABLE_SIZE) {
|
|||
|
KDDBG("KD: ran out of breakpoints!\n");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
Accessible = NT_SUCCESS(KdpCopyFromPtr(&Content,
|
|||
|
Address,
|
|||
|
sizeof(KDP_BREAKPOINT_TYPE),
|
|||
|
NULL));
|
|||
|
KDDBG("KD: memory %saccessible\n", Accessible ? "" : "in");
|
|||
|
|
|||
|
if (Accessible) {
|
|||
|
KdpBreakpointTable[Index].Address = Address;
|
|||
|
KdpBreakpointTable[Index].Content = Content;
|
|||
|
KdpBreakpointTable[Index].Flags = KD_BREAKPOINT_IN_USE;
|
|||
|
|
|||
|
if (!NT_SUCCESS(KdpCopyToPtr(Address,
|
|||
|
&KdpBreakpointInstruction,
|
|||
|
sizeof(KDP_BREAKPOINT_TYPE),
|
|||
|
NULL))) {
|
|||
|
KDDBG("KD: Unable to write BP!\n");
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
return Index+1;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KdpDeleteBreakpoint(
|
|||
|
IN ULONG Handle
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG Index = Handle - 1;
|
|||
|
KDDBG2("KD: Delete Breakpoint %d\n", Handle);
|
|||
|
|
|||
|
if ((Handle == 0) || (Handle > BREAKPOINT_TABLE_SIZE)) {
|
|||
|
KDDBG("KD: Breakpoint %d invalid.\n", Index);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Replace the instruction contents.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(KdpCopyToPtr(KdpBreakpointTable[Index].Address,
|
|||
|
&KdpBreakpointTable[Index].Content,
|
|||
|
sizeof(KDP_BREAKPOINT_TYPE),
|
|||
|
NULL))) {
|
|||
|
KDDBG("KD: Breakpoint at 0x%p; unable to clear, flag set.\n",
|
|||
|
KdpBreakpointTable[Index].Address);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
else {
|
|||
|
KDDBG2("KD: Breakpoint at 0x%p cleared.\n",
|
|||
|
KdpBreakpointTable[Index].Address);
|
|||
|
KdpBreakpointTable[Index].Flags = 0;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KdpWriteBreakpoint(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a write breakpoint state
|
|||
|
// manipulation message. Its function is to write a breakpoint
|
|||
|
// and return a handle to the breakpoint.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
// AdditionalData - Supplies any additional data for the message.
|
|||
|
// x86Context - Supplies the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
PDBGKD_WRITE_BREAKPOINT64 a = &m->u.WriteBreakPoint;
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(*m);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
ASSERT(AdditionalData->Length == 0);
|
|||
|
|
|||
|
a->BreakPointHandle = KdpAddBreakpoint((PVOID)(ULONG_PTR)a->BreakPointAddress);
|
|||
|
if (a->BreakPointHandle != 0) {
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
else {
|
|||
|
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KdpRestoreBreakpoint(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 m,
|
|||
|
IN PSTRING AdditionalData
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function is called in response of a restore breakpoint state
|
|||
|
// manipulation message. Its function is to restore a breakpoint
|
|||
|
// using the specified handle.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// m - Supplies the state manipulation message.
|
|||
|
// AdditionalData - Supplies any additional data for the message.
|
|||
|
// Context - Supplies the current context.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
PDBGKD_RESTORE_BREAKPOINT a = &m->u.RestoreBreakPoint;
|
|||
|
STRING MessageHeader;
|
|||
|
|
|||
|
MessageHeader.Length = sizeof(*m);
|
|||
|
MessageHeader.Buffer = (PCHAR)m;
|
|||
|
|
|||
|
ASSERT(AdditionalData->Length == 0);
|
|||
|
if (KdpDeleteBreakpoint(a->BreakPointHandle)) {
|
|||
|
m->ReturnStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
else {
|
|||
|
m->ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
NULL,
|
|||
|
&KdpContext);
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
VOID
|
|||
|
KdpGetStateChange(
|
|||
|
IN PDBGKD_MANIPULATE_STATE64 ManipulateState,
|
|||
|
IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// Extract continuation control data from Manipulate_State message
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// ManipulateState - supplies pointer to Manipulate_State packet
|
|||
|
// x86Context - Supplies a pointer to a context record.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// None.
|
|||
|
{
|
|||
|
//PKPRCB Prcb;
|
|||
|
//ULONG Processor;
|
|||
|
|
|||
|
if (NT_SUCCESS(ManipulateState->u.Continue2.ContinueStatus)) {
|
|||
|
//
|
|||
|
// If NT_SUCCESS returns TRUE, then the debugger is doing a
|
|||
|
// continue, and it makes sense to apply control changes.
|
|||
|
// Otherwise the debugger is saying that it doesn't know what
|
|||
|
// to do with this exception, so control values are ignored.
|
|||
|
//
|
|||
|
|
|||
|
if (ManipulateState->u.Continue2.ControlSet.TraceFlag == TRUE) {
|
|||
|
KDDBG2("KD: Warning - trace flag set\n");
|
|||
|
x86Context->efl |= Struct_Microsoft_Singularity_X86_EFlags_TF;
|
|||
|
}
|
|||
|
else {
|
|||
|
x86Context->efl &= ~Struct_Microsoft_Singularity_X86_EFlags_TF;
|
|||
|
}
|
|||
|
|
|||
|
UINT32 _dr7 = ManipulateState->u.Continue2.ControlSet.Dr7;
|
|||
|
x86Context->dr7 = ManipulateState->u.Continue2.ControlSet.Dr7;
|
|||
|
|
|||
|
#if 0
|
|||
|
__asm {
|
|||
|
mov eax, 0;
|
|||
|
mov dr6, eax;
|
|||
|
mov eax, _dr7;
|
|||
|
mov dr7, eax;
|
|||
|
}
|
|||
|
#endif
|
|||
|
#if 0
|
|||
|
for (Processor = 0; Processor < (ULONG)KeNumberProcessors; Processor++) {
|
|||
|
Prcb = KiProcessorBlock[Processor];
|
|||
|
|
|||
|
Prcb->ProcessorState.SpecialRegisters.KernelDr7 =
|
|||
|
ManipulateState->u.Continue2.ControlSet.Dr7;
|
|||
|
|
|||
|
Prcb->ProcessorState.SpecialRegisters.KernelDr6 = 0L;
|
|||
|
}
|
|||
|
if (ManipulateState->u.Continue2.ControlSet.CurrentSymbolStart != 1) {
|
|||
|
KdpCurrentSymbolStart = ManipulateState->u.Continue2.ControlSet.CurrentSymbolStart;
|
|||
|
KdpCurrentSymbolEnd = ManipulateState->u.Continue2.ControlSet.CurrentSymbolEnd;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KCONTINUE_STATUS
|
|||
|
KdpSendWaitContinue(
|
|||
|
IN ULONG OutPacketType,
|
|||
|
IN PSTRING OutMessageHeader,
|
|||
|
IN PSTRING OutMessageData OPTIONAL,
|
|||
|
IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This function sends a packet, and then waits for a continue message.
|
|||
|
// BreakIns received while waiting will always cause a resend of the
|
|||
|
// packet originally sent out. While waiting, manipulate messages
|
|||
|
// will be serviced.
|
|||
|
// A resend always resends the original event sent to the debugger,
|
|||
|
// not the last response to some debugger command.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// OutPacketType - Supplies the type of packet to send.
|
|||
|
// OutMessageHeader - Supplies a pointer to a string descriptor that describes
|
|||
|
// the message information.
|
|||
|
// OutMessageData - Supplies a pointer to a string descriptor that describes
|
|||
|
// the optional message data.
|
|||
|
// x86Record - Exception context
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// A value of TRUE is returned if the continue message indicates
|
|||
|
// success, Otherwise, a value of FALSE is returned.
|
|||
|
{
|
|||
|
|
|||
|
ULONG Length;
|
|||
|
STRING MessageData;
|
|||
|
STRING MessageHeader;
|
|||
|
DBGKD_MANIPULATE_STATE64 ManipulateState;
|
|||
|
ULONG ReturnCode;
|
|||
|
// NTSTATUS Status;
|
|||
|
// KCONTINUE_STATUS ContinueStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Loop servicing state manipulation message until a continue message
|
|||
|
// is received.
|
|||
|
//
|
|||
|
|
|||
|
MessageHeader.MaximumLength = sizeof(DBGKD_MANIPULATE_STATE64);
|
|||
|
MessageHeader.Buffer = (PCHAR)&ManipulateState;
|
|||
|
MessageData.MaximumLength = arrayof(KdpMessageBuffer);
|
|||
|
MessageData.Buffer = KdpMessageBuffer;
|
|||
|
KdpContextSent = FALSE;
|
|||
|
|
|||
|
ResendPacket:
|
|||
|
|
|||
|
//
|
|||
|
// Send event notification packet to debugger on host. Come back
|
|||
|
// here any time we see a breakin sequence.
|
|||
|
//
|
|||
|
|
|||
|
KdSendPacket(OutPacketType,
|
|||
|
OutMessageHeader,
|
|||
|
OutMessageData,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
//
|
|||
|
// After sending packet, if there is no response from debugger
|
|||
|
// AND the packet is for reporting symbol (un)load, the debugger
|
|||
|
// will be declared to be not present. Note If the packet is for
|
|||
|
// reporting exception, the KdSendPacket will never stop.
|
|||
|
//
|
|||
|
|
|||
|
if (KdDebuggerNotPresent) {
|
|||
|
return ContinueSuccess;
|
|||
|
}
|
|||
|
|
|||
|
for (;;) {
|
|||
|
//
|
|||
|
// Wait for State Manipulate Packet without timeout.
|
|||
|
//
|
|||
|
|
|||
|
KDDBG("KdpSendWait::\r");
|
|||
|
|
|||
|
do {
|
|||
|
ReturnCode = KdReceivePacket(
|
|||
|
PACKET_TYPE_KD_STATE_MANIPULATE,
|
|||
|
&MessageHeader,
|
|||
|
&MessageData,
|
|||
|
&Length,
|
|||
|
&KdpContext
|
|||
|
);
|
|||
|
KDDBG2("KdReceivePacket returned %d\n", ReturnCode);
|
|||
|
if (ReturnCode == KDP_PACKET_RESEND) {
|
|||
|
goto ResendPacket;
|
|||
|
}
|
|||
|
} while (ReturnCode == KDP_PACKET_TIMEOUT);
|
|||
|
|
|||
|
KDDBG2("KdpSendWaitContinue: ManipulateState.ApiNumber=0x%x\n", ManipulateState.ApiNumber);
|
|||
|
|
|||
|
//
|
|||
|
// Switch on the return message API number.
|
|||
|
//
|
|||
|
|
|||
|
switch (ManipulateState.ApiNumber) {
|
|||
|
|
|||
|
case DbgKdReadVirtualMemoryApi:
|
|||
|
KDDBG("KdpSendWait::KdReadVirtualMemory (%8p..%8p) (%d bytes)\n",
|
|||
|
(ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress,
|
|||
|
(ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress +
|
|||
|
ManipulateState.u.ReadMemory.TransferCount,
|
|||
|
ManipulateState.u.ReadMemory.TransferCount);
|
|||
|
KdpReadVirtualMemory(&ManipulateState,&MessageData);
|
|||
|
break;
|
|||
|
|
|||
|
#if 0
|
|||
|
case DbgKdReadVirtualMemory64Api:
|
|||
|
KdpReadVirtualMemory64(&ManipulateState,&MessageData);
|
|||
|
break;
|
|||
|
#endif
|
|||
|
case DbgKdWriteVirtualMemoryApi:
|
|||
|
KDDBG("KdpSendWait::KdWriteVirtualMemory(%8p..%8p)\n",
|
|||
|
(ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress,
|
|||
|
(ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress +
|
|||
|
ManipulateState.u.WriteMemory.TransferCount);
|
|||
|
KdpWriteVirtualMemory(&ManipulateState,&MessageData);
|
|||
|
break;
|
|||
|
#if 0
|
|||
|
case DbgKdWriteVirtualMemory64Api:
|
|||
|
KdpWriteVirtualMemory64(&ManipulateState,&MessageData);
|
|||
|
break;
|
|||
|
#endif
|
|||
|
case DbgKdGetVersionApi:
|
|||
|
KDDBG("KdpSendWait::KdGetVersion()\n");
|
|||
|
KdpGetVersion(&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdGetContextApi:
|
|||
|
KDDBG("KdpSendWait::KdGetContext(p=%x)\n", ManipulateState.Processor);
|
|||
|
KdpGetContext(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdReadControlSpaceApi:
|
|||
|
KDDBG("KdpSendWait::KdReadControlSpace (%8p..%8p p=%x)\n",
|
|||
|
(ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress,
|
|||
|
(ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress +
|
|||
|
ManipulateState.u.ReadMemory.TransferCount,
|
|||
|
(ULONG_PTR)ManipulateState.Processor);
|
|||
|
KdpReadControlSpace(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteControlSpaceApi:
|
|||
|
KDDBG("KdpSendWait::KdWriteControlSpace (%8p..%8p p=%x)\n",
|
|||
|
(ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress,
|
|||
|
(ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress +
|
|||
|
ManipulateState.u.WriteMemory.TransferCount,
|
|||
|
(ULONG_PTR)ManipulateState.Processor);
|
|||
|
KdpWriteControlSpace(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdSetContextApi:
|
|||
|
KDDBG("KdpSendWait::KdSetContext(p=%x)\n", ManipulateState.Processor);
|
|||
|
KdpSetContext(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteBreakPointApi:
|
|||
|
KDDBG("KdpSendWait::KdWriteBreakPoint(%p)\n",
|
|||
|
ManipulateState.u.WriteBreakPoint.BreakPointAddress);
|
|||
|
KdpWriteBreakpoint(&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdRestoreBreakPointApi:
|
|||
|
if (ManipulateState.u.RestoreBreakPoint.BreakPointHandle < 0x8 ||
|
|||
|
ManipulateState.u.RestoreBreakPoint.BreakPointHandle > 0x1e) {
|
|||
|
KDDBG("KdpSendWait::KdRestoreBreakpoint(h=%x)\n",
|
|||
|
ManipulateState.u.RestoreBreakPoint.BreakPointHandle);
|
|||
|
}
|
|||
|
KdpRestoreBreakpoint(&ManipulateState,&MessageData);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdContinueApi:
|
|||
|
KDDBG("KdpSendWait::KdContinue(ContinueStatus=%08x)\n",
|
|||
|
ManipulateState.u.Continue.ContinueStatus);
|
|||
|
if (NT_SUCCESS(ManipulateState.u.Continue.ContinueStatus)) {
|
|||
|
return ContinueSuccess;
|
|||
|
}
|
|||
|
else {
|
|||
|
return ContinueError;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdContinueApi2:
|
|||
|
KDDBG("KdpSendWait::KdContinue2(ContinueStatus=%08x)\n",
|
|||
|
ManipulateState.u.Continue2.ContinueStatus);
|
|||
|
if (NT_SUCCESS(ManipulateState.u.Continue2.ContinueStatus)) {
|
|||
|
KDDBG("KdpSendWait::KdpGetStateChange()\n");
|
|||
|
KdpGetStateChange(&ManipulateState,x86Context);
|
|||
|
return ContinueSuccess;
|
|||
|
}
|
|||
|
else {
|
|||
|
KDDBG("KdpSendWait::ContinueError!\n");
|
|||
|
return ContinueError;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdReadPhysicalMemoryApi:
|
|||
|
KDDBG("KdpSendWait::KdReadPhysicalMemory (%8p..%8p)\n",
|
|||
|
(ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress,
|
|||
|
(ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress +
|
|||
|
ManipulateState.u.ReadMemory.TransferCount);
|
|||
|
// KdpReadPhysicalMemory(&ManipulateState,&MessageData,x86Context);
|
|||
|
KdpReadPhysicalMemory(&ManipulateState,&MessageData);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWritePhysicalMemoryApi:
|
|||
|
KdpWritePhysicalMemory(&ManipulateState,&MessageData);
|
|||
|
KDDBG("KdpSendWait::KdWritePhysicalMemory (%8p..%8p)\n",
|
|||
|
(ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress,
|
|||
|
(ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress +
|
|||
|
ManipulateState.u.ReadMemory.TransferCount);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdSwitchProcessor:
|
|||
|
{
|
|||
|
// KdRestore(FALSE);
|
|||
|
bool switched = Class_Microsoft_Singularity_MpExecution::g_SwitchFrozenProcessor((int32) ManipulateState.Processor);
|
|||
|
return (switched == true) ? ContinueNextProcessor : ContinueSuccess;
|
|||
|
// KdSave(FALSE);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdReadMachineSpecificRegister:
|
|||
|
KdpReadMachineSpecificRegister(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteMachineSpecificRegister:
|
|||
|
KdpWriteMachineSpecificRegister(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
#if 0 // Haven't implemented most of the protocol yet!
|
|||
|
case DbgKdCheckLowMemoryApi:
|
|||
|
KdpCheckLowMemory (&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdReadControlSpaceApi:
|
|||
|
KdpReadControlSpace(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteControlSpaceApi:
|
|||
|
KdpWriteControlSpace(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdReadIoSpaceApi:
|
|||
|
KdpReadIoSpace(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteIoSpaceApi:
|
|||
|
KdpWriteIoSpace(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdReadIoSpaceExtendedApi:
|
|||
|
KdpReadIoSpaceExtended(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteIoSpaceExtendedApi:
|
|||
|
KdpWriteIoSpaceExtended(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdGetBusDataApi:
|
|||
|
KdpGetBusData(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdSetBusDataApi:
|
|||
|
KdpSetBusData(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdRebootApi:
|
|||
|
HalReturnToFirmware(HalRebootRoutine);
|
|||
|
break;
|
|||
|
|
|||
|
#if defined(i386)
|
|||
|
case DbgKdSetSpecialCallApi:
|
|||
|
KdSetSpecialCall(&ManipulateState,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdClearSpecialCallsApi:
|
|||
|
KdClearSpecialCalls();
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdSetInternalBreakPointApi:
|
|||
|
KdSetInternalBreakpoint(&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdGetInternalBreakPointApi:
|
|||
|
KdGetInternalBreakpoint(&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdClearAllInternalBreakpointsApi:
|
|||
|
KdpNumInternalBreakpoints = 0;
|
|||
|
break;
|
|||
|
|
|||
|
#endif // i386
|
|||
|
|
|||
|
case DbgKdCauseBugCheckApi:
|
|||
|
KdpCauseBugCheck(&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdPageInApi:
|
|||
|
KdpNotSupported(&ManipulateState);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdWriteBreakPointExApi:
|
|||
|
Status = KdpWriteBreakPointEx(&ManipulateState,
|
|||
|
&MessageData,
|
|||
|
x86Context);
|
|||
|
if (Status) {
|
|||
|
ManipulateState.ApiNumber = DbgKdContinueApi;
|
|||
|
ManipulateState.u.Continue.ContinueStatus = Status;
|
|||
|
return ContinueError;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdRestoreBreakPointExApi:
|
|||
|
KdpRestoreBreakPointEx(&ManipulateState,&MessageData,x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdSearchMemoryApi:
|
|||
|
KdpSearchMemory(&ManipulateState, &MessageData, x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdFillMemoryApi:
|
|||
|
KdpFillMemory(&ManipulateState, &MessageData, x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
case DbgKdQueryMemoryApi:
|
|||
|
KdpQueryMemory(&ManipulateState, x86Context);
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Invalid message.
|
|||
|
//
|
|||
|
#endif // XXX Haven't implemented most of the protocol yet!
|
|||
|
|
|||
|
default:
|
|||
|
kdprintf("KdpSendWaitContinue: unrecognized API number 0x%x\n", ManipulateState.ApiNumber);
|
|||
|
MessageData.Length = 0;
|
|||
|
ManipulateState.ReturnStatus = STATUS_UNSUCCESSFUL;
|
|||
|
KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, &MessageData, &KdpContext);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KdPrintString(
|
|||
|
IN PSTRING Output,
|
|||
|
IN BOOLEAN Unicode
|
|||
|
)
|
|||
|
// Routine Description:
|
|||
|
// This routine prints a string.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
// Output - Supplies a pointer to a string descriptor for the output string.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
// TRUE if Control-C present in input buffer after print is done.
|
|||
|
// FALSE otherwise.
|
|||
|
{
|
|||
|
|
|||
|
ULONG Length;
|
|||
|
STRING MessageData;
|
|||
|
STRING MessageHeader;
|
|||
|
DBGKD_DEBUG_IO DebugIo;
|
|||
|
|
|||
|
if (KdDebuggerNotPresent) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
KdpLock();
|
|||
|
|
|||
|
// Move the output string to the message buffer.
|
|||
|
//
|
|||
|
if (Unicode) {
|
|||
|
WCHAR *pBuffer = (WCHAR *)Output->Buffer;
|
|||
|
for (int i = 0; i < Output->Length; i++) {
|
|||
|
KdpMessageBuffer[i] = (char)pBuffer[i];
|
|||
|
}
|
|||
|
Length = Output->Length;
|
|||
|
}
|
|||
|
else {
|
|||
|
KdpCopyFromPtr(KdpMessageBuffer,
|
|||
|
Output->Buffer,
|
|||
|
Output->Length,
|
|||
|
&Length);
|
|||
|
}
|
|||
|
|
|||
|
// If the total message length is greater than the maximum packet size,
|
|||
|
// then truncate the output string.
|
|||
|
//
|
|||
|
if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE) {
|
|||
|
Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
|
|||
|
}
|
|||
|
|
|||
|
// Construct the print string message and message descriptor.
|
|||
|
//
|
|||
|
DebugIo.ApiNumber = DbgKdPrintStringApi;
|
|||
|
DebugIo.ProcessorLevel = KeProcessorLevel;
|
|||
|
DebugIo.Processor = (USHORT)GetCurrentProcessorNumber();
|
|||
|
DebugIo.u.PrintString.LengthOfString = Length;
|
|||
|
MessageHeader.Length = sizeof(DBGKD_DEBUG_IO);
|
|||
|
MessageHeader.Buffer = (PCHAR)&DebugIo;
|
|||
|
|
|||
|
// Construct the print string data and data descriptor.
|
|||
|
//
|
|||
|
MessageData.Length = (USHORT)Length;
|
|||
|
MessageData.Buffer = (PCHAR) KdpMessageBuffer;
|
|||
|
|
|||
|
// Send packet to the kernel debugger on the host machine.
|
|||
|
//
|
|||
|
KdSendPacket(PACKET_TYPE_KD_DEBUG_IO,
|
|||
|
&MessageHeader,
|
|||
|
&MessageData,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
KdpUnlock();
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// This should be fixed.
|
|||
|
VOID
|
|||
|
KdPutChar(
|
|||
|
CHAR c
|
|||
|
)
|
|||
|
{
|
|||
|
static char _KdpDebugStringBuf[KDP_MESSAGE_BUFFER_SIZE];
|
|||
|
static STRING KdpDebugString = { 0, KDP_MESSAGE_BUFFER_SIZE, (PCHAR)&_KdpDebugStringBuf };
|
|||
|
|
|||
|
if (KdpInDebugger) {
|
|||
|
koutput(NULL, c);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
KdpDebugString.Buffer[KdpDebugString.Length++] = c;
|
|||
|
if (c == '\n' || KdpDebugString.Length == KdpDebugString.MaximumLength) {
|
|||
|
KdPrintString(&KdpDebugString, FALSE);
|
|||
|
KdpDebugString.Length = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/////////////////////////////////////////////////////// Methods Exposed to C#.
|
|||
|
//
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::
|
|||
|
g_Trap(Struct_Microsoft_Singularity_X86_ThreadContext *context, bool firstChance)
|
|||
|
{
|
|||
|
EXCEPTION_RECORD64 er;
|
|||
|
bool handled;
|
|||
|
RtlZeroMemory(&er, sizeof(er));
|
|||
|
|
|||
|
KdpLock();
|
|||
|
|
|||
|
// Breakpoints:
|
|||
|
switch (context->num) {
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_SingleStep:
|
|||
|
er.ExceptionCode = STATUS_SINGLE_STEP;
|
|||
|
er.ExceptionAddress = (ULONG64)context->eip;
|
|||
|
// context->efl &= ~Struct_Microsoft_Singularity_X86_EFlags_TF;
|
|||
|
break;
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_Breakpoint:
|
|||
|
context->eip -= 1;
|
|||
|
er.ExceptionCode = STATUS_BREAKPOINT;
|
|||
|
er.NumberParameters = 1;
|
|||
|
er.ExceptionInformation[0] = BREAKPOINT_BREAK;
|
|||
|
er.ExceptionAddress = (ULONG64)context->eip;
|
|||
|
break;
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_IllegalInstruction:
|
|||
|
er.ExceptionCode = STATUS_ILLEGAL_INSTRUCTION;
|
|||
|
break;
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_PageFault:
|
|||
|
KDDBG("KD: 0x0E %d\n", context->num);
|
|||
|
er.ExceptionCode = STATUS_ACCESS_VIOLATION;
|
|||
|
er.ExceptionAddress = (UINT64)context->eip;
|
|||
|
er.NumberParameters = 1;
|
|||
|
er.ExceptionInformation[0] = context->cr2;
|
|||
|
break;
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_FirstChanceException: {
|
|||
|
KdDebugTrapData *trapData = (KdDebugTrapData *) (context->eax);
|
|||
|
switch(trapData->tag) {
|
|||
|
case KdDebugTrapData::FIRST_CHANCE_EXCEPTION:
|
|||
|
context->eax = trapData->u.firstChanceException.throwAddr;
|
|||
|
KDDBG("KD: First chance C# exception\n");
|
|||
|
// er.ExceptionCode = STATUS_CPP_EH_EXCEPTION; //0xe06d7363;
|
|||
|
er.ExceptionCode = STATUS_VCPP_EXCEPTION; //0x8000ff1f;
|
|||
|
er.ExceptionAddress = (UINT64)context->eip;
|
|||
|
er.NumberParameters = 1;
|
|||
|
er.ExceptionInformation[0] = BREAKPOINT_BREAK;
|
|||
|
break;
|
|||
|
case KdDebugTrapData::LOADED_BINARY:
|
|||
|
KDDBG("KD: Loaded binary\n");
|
|||
|
KdpUnlock();
|
|||
|
LoadedBinary(context);
|
|||
|
return true;
|
|||
|
case KdDebugTrapData::UNLOADED_BINARY:
|
|||
|
KDDBG("KD: Unloaded binary\n");
|
|||
|
KdpUnlock();
|
|||
|
UnloadedBinary(context);
|
|||
|
return true;
|
|||
|
default:
|
|||
|
KDDBG("KD: Unexpected interrupt %d\n", context->num);
|
|||
|
er.ExceptionCode = 0x80000000 + context->num;
|
|||
|
er.ExceptionAddress = (UINT64)context->eip;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_SecondChanceException:
|
|||
|
KDDBG("KD: Second chance C# exception\n");
|
|||
|
er.ExceptionCode = STATUS_VCPP_EXCEPTION;
|
|||
|
er.ExceptionAddress = (UINT64)context->eip;
|
|||
|
break;
|
|||
|
|
|||
|
case Struct_Microsoft_Singularity_X86_EVectors_DebuggerBreakRequest:
|
|||
|
KDDBG("KD: Debugger ctrl-break\n");
|
|||
|
er.ExceptionCode = STATUS_BREAKPOINT;
|
|||
|
er.ExceptionInformation[0] = BREAKPOINT_BREAK;
|
|||
|
er.ExceptionAddress = (UINT64)context->eip;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
KDDBG("KD: Unexpected interrupt %d\n", context->num);
|
|||
|
er.ExceptionCode = 0x80000000 + context->num;
|
|||
|
er.ExceptionAddress = (UINT64)context->eip;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
KDDBG("Trap: Context at %p\n", context);
|
|||
|
KDDBG(" CXT=%08x THR=%08x\n",
|
|||
|
context, context->_thread);
|
|||
|
KDDBG(" EIP=%08x EFL=%08x ERR=%08x CR2=%08x\n",
|
|||
|
context->eip, context->efl, context->err, context->cr2);
|
|||
|
KDDBG(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n",
|
|||
|
context->eax, context->ebx, context->ecx, context->edx);
|
|||
|
KDDBG(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n",
|
|||
|
context->esp, context->ebp, context->esi, context->edi);
|
|||
|
|
|||
|
#if 0
|
|||
|
kdprintf("Exception EIPs = %08x", er.ExceptionAddress);
|
|||
|
uintptr ebp = context->ebp;
|
|||
|
for (int i = 0; i < 3 && ebp >= Struct_Microsoft_Singularity_BootInfo_PHYSICAL_DISABLED; i++) {
|
|||
|
|
|||
|
uintptr next = ((uintptr *)ebp)[0];
|
|||
|
uintptr code = ((uintptr *)ebp)[1];
|
|||
|
|
|||
|
kdprintf(" %08x", code);
|
|||
|
ebp = next;
|
|||
|
}
|
|||
|
kdprintf("\n");
|
|||
|
#endif
|
|||
|
|
|||
|
KdpEnter();
|
|||
|
|
|||
|
handled = (KdpReportExceptionStateChange(&er, context, firstChance) == ContinueSuccess);
|
|||
|
if (context->num == Struct_Microsoft_Singularity_X86_EVectors_SingleStep &&
|
|||
|
(context->efl & Struct_Microsoft_Singularity_X86_EFlags_TF) == 0) {
|
|||
|
#if 0
|
|||
|
kdprintf("Continuing to eip=%08x [efl=%08x]\n", context->eip, context->efl);
|
|||
|
#endif
|
|||
|
|
|||
|
#if 0
|
|||
|
if (context->eip != 0 && *(uint8*)(context->eip) == 0x9c) {
|
|||
|
// we always override into single-step mode if the next instruction is pushfd.
|
|||
|
context->efl |= Struct_Microsoft_Singularity_X86_EFlags_TF;
|
|||
|
kdprintf("Continuing to eip=%08x [efl=%08x] ****\n", context->eip, context->efl);
|
|||
|
#if 0
|
|||
|
kdprintf(" CXT=%08x THR=%08x REG=%02x\n",
|
|||
|
context, context->_thread, context->regs);
|
|||
|
kdprintf(" EIP=%08x EFL=%08x ERR=%08x CR2=%08x\n",
|
|||
|
context->eip, context->efl, context->err, context->cr2);
|
|||
|
kdprintf(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n",
|
|||
|
context->eax, context->ebx, context->ecx, context->edx);
|
|||
|
kdprintf(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n",
|
|||
|
context->esp, context->ebp, context->esi, context->edi);
|
|||
|
kdprintf(" DR0=%08x DR1=%08x DR2=%08x DR3=%08x\n",
|
|||
|
context->dr0, context->dr1, context->dr2, context->dr3);
|
|||
|
kdprintf(" DR6=%08x DR7=%08x DR2=%08x DR3=%08x\n",
|
|||
|
context->dr6, context->dr7, context->dr2, context->dr3);
|
|||
|
#endif
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
#if 0
|
|||
|
if (context->num == Struct_Microsoft_Singularity_X86_EVectors_SingleStep &&
|
|||
|
context->eip != 0 && *(uint8*)(context->eip) == 0x9c) {
|
|||
|
kdprintf(" CXT=%08x THR=%08x REG=%02x\n",
|
|||
|
context, context->_thread, context->regs);
|
|||
|
kdprintf(" EIP=%08x EFL=%08x ERR=%08x CR2=%08x\n",
|
|||
|
context->eip, context->efl, context->err, context->cr2);
|
|||
|
kdprintf(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n",
|
|||
|
context->eax, context->ebx, context->ecx, context->edx);
|
|||
|
kdprintf(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n",
|
|||
|
context->esp, context->ebp, context->esi, context->edi);
|
|||
|
kdprintf(" DR0=%08x DR1=%08x DR2=%08x DR3=%08x\n",
|
|||
|
context->dr0, context->dr1, context->dr2, context->dr3);
|
|||
|
kdprintf(" DR6=%08x DR7=%08x DR2=%08x DR3=%08x\n",
|
|||
|
context->dr6, context->dr7, context->dr2, context->dr3);
|
|||
|
}
|
|||
|
kdprintf("-- Trap num=0x%02x eip=%08x\n", context->num, context->eip);
|
|||
|
#endif
|
|||
|
|
|||
|
KdpLeave();
|
|||
|
|
|||
|
KdpUnlock();
|
|||
|
|
|||
|
return handled;
|
|||
|
}
|
|||
|
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::
|
|||
|
g_TrapForProcessorSwitch(Struct_Microsoft_Singularity_X86_ThreadContext *context)
|
|||
|
{
|
|||
|
EXCEPTION_RECORD64 er;
|
|||
|
|
|||
|
RtlZeroMemory(&er, sizeof(er));
|
|||
|
er.ExceptionCode = STATUS_WAKE_SYSTEM_DEBUGGER;
|
|||
|
er.ExceptionRecord = (ULONG64)&er;
|
|||
|
er.ExceptionAddress = (ULONG64)context->eip;
|
|||
|
|
|||
|
// KdSave(FALSE);
|
|||
|
KCONTINUE_STATUS status = KdpReportExceptionStateChange(&er, context, true);
|
|||
|
// KdRestore(FALSE);
|
|||
|
return status == ContinueSuccess;
|
|||
|
}
|
|||
|
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_AddProcessor(int cpuId)
|
|||
|
{
|
|||
|
KdpLock();
|
|||
|
KeNumberProcessors = cpuId + 1;
|
|||
|
KdpUnlock();
|
|||
|
}
|
|||
|
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_RevertToUniprocessor()
|
|||
|
{
|
|||
|
KdpLock();
|
|||
|
KeNumberProcessors = 1;
|
|||
|
KdpUnlock();
|
|||
|
}
|
|||
|
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::g_PollForBreak()
|
|||
|
{
|
|||
|
// KdpLock();
|
|||
|
|
|||
|
// Don't re-enter debugger if already debugging.
|
|||
|
if (KdpInDebugger) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// If the debugger is enabled, see if a breakin by the kernel
|
|||
|
// debugger is pending.
|
|||
|
// We might want to enable retry here. The transports support it.
|
|||
|
if (KdDebuggerNotPresent) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// Did we already record a break from the host?
|
|||
|
if (KdpContext.KdpControlCPending) {
|
|||
|
KdpContext.KdpControlCPending = FALSE;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool success = KdPollBreakIn();
|
|||
|
// KdpUnlock();
|
|||
|
return success;
|
|||
|
}
|
|||
|
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_Break()
|
|||
|
{
|
|||
|
__asm int 3;
|
|||
|
}
|
|||
|
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::g_LoadedBinary(UIntPtr baseAddress,
|
|||
|
UIntPtr bytes,
|
|||
|
Class_System_String *name,
|
|||
|
uint32 checksum,
|
|||
|
uint32 timestamp,
|
|||
|
bool silent)
|
|||
|
{
|
|||
|
return g_LoadedBinary(baseAddress,
|
|||
|
bytes,
|
|||
|
(UIntPtr)&name->m_firstChar,
|
|||
|
checksum,
|
|||
|
timestamp,
|
|||
|
silent);
|
|||
|
}
|
|||
|
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::g_LoadedBinary(UIntPtr baseAddress,
|
|||
|
UIntPtr bytes,
|
|||
|
UIntPtr name,
|
|||
|
uint32 checksum,
|
|||
|
uint32 timestamp,
|
|||
|
bool silent)
|
|||
|
{
|
|||
|
KdDebugTrapData trapData, *trapDataPtr = &trapData;
|
|||
|
trapData.tag = KdDebugTrapData::LOADED_BINARY;
|
|||
|
trapData.u.loadedBinary.baseAddress = baseAddress;
|
|||
|
trapData.u.loadedBinary.bytes = bytes;
|
|||
|
trapData.u.loadedBinary.name = name;
|
|||
|
trapData.u.loadedBinary.checksum = checksum;
|
|||
|
trapData.u.loadedBinary.timestamp = timestamp;
|
|||
|
trapData.u.loadedBinary.silent = silent;
|
|||
|
|
|||
|
// Call LoadedBinary via an __asm int 29:
|
|||
|
__asm {
|
|||
|
mov eax, trapDataPtr;
|
|||
|
int 29;
|
|||
|
}
|
|||
|
|
|||
|
return trapData.u.loadedBinary.ret;
|
|||
|
}
|
|||
|
|
|||
|
static void LoadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context)
|
|||
|
{
|
|||
|
KdDebugTrapData *trapData = (KdDebugTrapData *) (context->eax);
|
|||
|
UIntPtr baseAddress = trapData->u.loadedBinary.baseAddress;
|
|||
|
UIntPtr bytes = trapData->u.loadedBinary.bytes;
|
|||
|
UIntPtr nameof = trapData->u.loadedBinary.name;
|
|||
|
uint32 checksum = trapData->u.loadedBinary.checksum;
|
|||
|
uint32 timestamp = trapData->u.loadedBinary.timestamp;
|
|||
|
bool silent = trapData->u.loadedBinary.silent;
|
|||
|
|
|||
|
KLDR_DATA_TABLE_ENTRY_WITH_NAME *pEntry;
|
|||
|
bool good = false;
|
|||
|
WCHAR * name = trim((WCHAR *)nameof);
|
|||
|
USHORT nlen = (USHORT)2 * wcslen(name);
|
|||
|
if (nlen > sizeof(pEntry->wzName)) {
|
|||
|
nlen = sizeof(pEntry->wzName);
|
|||
|
}
|
|||
|
|
|||
|
KdpLock();
|
|||
|
#if 0 // Debug module names
|
|||
|
if (name != NULL) {
|
|||
|
kdprintf("LoadedBinary(%08lx: %ls)\n", (uint64)(uintptr)name, name);
|
|||
|
}
|
|||
|
else {
|
|||
|
kdprintf("LoadedBinary(%08lx)\n", (uint64)(uintptr)name);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
for (int i = 0; i < ARRAYOF(KdModuleKernelEntry); i++) {
|
|||
|
pEntry = &KdModuleKernelEntry[i];
|
|||
|
|
|||
|
if (pEntry->DllBase == 0) {
|
|||
|
pEntry->DllBase = (PVOID *)baseAddress;
|
|||
|
pEntry->CheckSum = checksum;
|
|||
|
pEntry->TimeDateStamp = timestamp;
|
|||
|
pEntry->LoadCount = 1;
|
|||
|
pEntry->SizeOfImage = (uintptr)bytes;
|
|||
|
memcpy(pEntry->wzName, name, nlen);
|
|||
|
RtlInitUnicodeString(&pEntry->FullDllName, name, nlen);
|
|||
|
RtlInitUnicodeString(&pEntry->BaseDllName, name, nlen);
|
|||
|
|
|||
|
#if 0
|
|||
|
printf("----: BaseOfImage %8lx SizeOfImage %8x ModuleName %8x\n",
|
|||
|
(uint64)(uintptr)baseAddress,
|
|||
|
(uintptr)bytes,
|
|||
|
(uintptr)pEntry->BaseDllName.Buffer);
|
|||
|
printf(" ModuleName: %ls (%p)\n",
|
|||
|
pEntry->BaseDllName.Buffer,
|
|||
|
pEntry);
|
|||
|
#endif
|
|||
|
|
|||
|
// We should insert in the right order in the list...
|
|||
|
InsertTailList(&PsLoadedModuleList, &pEntry->InLoadOrderLinks);
|
|||
|
good = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!silent) {
|
|||
|
if (good) {
|
|||
|
KdpReportLoadSymbolsStateChange(pEntry->BaseDllName.Buffer,
|
|||
|
pEntry->BaseDllName.Length,
|
|||
|
(ULONG64)baseAddress,
|
|||
|
(ULONG)0,
|
|||
|
checksum,
|
|||
|
(LONG)bytes,
|
|||
|
FALSE,
|
|||
|
context);
|
|||
|
}
|
|||
|
else {
|
|||
|
KdpReportLoadSymbolsStateChange(NULL,
|
|||
|
0,
|
|||
|
(ULONG64)baseAddress,
|
|||
|
(ULONG)0,
|
|||
|
checksum,
|
|||
|
(LONG)bytes,
|
|||
|
FALSE,
|
|||
|
context);
|
|||
|
}
|
|||
|
}
|
|||
|
KdpUnlock();
|
|||
|
|
|||
|
trapData->u.loadedBinary.ret = good;
|
|||
|
}
|
|||
|
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::
|
|||
|
g_UnloadedBinary(UIntPtr baseAddress, bool silent)
|
|||
|
{
|
|||
|
KdDebugTrapData trapData, *trapDataPtr = &trapData;
|
|||
|
trapData.tag = KdDebugTrapData::UNLOADED_BINARY;
|
|||
|
trapData.u.unloadedBinary.baseAddress = baseAddress;
|
|||
|
trapData.u.unloadedBinary.silent = silent;
|
|||
|
|
|||
|
// Call UnloadedBinary via an __asm int 29:
|
|||
|
__asm {
|
|||
|
mov eax, trapDataPtr;
|
|||
|
int 29;
|
|||
|
}
|
|||
|
|
|||
|
return trapData.u.unloadedBinary.ret;
|
|||
|
}
|
|||
|
|
|||
|
static void UnloadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context)
|
|||
|
{
|
|||
|
KdDebugTrapData *trapData = (KdDebugTrapData *) (context->eax);
|
|||
|
UIntPtr baseAddress = trapData->u.unloadedBinary.baseAddress;
|
|||
|
bool silent = trapData->u.unloadedBinary.silent;
|
|||
|
|
|||
|
bool good = false;
|
|||
|
|
|||
|
KdpLock();
|
|||
|
KLDR_DATA_TABLE_ENTRY_WITH_NAME *pEntry = NULL;
|
|||
|
for (int i = 0; i < ARRAYOF(KdModuleKernelEntry); i++) {
|
|||
|
pEntry = &KdModuleKernelEntry[i];
|
|||
|
|
|||
|
if (pEntry->DllBase == (PVOID*)baseAddress) {
|
|||
|
RemoveEntryList(&pEntry->InLoadOrderLinks);
|
|||
|
good = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (good) {
|
|||
|
if (!silent) {
|
|||
|
// Only tell debugger if we found an image name.
|
|||
|
// The debugger ignores unload requests that lack a processId (not us)
|
|||
|
// or a path name.
|
|||
|
|
|||
|
KdpReportLoadSymbolsStateChange(pEntry->BaseDllName.Buffer,
|
|||
|
pEntry->BaseDllName.Length,
|
|||
|
(ULONG64)baseAddress,
|
|||
|
(ULONG)0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
TRUE,
|
|||
|
context);
|
|||
|
}
|
|||
|
RtlZeroMemory(pEntry, sizeof(*pEntry));
|
|||
|
}
|
|||
|
KdpUnlock();
|
|||
|
|
|||
|
trapData->u.unloadedBinary.ret = good;
|
|||
|
}
|
|||
|
|
|||
|
bool Class_Microsoft_Singularity_DebugStub::g_IsDebuggerPresent()
|
|||
|
{
|
|||
|
return !KdDebuggerNotPresent;
|
|||
|
}
|
|||
|
|
|||
|
//////////////////////////////////////////////////////////////////////////////
|
|||
|
// Note: Leaves the lock held by the client code, so it had better be trustworthy.
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_PrintBegin(WCHAR **buffer, int *length)
|
|||
|
{
|
|||
|
if (KdDebuggerNotPresent) {
|
|||
|
*buffer = NULL;
|
|||
|
*length = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
else {
|
|||
|
KdpLock();
|
|||
|
|
|||
|
*buffer = (WCHAR *)KdpMessageBuffer;
|
|||
|
*length = sizeof(KdpMessageBuffer) / sizeof(WCHAR);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Note: Assumes the lock is held, so the client code had better be trustworthy.
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_PrintComplete(WCHAR *buffer, int length)
|
|||
|
{
|
|||
|
if (KdDebuggerNotPresent) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
CHAR *out = KdpMessageBuffer;
|
|||
|
if (length > arrayof(KdpMessageBuffer)) {
|
|||
|
length = arrayof(KdpMessageBuffer);
|
|||
|
}
|
|||
|
|
|||
|
for (int i = 0; i < length; i++) {
|
|||
|
*out++ = (CHAR)*buffer++;
|
|||
|
}
|
|||
|
|
|||
|
// If the total message length is greater than the maximum packet size,
|
|||
|
// then truncate the output string.
|
|||
|
//
|
|||
|
if ((sizeof(DBGKD_DEBUG_IO) + length) > PACKET_MAX_SIZE) {
|
|||
|
length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Construct the print string message and message descriptor.
|
|||
|
//
|
|||
|
DBGKD_DEBUG_IO DebugIo;
|
|||
|
DebugIo.ApiNumber = DbgKdPrintStringApi;
|
|||
|
DebugIo.ProcessorLevel = KeProcessorLevel;
|
|||
|
DebugIo.Processor = (USHORT)GetCurrentProcessorNumber();
|
|||
|
DebugIo.u.PrintString.LengthOfString = length;
|
|||
|
|
|||
|
STRING MessageHeader;
|
|||
|
MessageHeader.Length = sizeof(DBGKD_DEBUG_IO);
|
|||
|
MessageHeader.Buffer = (PCHAR)&DebugIo;
|
|||
|
|
|||
|
//
|
|||
|
// Construct the print string data and data descriptor.
|
|||
|
//
|
|||
|
STRING MessageData;
|
|||
|
MessageData.Length = (USHORT)length;
|
|||
|
MessageData.Buffer = KdpMessageBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Send packet to the kernel debugger on the host machine.
|
|||
|
//
|
|||
|
KdSendPacket(PACKET_TYPE_KD_DEBUG_IO,
|
|||
|
&MessageHeader,
|
|||
|
&MessageData,
|
|||
|
&KdpContext);
|
|||
|
|
|||
|
KdpUnlock();
|
|||
|
}
|
|||
|
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_Print(WCHAR *buf, int len)
|
|||
|
{
|
|||
|
if (KdDebuggerNotPresent) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
WCHAR *buffer;
|
|||
|
int length;
|
|||
|
|
|||
|
g_PrintBegin(&buffer, &length);
|
|||
|
g_PrintComplete(buf, len);
|
|||
|
}
|
|||
|
|
|||
|
void Class_Microsoft_Singularity_DebugStub::g_Print(WCHAR *buf)
|
|||
|
{
|
|||
|
int len = 0;
|
|||
|
|
|||
|
while (buf[len] != '\0') {
|
|||
|
len++;
|
|||
|
}
|
|||
|
g_Print(buf, len);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
///////////////////////////////////////////////////////////////// End of File.
|