From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Debugger/DBGCRemoteKd.cpp | 4550 ++++++++++++++++++++++++++++++++++++ 1 file changed, 4550 insertions(+) create mode 100644 src/VBox/Debugger/DBGCRemoteKd.cpp (limited to 'src/VBox/Debugger/DBGCRemoteKd.cpp') diff --git a/src/VBox/Debugger/DBGCRemoteKd.cpp b/src/VBox/Debugger/DBGCRemoteKd.cpp new file mode 100644 index 00000000..c73d6884 --- /dev/null +++ b/src/VBox/Debugger/DBGCRemoteKd.cpp @@ -0,0 +1,4550 @@ +/* $Id: DBGCRemoteKd.cpp $ */ +/** @file + * DBGC - Debugger Console, Windows Kd Remote Stub. + */ + +/* + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include /* VMR3GetVM() */ +#include /* HMR3IsEnabled */ +#include /* NEMR3IsEnabled */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "DBGCInternal.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ + +/** Number of milliseconds we wait for new data to arrive when a new packet was detected. */ +#define DBGC_KD_RECV_TIMEOUT_MS UINT32_C(1000) + +/** NT status code - Success. */ +#define NTSTATUS_SUCCESS 0 +/** NT status code - buffer overflow. */ +#define NTSTATUS_BUFFER_OVERFLOW UINT32_C(0x80000005) +/** NT status code - operation unsuccesful. */ +#define NTSTATUS_UNSUCCESSFUL UINT32_C(0xc0000001) +/** NT status code - operation not implemented. */ +#define NTSTATUS_NOT_IMPLEMENTED UINT32_C(0xc0000002) +/** NT status code - Object not found. */ +#define NTSTATUS_NOT_FOUND UINT32_C(0xc0000225) + +/** Offset where the KD version block pointer is stored in the KPCR. + * From: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kprcb/amd64.htm */ +#define KD_KPCR_VERSION_BLOCK_ADDR_OFF 0x34 + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * KD packet header as sent over the wire. + */ +typedef struct KDPACKETHDR +{ + /** Packet signature (leader) - defines the type of packet. */ + uint32_t u32Signature; + /** Packet (sub) type. */ + uint16_t u16SubType; + /** Size of the packet body in bytes.*/ + uint16_t cbBody; + /** Packet ID. */ + uint32_t idPacket; + /** Checksum of the packet body. */ + uint32_t u32ChkSum; +} KDPACKETHDR; +AssertCompileSize(KDPACKETHDR, 16); +/** Pointer to a packet header. */ +typedef KDPACKETHDR *PKDPACKETHDR; +/** Pointer to a const packet header. */ +typedef const KDPACKETHDR *PCKDPACKETHDR; + +/** Signature for a data packet. */ +#define KD_PACKET_HDR_SIGNATURE_DATA UINT32_C(0x30303030) +/** First byte for a data packet header. */ +#define KD_PACKET_HDR_SIGNATURE_DATA_BYTE 0x30 +/** Signature for a control packet. */ +#define KD_PACKET_HDR_SIGNATURE_CONTROL UINT32_C(0x69696969) +/** First byte for a control packet header. */ +#define KD_PACKET_HDR_SIGNATURE_CONTROL_BYTE 0x69 +/** Signature for a breakin packet. */ +#define KD_PACKET_HDR_SIGNATURE_BREAKIN UINT32_C(0x62626262) +/** First byte for a breakin packet header. */ +#define KD_PACKET_HDR_SIGNATURE_BREAKIN_BYTE 0x62 + +/** @name Packet sub types. + * @{ */ +#define KD_PACKET_HDR_SUB_TYPE_STATE_CHANGE32 UINT16_C(1) +#define KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE UINT16_C(2) +#define KD_PACKET_HDR_SUB_TYPE_DEBUG_IO UINT16_C(3) +#define KD_PACKET_HDR_SUB_TYPE_ACKNOWLEDGE UINT16_C(4) +#define KD_PACKET_HDR_SUB_TYPE_RESEND UINT16_C(5) +#define KD_PACKET_HDR_SUB_TYPE_RESET UINT16_C(6) +#define KD_PACKET_HDR_SUB_TYPE_STATE_CHANGE64 UINT16_C(7) +#define KD_PACKET_HDR_SUB_TYPE_POLL_BREAKIN UINT16_C(8) +#define KD_PACKET_HDR_SUB_TYPE_TRACE_IO UINT16_C(9) +#define KD_PACKET_HDR_SUB_TYPE_CONTROL_REQUEST UINT16_C(10) +#define KD_PACKET_HDR_SUB_TYPE_FILE_IO UINT16_C(11) +#define KD_PACKET_HDR_SUB_TYPE_MAX UINT16_C(12) +/** @} */ + +/** Initial packet ID value. */ +#define KD_PACKET_HDR_ID_INITIAL UINT32_C(0x80800800) +/** Packet ID value after a resync. */ +#define KD_PACKET_HDR_ID_RESET UINT32_C(0x80800000) + +/** Trailing byte of a packet. */ +#define KD_PACKET_TRAILING_BYTE 0xaa + + +/** Maximum number of parameters in the exception record. */ +#define KDPACKETEXCP_PARMS_MAX 15 + +/** + * 64bit exception record. + */ +typedef struct KDPACKETEXCP64 +{ + /** The exception code identifying the excpetion. */ + uint32_t u32ExcpCode; + /** Flags associated with the exception. */ + uint32_t u32ExcpFlags; + /** Pointer to a chained exception record. */ + uint64_t u64PtrExcpRecNested; + /** Address where the exception occurred. */ + uint64_t u64PtrExcpAddr; + /** Number of parameters in the exception information array. */ + uint32_t cExcpParms; + /** Alignment. */ + uint32_t u32Alignment; + /** Exception parameters array. */ + uint64_t au64ExcpParms[KDPACKETEXCP_PARMS_MAX]; +} KDPACKETEXCP64; +AssertCompileSize(KDPACKETEXCP64, 152); +/** Pointer to an exception record. */ +typedef KDPACKETEXCP64 *PKDPACKETEXCP64; +/** Pointer to a const exception record. */ +typedef const KDPACKETEXCP64 *PCKDPACKETEXCP64; + + +/** + * amd64 NT context structure. + */ +typedef struct NTCONTEXT64 +{ + /** The P[1-6]Home members. */ + uint64_t au64PHome[6]; + /** Context flags indicating the valid bits, see NTCONTEXT_F_XXX. */ + uint32_t fContext; + /** MXCSR register. */ + uint32_t u32RegMxCsr; + /** CS selector. */ + uint16_t u16SegCs; + /** DS selector. */ + uint16_t u16SegDs; + /** ES selector. */ + uint16_t u16SegEs; + /** FS selector. */ + uint16_t u16SegFs; + /** GS selector. */ + uint16_t u16SegGs; + /** SS selector. */ + uint16_t u16SegSs; + /** EFlags register. */ + uint32_t u32RegEflags; + /** DR0 register. */ + uint64_t u64RegDr0; + /** DR1 register. */ + uint64_t u64RegDr1; + /** DR2 register. */ + uint64_t u64RegDr2; + /** DR3 register. */ + uint64_t u64RegDr3; + /** DR6 register. */ + uint64_t u64RegDr6; + /** DR7 register. */ + uint64_t u64RegDr7; + /** RAX register. */ + uint64_t u64RegRax; + /** RCX register. */ + uint64_t u64RegRcx; + /** RDX register. */ + uint64_t u64RegRdx; + /** RBX register. */ + uint64_t u64RegRbx; + /** RSP register. */ + uint64_t u64RegRsp; + /** RBP register. */ + uint64_t u64RegRbp; + /** RSI register. */ + uint64_t u64RegRsi; + /** RDI register. */ + uint64_t u64RegRdi; + /** R8 register. */ + uint64_t u64RegR8; + /** R9 register. */ + uint64_t u64RegR9; + /** R10 register. */ + uint64_t u64RegR10; + /** R11 register. */ + uint64_t u64RegR11; + /** R12 register. */ + uint64_t u64RegR12; + /** R13 register. */ + uint64_t u64RegR13; + /** R14 register. */ + uint64_t u64RegR14; + /** R15 register. */ + uint64_t u64RegR15; + /** RIP register. */ + uint64_t u64RegRip; + /** Extended floating point save area. */ + X86FXSTATE FxSave; + /** AVX(?) vector registers. */ + RTUINT128U aRegsVec[26]; + /** Vector control register. */ + uint64_t u64RegVecCtrl; + /** Debug control. */ + uint64_t u64DbgCtrl; + /** @todo lbr */ + uint64_t u64LastBrToRip; + uint64_t u64LastBrFromRip; + uint64_t u64LastExcpToRip; + uint64_t u64LastExcpFromRip; +} NTCONTEXT64; +AssertCompileSize(NTCONTEXT64, 1232); +AssertCompileMemberOffset(NTCONTEXT64, FxSave, 0x100); +AssertCompileMemberOffset(NTCONTEXT64, aRegsVec, 0x300); +/** Pointer to an amd64 NT context. */ +typedef NTCONTEXT64 *PNTCONTEXT64; +/** Pointer to a const amd64 NT context. */ +typedef const NTCONTEXT64 *PCNTCONTEXT64; + + +/** + * 64bit [GI]DT descriptor. + */ +typedef struct NTKCONTEXTDESC64 +{ + /** Alignment. */ + uint16_t au16Alignment[3]; + /** Limit. */ + uint16_t u16Limit; + /** Base address. */ + uint64_t u64PtrBase; +} NTKCONTEXTDESC64; +AssertCompileSize(NTKCONTEXTDESC64, 2 * 8); +/** Pointer to a 64bit [GI]DT descriptor. */ +typedef NTKCONTEXTDESC64 *PNTKCONTEXTDESC64; +/** Pointer to a const 64bit [GI]DT descriptor. */ +typedef const NTKCONTEXTDESC64 *PCNTKCONTEXTDESC64; + + +/** + * Kernel context as queried by KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE + */ +typedef struct NTKCONTEXT64 +{ + /** CR0 register. */ + uint64_t u64RegCr0; + /** CR2 register. */ + uint64_t u64RegCr2; + /** CR3 register. */ + uint64_t u64RegCr3; + /** CR4 register. */ + uint64_t u64RegCr4; + /** DR0 register. */ + uint64_t u64RegDr0; + /** DR1 register. */ + uint64_t u64RegDr1; + /** DR2 register. */ + uint64_t u64RegDr2; + /** DR3 register. */ + uint64_t u64RegDr3; + /** DR6 register. */ + uint64_t u64RegDr6; + /** DR7 register. */ + uint64_t u64RegDr7; + /** GDTR. */ + NTKCONTEXTDESC64 Gdtr; + /** IDTR. */ + NTKCONTEXTDESC64 Idtr; + /** TR register. */ + uint16_t u16RegTr; + /** LDTR register. */ + uint16_t u16RegLdtr; + /** MXCSR register. */ + uint32_t u32RegMxCsr; + /** Debug control. */ + uint64_t u64DbgCtrl; + /** @todo lbr */ + uint64_t u64LastBrToRip; + uint64_t u64LastBrFromRip; + uint64_t u64LastExcpToRip; + uint64_t u64LastExcpFromRip; + /** CR8 register. */ + uint64_t u64RegCr8; + /** GS base MSR register. */ + uint64_t u64MsrGsBase; + /** Kernel GS base MSR register. */ + uint64_t u64MsrKernelGsBase; + /** STAR MSR register. */ + uint64_t u64MsrStar; + /** LSTAR MSR register. */ + uint64_t u64MsrLstar; + /** CSTAR MSR register. */ + uint64_t u64MsrCstar; + /** SFMASK MSR register. */ + uint64_t u64MsrSfMask; + /** XCR0 register. */ + uint64_t u64RegXcr0; + /** Standard context. */ + NTCONTEXT64 Ctx; +} NTKCONTEXT64; +AssertCompileMemberOffset(NTKCONTEXT64, Ctx, 224); +/** Pointer to an amd64 NT context. */ +typedef NTKCONTEXT64 *PNTKCONTEXT64; +/** Pointer to a const amd64 NT context. */ +typedef const NTKCONTEXT64 *PCNTKCONTEXT64; + + +/** + * 32bit context FPU save area. + */ +typedef struct NTCONTEXT32_FPU_SAVE_AREA +{ + uint32_t u32CtrlWord; + uint32_t u32StatusWord; + uint32_t u32TagWord; + uint32_t u32ErrorOff; + uint32_t u32ErrorSel; + uint32_t u32DataOff; + uint32_t u32DataSel; + X86FPUMMX aFpuRegs[8]; + uint32_t u32Cr0Npx; +} NTCONTEXT32_FPU_SAVE_AREA; +/** Pointer to an 32bit context FPU save area. */ +typedef NTCONTEXT32_FPU_SAVE_AREA *PNTCONTEXT32_FPU_SAVE_AREA; +/** Pointer to a const 32bit context FPU save area. */ +typedef const NTCONTEXT32_FPU_SAVE_AREA *PCNTCONTEXT32_FPU_SAVE_AREA; + + +/** + * i386 NT context structure. + */ +typedef struct NTCONTEXT32 +{ + /** Context flags indicating the valid bits, see NTCONTEXT_F_XXX. */ + uint32_t fContext; + /** DR0 register. */ + uint32_t u32RegDr0; + /** DR1 register. */ + uint32_t u32RegDr1; + /** DR2 register. */ + uint32_t u32RegDr2; + /** DR3 register. */ + uint32_t u32RegDr3; + /** DR6 register. */ + uint32_t u32RegDr6; + /** DR7 register. */ + uint32_t u32RegDr7; + /** Floating point save area. */ + NTCONTEXT32_FPU_SAVE_AREA FloatSave; + /** GS segment. */ + uint32_t u32SegGs; + /** FS segment. */ + uint32_t u32SegFs; + /** ES segment. */ + uint32_t u32SegEs; + /** DS segment. */ + uint32_t u32SegDs; + /** EDI register. */ + uint32_t u32RegEdi; + /** ESI register. */ + uint32_t u32RegEsi; + /** EBX register. */ + uint32_t u32RegEbx; + /** EDX register. */ + uint32_t u32RegEdx; + /** ECX register. */ + uint32_t u32RegEcx; + /** EAX register. */ + uint32_t u32RegEax; + /** EBP register. */ + uint32_t u32RegEbp; + /** EIP register. */ + uint32_t u32RegEip; + /** CS segment. */ + uint32_t u32SegCs; + /** EFLAGS register. */ + uint32_t u32RegEflags; + /** ESP register. */ + uint32_t u32RegEsp; + /** SS segment. */ + uint32_t u32SegSs; + /** @todo Extended registers */ + uint8_t abRegsExtended[512]; +} NTCONTEXT32; +AssertCompileSize(NTCONTEXT32, 716); +/** Pointer to an i386 NT context. */ +typedef NTCONTEXT32 *PNTCONTEXT32; +/** Pointer to a const i386 NT context. */ +typedef const NTCONTEXT32 *PCNTCONTEXT32; + + +/** + * 32bit [GI]DT descriptor. + */ +typedef struct NTKCONTEXTDESC32 +{ + /** Alignment. */ + uint16_t u16Alignment; + /** Limit. */ + uint16_t u16Limit; + /** Base address. */ + uint32_t u32PtrBase; +} NTKCONTEXTDESC32; +AssertCompileSize(NTKCONTEXTDESC32, 2 * 4); +/** Pointer to an 32bit [GI]DT descriptor. */ +typedef NTKCONTEXTDESC32 *PNTKCONTEXTDESC32; +/** Pointer to a const 32bit [GI]DT descriptor. */ +typedef const NTKCONTEXTDESC32 *PCNTKCONTEXTDESC32; + + +/** + * 32bit Kernel context as queried by KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE + */ +typedef struct NTKCONTEXT32 +{ + /** CR0 register. */ + uint32_t u32RegCr0; + /** CR2 register. */ + uint32_t u32RegCr2; + /** CR3 register. */ + uint32_t u32RegCr3; + /** CR4 register. */ + uint32_t u32RegCr4; + /** DR0 register. */ + uint32_t u32RegDr0; + /** DR1 register. */ + uint32_t u32RegDr1; + /** DR2 register. */ + uint32_t u32RegDr2; + /** DR3 register. */ + uint32_t u32RegDr3; + /** DR6 register. */ + uint32_t u32RegDr6; + /** DR7 register. */ + uint32_t u32RegDr7; + /** GDTR. */ + NTKCONTEXTDESC32 Gdtr; + /** IDTR. */ + NTKCONTEXTDESC32 Idtr; + /** TR register. */ + uint16_t u16RegTr; + /** LDTR register. */ + uint16_t u16RegLdtr; + /** Padding. */ + uint8_t abPad[24]; +} NTKCONTEXT32; +AssertCompileSize(NTKCONTEXT32, 84); +/** Pointer to an i386 NT context. */ +typedef NTKCONTEXT32 *PNTKCONTEXT32; +/** Pointer to a const i386 NT context. */ +typedef const NTKCONTEXT32 *PCNTKCONTEXT32; + + +/** x86 context. */ +#define NTCONTEXT_F_X86 UINT32_C(0x00010000) +/** AMD64 context. */ +#define NTCONTEXT_F_AMD64 UINT32_C(0x00100000) +/** Control registers valid (CS, (R)SP, (R)IP, FLAGS and BP). */ +#define NTCONTEXT_F_CONTROL RT_BIT_32(0) +/** Integer registers valid. */ +#define NTCONTEXT_F_INTEGER RT_BIT_32(1) +/** Segment registers valid. */ +#define NTCONTEXT_F_SEGMENTS RT_BIT_32(2) +/** Floating point registers valid. */ +#define NTCONTEXT_F_FLOATING_POINT RT_BIT_32(3) +/** Debug registers valid. */ +#define NTCONTEXT_F_DEBUG RT_BIT_32(4) +/** Extended registers valid (x86 only). */ +#define NTCONTEXT_F_EXTENDED RT_BIT_32(5) +/** Full x86 context valid. */ +#define NTCONTEXT32_F_FULL (NTCONTEXT_F_X86 | NTCONTEXT_F_CONTROL | NTCONTEXT_F_INTEGER | NTCONTEXT_F_SEGMENTS) +/** Full amd64 context valid. */ +#define NTCONTEXT64_F_FULL (NTCONTEXT_F_AMD64 | NTCONTEXT_F_CONTROL | NTCONTEXT_F_INTEGER | NTCONTEXT_F_SEGMENTS) + + +/** + * 32bit exception record. + */ +typedef struct KDPACKETEXCP32 +{ + /** The exception code identifying the excpetion. */ + uint32_t u32ExcpCode; + /** Flags associated with the exception. */ + uint32_t u32ExcpFlags; + /** Pointer to a chained exception record. */ + uint32_t u32PtrExcpRecNested; + /** Address where the exception occurred. */ + uint32_t u32PtrExcpAddr; + /** Number of parameters in the exception information array. */ + uint32_t cExcpParms; + /** Exception parameters array. */ + uint32_t au32ExcpParms[KDPACKETEXCP_PARMS_MAX]; +} KDPACKETEXCP32; +AssertCompileSize(KDPACKETEXCP32, 80); +/** Pointer to an exception record. */ +typedef KDPACKETEXCP32 *PKDPACKETEXCP32; +/** Pointer to a const exception record. */ +typedef const KDPACKETEXCP32 *PCKDPACKETEXCP32; + + +/** @name Exception codes. + * @{ */ +/** A breakpoint was hit. */ +#define KD_PACKET_EXCP_CODE_BKPT UINT32_C(0x80000003) +/** An instruction was single stepped. */ +#define KD_PACKET_EXCP_CODE_SINGLE_STEP UINT32_C(0x80000004) +/** @} */ + + +/** Maximum number of bytes in the instruction stream. */ +#define KD_PACKET_CTRL_REPORT_INSN_STREAM_MAX 16 + +/** + * 64bit control report record. + */ +typedef struct KDPACKETCTRLREPORT64 +{ + /** Value of DR6. */ + uint64_t u64RegDr6; + /** Value of DR7. */ + uint64_t u64RegDr7; + /** EFLAGS. */ + uint32_t u32RegEflags; + /** Number of instruction bytes in the instruction stream. */ + uint16_t cbInsnStream; + /** Report flags. */ + uint16_t fFlags; + /** The instruction stream. */ + uint8_t abInsn[KD_PACKET_CTRL_REPORT_INSN_STREAM_MAX]; + /** CS selector. */ + uint16_t u16SegCs; + /** DS selector. */ + uint16_t u16SegDs; + /** ES selector. */ + uint16_t u16SegEs; + /** FS selector. */ + uint16_t u16SegFs; +} KDPACKETCTRLREPORT64; +AssertCompileSize(KDPACKETCTRLREPORT64, 2 * 8 + 4 + 2 * 2 + 16 + 4 * 2); +/** Pointer to a control report record. */ +typedef KDPACKETCTRLREPORT64 *PKDPACKETCTRLREPORT64; +/** Pointer to a const control report record. */ +typedef const KDPACKETCTRLREPORT64 *PCKDPACKETCTRLREPORT64; + + +/** + * 64bit state change packet body. + */ +typedef struct KDPACKETSTATECHANGE64 +{ + /** The new state. */ + uint32_t u32StateNew; + /** The processor level. */ + uint16_t u16CpuLvl; + /** The processor ID generating the state change. */ + uint16_t idCpu; + /** Number of processors in the system. */ + uint32_t cCpus; + /** Alignment. */ + uint32_t u32Alignment; + /** The thread ID currently executing when the state change occurred. */ + uint64_t idThread; + /** Program counter of the thread. */ + uint64_t u64RipThread; + /** Data based on the state. */ + union + { + /** Exception occurred data. */ + struct + { + /** The exception record. */ + KDPACKETEXCP64 ExcpRec; + /** First chance(?). */ + uint32_t u32FirstChance; + } Exception; + } u; + /** The control report */ + union + { + /** AMD64 control report. */ + KDPACKETCTRLREPORT64 Amd64; + } uCtrlReport; +} KDPACKETSTATECHANGE64; +//AssertCompileSize(KDPACKETSTATECHANGE64, 4 + 2 * 2 + 2 * 4 + 2 * 8 + sizeof(KDPACKETEXCP64) + 4 + sizeof(KDPACKETCTRLREPORT64)); +/** Pointer to a 64bit state change packet body. */ +typedef KDPACKETSTATECHANGE64 *PKDPACKETSTATECHANGE64; +/** Pointer to a const 64bit state change packet body. */ +typedef const KDPACKETSTATECHANGE64 *PCKDPACKETSTATECHANGE64; + + +/** @name State change state types. + * @{ */ +/** Minimum state change type. */ +#define KD_PACKET_STATE_CHANGE_MIN UINT32_C(0x00003030) +/** An exception occured. */ +#define KD_PACKET_STATE_CHANGE_EXCEPTION KD_PACKET_STATE_CHANGE_MIN +/** Symbols were loaded(?). */ +#define KD_PACKET_STATE_CHANGE_LOAD_SYMBOLS UINT32_C(0x00003031) +/** Command string (custom command was executed?). */ +#define KD_PACKET_STATE_CHANGE_CMD_STRING UINT32_C(0x00003032) +/** Maximum state change type (exclusive). */ +#define KD_PACKET_STATE_CHANGE_MAX UINT32_C(0x00003033) +/** @} */ + + +/** + * Debug I/O payload. + */ +typedef struct KDPACKETDEBUGIO +{ + /** Debug I/O payload type (KD_PACKET_DEBUG_IO_STRING). */ + uint32_t u32Type; + /** The processor level. */ + uint16_t u16CpuLvl; + /** The processor ID generating this packet. */ + uint16_t idCpu; + /** Type dependent data. */ + union + { + /** Debug string sent. */ + struct + { + /** Length of the string following in bytes. */ + uint32_t cbStr; + /** Some padding it looks like. */ + uint32_t u32Pad; + } Str; + /** Debug prompt. */ + struct + { + /** Length of prompt. */ + uint32_t cbPrompt; + /** Size of the string returned on success. */ + uint32_t cbReturn; + } Prompt; + } u; +} KDPACKETDEBUGIO; +AssertCompileSize(KDPACKETDEBUGIO, 16); +/** Pointer to a Debug I/O payload. */ +typedef KDPACKETDEBUGIO *PKDPACKETDEBUGIO; +/** Pointer to a const Debug I/O payload. */ +typedef const KDPACKETDEBUGIO *PCKDPACKETDEBUGIO; + + +/** @name Debug I/O types. + * @{ */ +/** Debug string output (usually DbgPrint() and friends). */ +#define KD_PACKET_DEBUG_IO_STRING UINT32_C(0x00003230) +/** Get debug string (DbgPrompt()). */ +#define KD_PACKET_DEBUG_IO_GET_STRING UINT32_C(0x00003231) +/** @} */ + + +/** + * 64bit get version manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_GETVERSION64 +{ + /** Major version. */ + uint16_t u16VersMaj; + /** Minor version. */ + uint16_t u16VersMin; + /** Protocol version. */ + uint8_t u8VersProtocol; + /** KD secondary version. */ + uint8_t u8VersKdSecondary; + /** Flags. */ + uint16_t fFlags; + /** Machine type. */ + uint16_t u16MachineType; + /** Maximum packet type. */ + uint8_t u8MaxPktType; + /** Maximum state change */ + uint8_t u8MaxStateChange; + /** Maximum manipulate request ID. */ + uint8_t u8MaxManipulate; + /** Some simulation flag. */ + uint8_t u8Simulation; + /** Padding. */ + uint16_t u16Padding; + /** Kernel base. */ + uint64_t u64PtrKernBase; + /** Pointer of the loaded module list head. */ + uint64_t u64PtrPsLoadedModuleList; + /** Pointer of the debugger data list. */ + uint64_t u64PtrDebuggerDataList; +} KDPACKETMANIPULATE_GETVERSION64; +AssertCompileSize(KDPACKETMANIPULATE_GETVERSION64, 40); +/** Pointer to a 64bit get version manipulate payload. */ +typedef KDPACKETMANIPULATE_GETVERSION64 *PKDPACKETMANIPULATE_GETVERSION64; +/** Pointer to a const 64bit get version manipulate payload. */ +typedef const KDPACKETMANIPULATE_GETVERSION64 *PCKDPACKETMANIPULATE_GETVERSION64; + + +/** @name Get version flags. + * @{ */ +/** Flag whether this is a multi processor kernel. */ +#define KD_PACKET_MANIPULATE64_GET_VERSION_F_MP RT_BIT_32(0) +/** Flag whether the pointer is 64bit. */ +#define KD_PACKET_MANIPULATE64_GET_VERSION_F_PTR64 RT_BIT_32(2) +/** @} */ + + +/** + * 64bit memory transfer manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_XFERMEM64 +{ + /** Target base address. */ + uint64_t u64PtrTarget; + /** Requested number of bytes to transfer*/ + uint32_t cbXferReq; + /** Number of bytes actually transferred (response). */ + uint32_t cbXfered; + /** Some padding?. */ + uint64_t au64Pad[3]; +} KDPACKETMANIPULATE_XFERMEM64; +AssertCompileSize(KDPACKETMANIPULATE_XFERMEM64, 40); +/** Pointer to a 64bit memory transfer manipulate payload. */ +typedef KDPACKETMANIPULATE_XFERMEM64 *PKDPACKETMANIPULATE_XFERMEM64; +/** Pointer to a const 64bit memory transfer manipulate payload. */ +typedef const KDPACKETMANIPULATE_XFERMEM64 *PCKDPACKETMANIPULATE_XFERMEM64; + + +/** + * 64bit control space transfer manipulate payload. + * + * @note Same layout as the memory transfer but the pointer has a different meaning so + * we moved it into a separate request structure. + */ +typedef struct KDPACKETMANIPULATE_XFERCTRLSPACE64 +{ + /** Identifier of the item to transfer in the control space. */ + uint64_t u64IdXfer; + /** Requested number of bytes to transfer*/ + uint32_t cbXferReq; + /** Number of bytes actually transferred (response). */ + uint32_t cbXfered; + /** Some padding?. */ + uint64_t au64Pad[3]; +} KDPACKETMANIPULATE_XFERCTRLSPACE64; +AssertCompileSize(KDPACKETMANIPULATE_XFERCTRLSPACE64, 40); +/** Pointer to a 64bit memory transfer manipulate payload. */ +typedef KDPACKETMANIPULATE_XFERCTRLSPACE64 *PKDPACKETMANIPULATE_XFERCTRLSPACE64; +/** Pointer to a const 64bit memory transfer manipulate payload. */ +typedef const KDPACKETMANIPULATE_XFERCTRLSPACE64 *PCKDPACKETMANIPULATE_XFERCTRLSPACE64; + + +/** @name Known control space identifiers. + * @{ */ +/** Read/Write KPCR address. */ +#define KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KPCR UINT64_C(0) +/** Read/Write KPCRB address. */ +#define KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KPCRB UINT64_C(1) +/** Read/Write Kernel context. */ +#define KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KCTX UINT64_C(2) +/** Read/Write current kernel thread. */ +#define KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KTHRD UINT64_C(3) +/** @} */ + + +/** + * 64bit restore breakpoint manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_RESTOREBKPT64 +{ + /** The breakpoint handle to restore. */ + uint32_t u32HndBkpt; + /** Blows up the request to the required size. */ + uint8_t abPad[36]; +} KDPACKETMANIPULATE_RESTOREBKPT64; +AssertCompileSize(KDPACKETMANIPULATE_RESTOREBKPT64, 40); +/** Pointer to a 64bit restore breakpoint manipulate payload. */ +typedef KDPACKETMANIPULATE_RESTOREBKPT64 *PKDPACKETMANIPULATE_RESTOREBKPT64; +/** Pointer to a const 64bit restore breakpoint manipulate payload. */ +typedef const KDPACKETMANIPULATE_RESTOREBKPT64 *PCKDPACKETMANIPULATE_RESTOREBKPT64; + + +/** + * 64bit write breakpoint manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_WRITEBKPT64 +{ + /** Where to write the breakpoint. */ + uint64_t u64PtrBkpt; + /** The breakpoint handle returned in the response. */ + uint32_t u32HndBkpt; + /** Blows up the request to the required size. */ + uint8_t abPad[28]; +} KDPACKETMANIPULATE_WRITEBKPT64; +AssertCompileSize(KDPACKETMANIPULATE_WRITEBKPT64, 40); +/** Pointer to a 64bit write breakpoint manipulate payload. */ +typedef KDPACKETMANIPULATE_WRITEBKPT64 *PKDPACKETMANIPULATE_WRITEBKPT64; +/** Pointer to a const 64bit write breakpoint manipulate payload. */ +typedef const KDPACKETMANIPULATE_WRITEBKPT64 *PCKDPACKETMANIPULATE_WRITEBKPT64; + + +/** + * Context extended manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_CONTEXTEX +{ + /** Where to start copying the context. */ + uint32_t offStart; + /** Number of bytes to transfer. */ + uint32_t cbXfer; + /** Number of bytes actually transfered. */ + uint32_t cbXfered; + /** Blows up the request to the required size. */ + uint8_t abPad[28]; +} KDPACKETMANIPULATE_CONTEXTEX; +AssertCompileSize(KDPACKETMANIPULATE_CONTEXTEX, 40); +/** Pointer to a context extended manipulate payload. */ +typedef KDPACKETMANIPULATE_CONTEXTEX *PKDPACKETMANIPULATE_CONTEXTEX; +/** Pointer to a const context extended manipulate payload. */ +typedef const KDPACKETMANIPULATE_CONTEXTEX *PCKDPACKETMANIPULATE_CONTEXTEX; + + +/** + * Continue manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_CONTINUE +{ + /** Continue (status?). */ + uint32_t u32NtContSts; + /** Blows up the request to the required size. */ + uint8_t abPad[36]; +} KDPACKETMANIPULATE_CONTINUE; +AssertCompileSize(KDPACKETMANIPULATE_CONTINUE, 40); +/** Pointer to a continue manipulate payload. */ +typedef KDPACKETMANIPULATE_CONTINUE *PKDPACKETMANIPULATE_CONTINUE; +/** Pointer to a const continue manipulate payload. */ +typedef const KDPACKETMANIPULATE_CONTINUE *PCKDPACKETMANIPULATE_CONTINUE; + + +/** + * Continue 2 manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_CONTINUE2 +{ + /** Continue (status?). */ + uint32_t u32NtContSts; + /** Trace flag. */ + uint32_t fTrace; + /** Bitsize dependent data. */ + union + { + /** 32bit. */ + struct + { + /** DR7 value to continue with. */ + uint32_t u32RegDr7; + /** @todo (?) */ + uint32_t u32SymCurStart; + uint32_t u32SymCurEnd; + } x86; + /** 64bit. */ + struct + { + /** DR7 value to continue with. */ + uint64_t u64RegDr7; + /** @todo (?) */ + uint64_t u64SymCurStart; + uint64_t u64SymCurEnd; + } amd64; + } u; + /** Blows up the request to the required size. */ + uint8_t abPad[8]; +} KDPACKETMANIPULATE_CONTINUE2; +AssertCompileSize(KDPACKETMANIPULATE_CONTINUE2, 40); +/** Pointer to a continue 2 manipulate payload. */ +typedef KDPACKETMANIPULATE_CONTINUE2 *PKDPACKETMANIPULATE_CONTINUE2; +/** Pointer to a const continue 2 manipulate payload. */ +typedef const KDPACKETMANIPULATE_CONTINUE2 *PCKDPACKETMANIPULATE_CONTINUE2; + + +/** + * Set context manipulate payload. + */ +typedef struct KDPACKETMANIPULATE_SETCONTEXT +{ + /** Continue (status?). */ + uint32_t u32CtxFlags; + /** Blows up the request to the required size. */ + uint8_t abPad[36]; +} KDPACKETMANIPULATE_SETCONTEXT; +AssertCompileSize(KDPACKETMANIPULATE_SETCONTEXT, 40); +/** Pointer to a set context manipulate payload. */ +typedef KDPACKETMANIPULATE_SETCONTEXT *PKDPACKETMANIPULATE_SETCONTEXT; +/** Pointer to a const set context manipulate payload. */ +typedef const KDPACKETMANIPULATE_SETCONTEXT *PCKDPACKETMANIPULATE_SETCONTEXT; + + +/** + * Query memory properties payload. + */ +typedef struct KDPACKETMANIPULATE_QUERYMEMORY +{ + /** The address to query the properties for. */ + uint64_t u64GCPtr; + /** Reserved. */ + uint64_t u64Rsvd; + /** Address space type on return. */ + uint32_t u32AddrSpace; + /** Protection flags. */ + uint32_t u32Flags; + /** Blows up the request to the required size. */ + uint8_t abPad[16]; +} KDPACKETMANIPULATE_QUERYMEMORY; +AssertCompileSize(KDPACKETMANIPULATE_QUERYMEMORY, 40); +/** Pointer to a query memory properties payload. */ +typedef KDPACKETMANIPULATE_QUERYMEMORY *PKDPACKETMANIPULATE_QUERYMEMORY; +/** Pointer to a const query memory properties payload. */ +typedef const KDPACKETMANIPULATE_QUERYMEMORY *PCKDPACKETMANIPULATE_QUERYMEMORY; + + +/** @name Query memory address space identifiers. + * @{ */ +/** Process memory space. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_SPACE_PROCESS UINT32_C(0) +/** Session memory space. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_SPACE_SESSION UINT32_C(1) +/** Kernel memory space. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_SPACE_KERNEL UINT32_C(2) +/** @} */ + + +/** @name Query memory address protection flags. + * @{ */ +/** Readable. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_READ RT_BIT_32(0) +/** Writable. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_WRITE RT_BIT_32(1) +/** Executable. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_EXEC RT_BIT_32(2) +/** Fixed address. */ +#define KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_FIXED RT_BIT_32(3) +/** @} */ + + +/** + * Search memory payload. + */ +typedef struct KDPACKETMANIPULATE_SEARCHMEMORY +{ + /** The address to start searching at on input, found address on output. */ + uint64_t u64GCPtr; + /** Number of bytes to search. */ + uint64_t cbSearch; + /** Length of the pattern to search for following the payload. */ + uint32_t cbPattern; + /** Padding to the required size. */ + uint32_t au32Pad[5]; +} KDPACKETMANIPULATE_SEARCHMEMORY; +AssertCompileSize(KDPACKETMANIPULATE_SEARCHMEMORY, 40); +/** Pointer to a search memory properties payload. */ +typedef KDPACKETMANIPULATE_SEARCHMEMORY *PKDPACKETMANIPULATE_SEARCHMEMORY; +/** Pointer to a const search memory properties payload. */ +typedef const KDPACKETMANIPULATE_SEARCHMEMORY *PCKDPACKETMANIPULATE_SEARCHMEMORY; + + +/** + * Manipulate request packet header (Same for 32bit and 64bit). + */ +typedef struct KDPACKETMANIPULATEHDR +{ + /** The request to execute. */ + uint32_t idReq; + /** The processor level to execute the request on. */ + uint16_t u16CpuLvl; + /** The processor ID to execute the request on. */ + uint16_t idCpu; + /** Return status code. */ + uint32_t u32NtStatus; + /** Alignment. */ + uint32_t u32Alignment; +} KDPACKETMANIPULATEHDR; +AssertCompileSize(KDPACKETMANIPULATEHDR, 3 * 4 + 2 * 2); +/** Pointer to a manipulate request packet header. */ +typedef KDPACKETMANIPULATEHDR *PKDPACKETMANIPULATEHDR; +/** Pointer to a const manipulate request packet header. */ +typedef const KDPACKETMANIPULATEHDR *PCPKDPACKETMANIPULATEHDR; + + +/** + * 64bit manipulate state request packet. + */ +typedef struct KDPACKETMANIPULATE64 +{ + /** Header. */ + KDPACKETMANIPULATEHDR Hdr; + /** Request payloads. */ + union + { + /** Get Version. */ + KDPACKETMANIPULATE_GETVERSION64 GetVersion; + /** Read/Write memory. */ + KDPACKETMANIPULATE_XFERMEM64 XferMem; + /** Continue. */ + KDPACKETMANIPULATE_CONTINUE Continue; + /** Continue2. */ + KDPACKETMANIPULATE_CONTINUE2 Continue2; + /** Set context. */ + KDPACKETMANIPULATE_SETCONTEXT SetContext; + /** Read/Write control space. */ + KDPACKETMANIPULATE_XFERCTRLSPACE64 XferCtrlSpace; + /** Restore breakpoint. */ + KDPACKETMANIPULATE_RESTOREBKPT64 RestoreBkpt; + /** Write breakpoint. */ + KDPACKETMANIPULATE_WRITEBKPT64 WriteBkpt; + /** Context extended. */ + KDPACKETMANIPULATE_CONTEXTEX ContextEx; + /** Query memory. */ + KDPACKETMANIPULATE_QUERYMEMORY QueryMemory; + /** Search memory. */ + KDPACKETMANIPULATE_SEARCHMEMORY SearchMemory; + } u; +} KDPACKETMANIPULATE64; +AssertCompileSize(KDPACKETMANIPULATE64, 16 + 40); +/** Pointer to a 64bit manipulate state request packet. */ +typedef KDPACKETMANIPULATE64 *PKDPACKETMANIPULATE64; +/** Pointer to a const 64bit manipulate state request packet. */ +typedef const KDPACKETMANIPULATE64 *PCKDPACKETMANIPULATE64; + +/** @name Manipulate requests. + * @{ */ +/** Minimum available request. */ +#define KD_PACKET_MANIPULATE_REQ_MIN UINT32_C(0x00003130) +/** Read virtual memory request. */ +#define KD_PACKET_MANIPULATE_REQ_READ_VIRT_MEM KD_PACKET_MANIPULATE_REQ_MIN +/** Write virtual memory request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_VIRT_MEM UINT32_C(0x00003131) +/** Get context request. */ +#define KD_PACKET_MANIPULATE_REQ_GET_CONTEXT UINT32_C(0x00003132) +/** Set context request. */ +#define KD_PACKET_MANIPULATE_REQ_SET_CONTEXT UINT32_C(0x00003133) +/** Write breakpoint request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_BKPT UINT32_C(0x00003134) +/** Restore breakpoint request. */ +#define KD_PACKET_MANIPULATE_REQ_RESTORE_BKPT UINT32_C(0x00003135) +/** Continue request. */ +#define KD_PACKET_MANIPULATE_REQ_CONTINUE UINT32_C(0x00003136) +/** Read control space request. */ +#define KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE UINT32_C(0x00003137) +/** Write control space request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_CTRL_SPACE UINT32_C(0x00003138) +/** Read I/O space request. */ +#define KD_PACKET_MANIPULATE_REQ_READ_IO_SPACE UINT32_C(0x00003139) +/** Write I/O space request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_IO_SPACE UINT32_C(0x0000313a) +/** Reboot request. */ +#define KD_PACKET_MANIPULATE_REQ_REBOOT UINT32_C(0x0000313b) +/** continue 2nd version request. */ +#define KD_PACKET_MANIPULATE_REQ_CONTINUE2 UINT32_C(0x0000313c) +/** Read physical memory request. */ +#define KD_PACKET_MANIPULATE_REQ_READ_PHYS_MEM UINT32_C(0x0000313d) +/** Write physical memory request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_PHYS_MEM UINT32_C(0x0000313e) +/** Query special calls request. */ +#define KD_PACKET_MANIPULATE_REQ_QUERY_SPEC_CALLS UINT32_C(0x0000313f) +/** Set special calls request. */ +#define KD_PACKET_MANIPULATE_REQ_SET_SPEC_CALLS UINT32_C(0x00003140) +/** Clear special calls request. */ +#define KD_PACKET_MANIPULATE_REQ_CLEAR_SPEC_CALLS UINT32_C(0x00003141) +/** Set internal breakpoint request. */ +#define KD_PACKET_MANIPULATE_REQ_SET_INTERNAL_BKPT UINT32_C(0x00003142) +/** Get internal breakpoint request. */ +#define KD_PACKET_MANIPULATE_REQ_GET_INTERNAL_BKPT UINT32_C(0x00003143) +/** Read I/O space extended request. */ +#define KD_PACKET_MANIPULATE_REQ_READ_IO_SPACE_EX UINT32_C(0x00003144) +/** Write I/O space extended request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_IO_SPACE_EX UINT32_C(0x00003145) +/** Get version request. */ +#define KD_PACKET_MANIPULATE_REQ_GET_VERSION UINT32_C(0x00003146) +/** Write breakpoint extended request. */ +#define KD_PACKET_MANIPULATE_REQ_WRITE_BKPT_EX UINT32_C(0x00003147) +/** Restore breakpoint extended request. */ +#define KD_PACKET_MANIPULATE_REQ_RESTORE_BKPT_EX UINT32_C(0x00003148) +/** Cause a bugcheck request. */ +#define KD_PACKET_MANIPULATE_REQ_CAUSE_BUGCHECK UINT32_C(0x00003149) +/** Cause a bugcheck request. */ +#define KD_PACKET_MANIPULATE_REQ_SWITCH_PROCESSOR UINT32_C(0x00003150) +/** @todo 0x3151-0x3155 */ +/** Search memory for a pattern request. */ +#define KD_PACKET_MANIPULATE_REQ_SEARCH_MEMORY UINT32_C(0x00003156) +/** @todo 0x3157-0x3159 */ +/** Clear all internal breakpoints request. */ +#define KD_PACKET_MANIPULATE_REQ_CLEAR_ALL_INTERNAL_BKPT UINT32_C(0x0000315a) +/** Fill memory. */ +#define KD_PACKET_MANIPULATE_REQ_FILL_MEMORY UINT32_C(0x0000315b) +/** Query memory properties. */ +#define KD_PACKET_MANIPULATE_REQ_QUERY_MEMORY UINT32_C(0x0000315c) +/** @todo 0x315d, 0x315e */ +/** Get context extended request. */ +#define KD_PACKET_MANIPULATE_REQ_GET_CONTEXT_EX UINT32_C(0x0000315f) +/** @todo 0x3160 */ +/** Maximum available request (exclusive). */ +#define KD_PACKET_MANIPULATE_REQ_MAX UINT32_C(0x00003161) +/** @} */ + +/** + * KD stub receive state. + */ +typedef enum KDRECVSTATE +{ + /** Invalid state. */ + KDRECVSTATE_INVALID = 0, + /** Receiving the first byte of the packet header. */ + KDRECVSTATE_PACKET_HDR_FIRST_BYTE, + /** Receiving the second byte of the packet header. */ + KDRECVSTATE_PACKET_HDR_SECOND_BYTE, + /** Receiving the header. */ + KDRECVSTATE_PACKET_HDR, + /** Receiving the packet body. */ + KDRECVSTATE_PACKET_BODY, + /** Receiving the trailing byte. */ + KDRECVSTATE_PACKET_TRAILER, + /** Blow up the enum to 32bits for easier alignment of members in structs. */ + KDRECVSTATE_32BIT_HACK = 0x7fffffff +} KDRECVSTATE; + + +/** + * KD emulated hardware breakpoint. + */ +typedef struct KDCTXHWBP +{ + /** The DBGF breakpoint handle if active, NIL_DBGFBP if not active. */ + DBGFBP hDbgfBp; + /** The linear address of the breakpoint if active. */ + RTGCPTR GCPtrBp; + /** Access type of the breakpoint, see X86_DR7_RW_*. */ + uint8_t fAcc; + /** Length flags of the breakpoint. */ + uint8_t fLen; + /** Flag whether it is a local breakpoint. */ + bool fLocal; + /** Flag whether it is a global breakpoint. */ + bool fGlobal; + /** Flag whether the breakpoint has triggered since the last time of the reset. */ + bool fTriggered; +} KDCTXHWBP; +/** Pointer to an emulated hardware breakpoint. */ +typedef KDCTXHWBP *PKDCTXHWBP; +/** Pointer to a const emulated hardware breakpoint. */ +typedef const KDCTXHWBP *PCKDCTXHWBP; + + +/** + * KD context data. + */ +typedef struct KDCTX +{ + /** Internal debugger console data. */ + DBGC Dbgc; + /** Number of bytes received left for the current state. */ + size_t cbRecvLeft; + /** Pointer where to write the next received data. */ + uint8_t *pbRecv; + /** The current state when receiving a new packet. */ + KDRECVSTATE enmState; + /** The timeout waiting for new data. */ + RTMSINTERVAL msRecvTimeout; + /** Timestamp when we last received data from the remote end. */ + uint64_t tsRecvLast; + /** Packet header being received. */ + union + { + KDPACKETHDR Fields; + uint8_t ab[16]; + } PktHdr; + /** The next packet ID to send. */ + uint32_t idPktNext; + /** Offset into the body receive buffer. */ + size_t offBodyRecv; + /** Body data. */ + uint8_t abBody[_4K]; + /** The trailer byte storage. */ + uint8_t bTrailer; + /** Flag whether a breakin packet was received since the last time it was reset. */ + bool fBreakinRecv; + /** Flag whether we entered the native VBox hypervisor through a bugcheck request. */ + bool fInVBoxDbg; + + /** Emulated hardware breakpoint handling. */ + KDCTXHWBP aHwBp[4]; + /** Flag whether a single step completed since last time this was cleared. */ + bool fSingleStepped; + + /** Pointer to the OS digger WinNt interface if a matching guest was detected. */ + PDBGFOSIWINNT pIfWinNt; + /** Flag whether the detected guest is 32bit (false if 64bit). */ + bool f32Bit; +} KDCTX; +/** Pointer to the KD context data. */ +typedef KDCTX *PKDCTX; +/** Pointer to const KD context data. */ +typedef const KDCTX *PCKDCTX; +/** Pointer to a KD context data pointer. */ +typedef PKDCTX *PPKDCTX; + + +/** + * Register mapping descriptor. + */ +typedef struct KDREGDESC +{ + /** The DBGF register enum. */ + DBGFREG enmReg; + /** Register width. */ + DBGFREGVALTYPE enmValType; + /** The offset into the context structure where the value ends up. */ + uintptr_t offReg; +} KDREGDESC; +/** Pointer to a register mapping structure. */ +typedef KDREGDESC *PKDREGDESC; +/** Pointer to a const register mapping structure. */ +typedef const KDREGDESC *PCKDREGDESC; + + +/** Creates a possibly sign extended guest context pointer which is required for 32bit targets. */ +#define KD_PTR_CREATE(a_pThis, a_GCPtr) ((a_pThis)->f32Bit && ((a_GCPtr) & RT_BIT_32(31)) ? (a_GCPtr) | UINT64_C(0xffffffff00000000) : (a_GCPtr)) +/** Returns the value of a possibly sign extended guest context pointer received for 32bit targets. */ +#define KD_PTR_GET(a_pThis, a_GCPtr) ((a_pThis)->f32Bit ? (a_GCPtr) & ~UINT64_C(0xffffffff00000000) : (a_GCPtr)) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ + +/** 64bit control register set. */ +static const KDREGDESC g_aRegsCtrl64[] = +{ + { DBGFREG_CS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, u16SegCs) }, + { DBGFREG_SS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, u16SegSs) }, + { DBGFREG_RIP, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRip) }, + { DBGFREG_RSP, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRsp) }, + { DBGFREG_RBP, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRbp) }, + { DBGFREG_EFLAGS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT64, u32RegEflags) } +}; + + +/** 64bit integer register set. */ +static const KDREGDESC g_aRegsInt64[] = +{ + { DBGFREG_RAX, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRax) }, + { DBGFREG_RCX, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRcx) }, + { DBGFREG_RDX, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRdx) }, + { DBGFREG_RBX, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRbx) }, + { DBGFREG_RSI, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRsi) }, + { DBGFREG_RDI, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegRdi) }, + { DBGFREG_R8, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR8) }, + { DBGFREG_R9, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR9) }, + { DBGFREG_R10, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR10) }, + { DBGFREG_R11, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR11) }, + { DBGFREG_R12, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR12) }, + { DBGFREG_R13, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR13) }, + { DBGFREG_R14, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR14) }, + { DBGFREG_R15, DBGFREGVALTYPE_U64, RT_UOFFSETOF(NTCONTEXT64, u64RegR15) } +}; + + +/** 64bit segments register set. */ +static const KDREGDESC g_aRegsSegs64[] = +{ + { DBGFREG_DS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, u16SegDs) }, + { DBGFREG_ES, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, u16SegEs) }, + { DBGFREG_FS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, u16SegFs) }, + { DBGFREG_GS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, u16SegGs) } +}; + + +/** 64bit floating point register set. */ +static const KDREGDESC g_aRegsFx64[] = +{ + { DBGFREG_FCW, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, FxSave.FCW) }, + { DBGFREG_FSW, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, FxSave.FSW) }, + { DBGFREG_FTW, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, FxSave.FTW) }, + { DBGFREG_FOP, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, FxSave.FOP) }, + { DBGFREG_FPUIP, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT64, FxSave.FPUIP) }, + /// @todo Fails on Solaris { DBGFREG_FPUCS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, FxSave.CS) }, + { DBGFREG_FPUDP, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT64, FxSave.FPUDP) }, + /// @todo Fails on Solaris { DBGFREG_FPUDS, DBGFREGVALTYPE_U16, RT_UOFFSETOF(NTCONTEXT64, FxSave.DS) }, + { DBGFREG_MXCSR, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT64, FxSave.MXCSR) }, + { DBGFREG_MXCSR_MASK, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT64, FxSave.MXCSR_MASK) }, + { DBGFREG_ST0, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[0]) }, + { DBGFREG_ST1, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[1]) }, + { DBGFREG_ST2, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[2]) }, + { DBGFREG_ST3, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[3]) }, + { DBGFREG_ST4, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[4]) }, + { DBGFREG_ST5, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[5]) }, + { DBGFREG_ST6, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[6]) }, + { DBGFREG_ST7, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT64, FxSave.aRegs[7]) }, + { DBGFREG_XMM0, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[0]) }, + { DBGFREG_XMM1, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[1]) }, + { DBGFREG_XMM2, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[2]) }, + { DBGFREG_XMM3, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[3]) }, + { DBGFREG_XMM4, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[4]) }, + { DBGFREG_XMM5, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[5]) }, + { DBGFREG_XMM6, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[6]) }, + { DBGFREG_XMM7, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[7]) }, + { DBGFREG_XMM8, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[8]) }, + { DBGFREG_XMM9, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[9]) }, + { DBGFREG_XMM10, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[10]) }, + { DBGFREG_XMM11, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[11]) }, + { DBGFREG_XMM12, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[12]) }, + { DBGFREG_XMM13, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[13]) }, + { DBGFREG_XMM14, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[14]) }, + { DBGFREG_XMM15, DBGFREGVALTYPE_U128, RT_UOFFSETOF(NTCONTEXT64, FxSave.aXMM[15]) } +}; + + +/** 32bit control register set. */ +static const KDREGDESC g_aRegsCtrl32[] = +{ + { DBGFREG_CS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32SegCs) }, + { DBGFREG_SS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32SegSs) }, + { DBGFREG_EIP, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEip) }, + { DBGFREG_ESP, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEsp) }, + { DBGFREG_EBP, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEbp) }, + { DBGFREG_EFLAGS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEflags) } +}; + + +/** 32bit integer register set. */ +static const KDREGDESC g_aRegsInt32[] = +{ + { DBGFREG_EAX, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEax) }, + { DBGFREG_ECX, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEcx) }, + { DBGFREG_EDX, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEdx) }, + { DBGFREG_EBX, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEbx) }, + { DBGFREG_ESI, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEsi) }, + { DBGFREG_EDI, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegEdi) } +}; + + +/** 32bit segments register set. */ +static const KDREGDESC g_aRegsSegs32[] = +{ + { DBGFREG_DS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32SegDs) }, + { DBGFREG_ES, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32SegEs) }, + { DBGFREG_FS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32SegFs) }, + { DBGFREG_GS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32SegGs) } +}; + + +/** 32bit debug register set. */ +static const KDREGDESC g_aRegsDbg32[] = +{ + { DBGFREG_DR0, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegDr0) }, + { DBGFREG_DR1, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegDr1) }, + { DBGFREG_DR2, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegDr2) }, + { DBGFREG_DR3, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegDr3) }, + { DBGFREG_DR6, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegDr6) }, + { DBGFREG_DR7, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, u32RegDr7) } +}; + + +/** 32bit floating point register set. */ +static const KDREGDESC g_aRegsFx32[] = +{ + { DBGFREG_FCW, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32CtrlWord) }, + { DBGFREG_FSW, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32StatusWord)}, + { DBGFREG_FTW, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32TagWord) }, + { DBGFREG_FCW, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32CtrlWord) }, + { DBGFREG_FPUIP, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32ErrorOff) }, + { DBGFREG_FPUCS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32ErrorSel) }, + { DBGFREG_FPUDS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32DataOff) }, + { DBGFREG_FPUDS, DBGFREGVALTYPE_U32, RT_UOFFSETOF(NTCONTEXT32, FloatSave.u32DataSel) }, + { DBGFREG_ST0, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[0]) }, + { DBGFREG_ST1, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[1]) }, + { DBGFREG_ST2, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[2]) }, + { DBGFREG_ST3, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[3]) }, + { DBGFREG_ST4, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[4]) }, + { DBGFREG_ST5, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[5]) }, + { DBGFREG_ST6, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[6]) }, + { DBGFREG_ST7, DBGFREGVALTYPE_R80, RT_UOFFSETOF(NTCONTEXT32, FloatSave.aFpuRegs[7]) } +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void dbgcKdCtxMsgSend(PKDCTX pThis, bool fWarning, const char *pszMsg); + + +#ifdef LOG_ENABLED +/** + * Returns a human readable string of the given packet sub type. + * + * @returns Pointer to sub type string. + * @param u16SubType The sub type to convert to a string. + */ +static const char *dbgcKdPktDumpSubTypeToStr(uint16_t u16SubType) +{ + switch (u16SubType) + { + case KD_PACKET_HDR_SUB_TYPE_STATE_CHANGE32: return "StateChange32"; + case KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE: return "Manipulate"; + case KD_PACKET_HDR_SUB_TYPE_DEBUG_IO: return "DebugIo"; + case KD_PACKET_HDR_SUB_TYPE_ACKNOWLEDGE: return "Ack"; + case KD_PACKET_HDR_SUB_TYPE_RESEND: return "Resend"; + case KD_PACKET_HDR_SUB_TYPE_RESET: return "Reset"; + case KD_PACKET_HDR_SUB_TYPE_STATE_CHANGE64: return "StateChange64"; + case KD_PACKET_HDR_SUB_TYPE_POLL_BREAKIN: return "PollBreakin"; + case KD_PACKET_HDR_SUB_TYPE_TRACE_IO: return "TraceIo"; + case KD_PACKET_HDR_SUB_TYPE_CONTROL_REQUEST: return "ControlRequest"; + case KD_PACKET_HDR_SUB_TYPE_FILE_IO: return "FileIo"; + default: break; + } + + return ""; +} + + +/** + * Returns a human readable string of the given manipulate request ID. + * + * @returns Human readable string (read only). + * @param idReq Request ID (API number in KD speak). + */ +static const char *dbgcKdPktDumpManipulateReqToStr(uint32_t idReq) +{ + switch (idReq) + { + case KD_PACKET_MANIPULATE_REQ_READ_VIRT_MEM: return "ReadVirtMem"; + case KD_PACKET_MANIPULATE_REQ_WRITE_VIRT_MEM: return "WriteVirtMem"; + case KD_PACKET_MANIPULATE_REQ_GET_CONTEXT: return "GetContext"; + case KD_PACKET_MANIPULATE_REQ_SET_CONTEXT: return "SetContext"; + case KD_PACKET_MANIPULATE_REQ_WRITE_BKPT: return "WriteBkpt"; + case KD_PACKET_MANIPULATE_REQ_RESTORE_BKPT: return "RestoreBkpt"; + case KD_PACKET_MANIPULATE_REQ_CONTINUE: return "Continue"; + case KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE: return "ReadCtrlSpace"; + case KD_PACKET_MANIPULATE_REQ_WRITE_CTRL_SPACE: return "WriteCtrlSpace"; + case KD_PACKET_MANIPULATE_REQ_READ_IO_SPACE: return "ReadIoSpace"; + case KD_PACKET_MANIPULATE_REQ_WRITE_IO_SPACE: return "WriteIoSpace"; + case KD_PACKET_MANIPULATE_REQ_REBOOT: return "Reboot"; + case KD_PACKET_MANIPULATE_REQ_CONTINUE2: return "Continue2"; + case KD_PACKET_MANIPULATE_REQ_READ_PHYS_MEM: return "ReadPhysMem"; + case KD_PACKET_MANIPULATE_REQ_WRITE_PHYS_MEM: return "WritePhysMem"; + case KD_PACKET_MANIPULATE_REQ_QUERY_SPEC_CALLS: return "QuerySpecCalls"; + case KD_PACKET_MANIPULATE_REQ_SET_SPEC_CALLS: return "SetSpecCalls"; + case KD_PACKET_MANIPULATE_REQ_CLEAR_SPEC_CALLS: return "ClrSpecCalls"; + case KD_PACKET_MANIPULATE_REQ_SET_INTERNAL_BKPT: return "SetIntBkpt"; + case KD_PACKET_MANIPULATE_REQ_GET_INTERNAL_BKPT: return "GetIntBkpt"; + case KD_PACKET_MANIPULATE_REQ_READ_IO_SPACE_EX: return "ReadIoSpaceEx"; + case KD_PACKET_MANIPULATE_REQ_WRITE_IO_SPACE_EX: return "WriteIoSpaceEx"; + case KD_PACKET_MANIPULATE_REQ_GET_VERSION: return "GetVersion"; + case KD_PACKET_MANIPULATE_REQ_CLEAR_ALL_INTERNAL_BKPT: return "ClrAllIntBkpt"; + case KD_PACKET_MANIPULATE_REQ_GET_CONTEXT_EX: return "GetContextEx"; + case KD_PACKET_MANIPULATE_REQ_QUERY_MEMORY: return "QueryMemory"; + case KD_PACKET_MANIPULATE_REQ_CAUSE_BUGCHECK: return "CauseBugCheck"; + case KD_PACKET_MANIPULATE_REQ_SWITCH_PROCESSOR: return "SwitchProcessor"; + case KD_PACKET_MANIPULATE_REQ_SEARCH_MEMORY: return "SearchMemory"; + default: break; + } + + return ""; +} + + +/** + * Dumps the content of a manipulate packet. + * + * @param pSgBuf S/G buffer containing the manipulate packet payload. + */ +static void dbgcKdPktDumpManipulate(PRTSGBUF pSgBuf) +{ + KDPACKETMANIPULATEHDR Hdr; + size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, &Hdr, sizeof(Hdr)); + + if (cbCopied == sizeof(Hdr)) + { + const char *pszReq = dbgcKdPktDumpManipulateReqToStr(Hdr.idReq); + + Log3((" MANIPULATE(%#x (%s), %#x, %u, %#x)\n", + Hdr.idReq, pszReq, Hdr.u16CpuLvl, Hdr.idCpu, Hdr.u32NtStatus)); + + switch (Hdr.idReq) + { + case KD_PACKET_MANIPULATE_REQ_READ_VIRT_MEM: + case KD_PACKET_MANIPULATE_REQ_WRITE_VIRT_MEM: + case KD_PACKET_MANIPULATE_REQ_READ_PHYS_MEM: + case KD_PACKET_MANIPULATE_REQ_WRITE_PHYS_MEM: + { + KDPACKETMANIPULATE_XFERMEM64 XferMem64; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &XferMem64, sizeof(XferMem64)); + if (cbCopied == sizeof(XferMem64)) + { + Log3((" u64PtrTarget: %RX64\n" + " cbXferReq: %RX32\n" + " cbXfered: %RX32\n", + XferMem64.u64PtrTarget, XferMem64.cbXferReq, XferMem64.cbXfered)); + } + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(XferMem64), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_RESTORE_BKPT: + { + KDPACKETMANIPULATE_RESTOREBKPT64 RestoreBkpt64; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &RestoreBkpt64, sizeof(RestoreBkpt64)); + if (cbCopied == sizeof(RestoreBkpt64)) + Log3((" u32HndBkpt: %RX32\n", RestoreBkpt64.u32HndBkpt)); + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(RestoreBkpt64), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_WRITE_BKPT: + { + KDPACKETMANIPULATE_WRITEBKPT64 WriteBkpt64; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &WriteBkpt64, sizeof(WriteBkpt64)); + if (cbCopied == sizeof(WriteBkpt64)) + Log3((" u64PtrBkpt: %RX64\n" + " u32HndBkpt: %RX32\n", + WriteBkpt64.u64PtrBkpt, WriteBkpt64.u32HndBkpt)); + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(WriteBkpt64), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_CONTINUE: + { + KDPACKETMANIPULATE_CONTINUE Continue; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &Continue, sizeof(Continue)); + if (cbCopied == sizeof(Continue)) + Log3((" u32NtContSts: %RX32\n", Continue.u32NtContSts)); + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(Continue), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_CONTINUE2: + { + KDPACKETMANIPULATE_CONTINUE2 Continue; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &Continue, sizeof(Continue)); + if (cbCopied == sizeof(Continue)) + Log3((" u32NtContSts: %RX32\n" + " fTrace: %RX32\n", + Continue.u32NtContSts, Continue.fTrace)); + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(Continue), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE: + case KD_PACKET_MANIPULATE_REQ_WRITE_CTRL_SPACE: + { + KDPACKETMANIPULATE_XFERCTRLSPACE64 XferCtrlSpace64; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &XferCtrlSpace64, sizeof(XferCtrlSpace64)); + if (cbCopied == sizeof(XferCtrlSpace64)) + { + Log3((" u64IdXfer: %RX64\n" + " cbXferReq: %RX32\n" + " cbXfered: %RX32\n", + XferCtrlSpace64.u64IdXfer, XferCtrlSpace64.cbXferReq, XferCtrlSpace64.cbXfered)); + } + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(XferCtrlSpace64), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_GET_CONTEXT_EX: + { + KDPACKETMANIPULATE_CONTEXTEX GetContextEx; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &GetContextEx, sizeof(GetContextEx)); + if (cbCopied == sizeof(GetContextEx)) + { + Log3((" offStart: %RX32\n" + " cbXferReq: %RX32\n" + " cbXfered: %RX32\n", + GetContextEx.offStart, GetContextEx.cbXfer, GetContextEx.cbXfered)); + } + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(GetContextEx), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_QUERY_MEMORY: + { + KDPACKETMANIPULATE_QUERYMEMORY QueryMemory; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &QueryMemory, sizeof(QueryMemory)); + if (cbCopied == sizeof(QueryMemory)) + { + Log3((" u64GCPtr: %RX64\n" + " u32AddrSpace: %RX32\n" + " u32Flags: %RX32\n", + QueryMemory.u64GCPtr, QueryMemory.u32AddrSpace, QueryMemory.u32Flags)); + } + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(QueryMemory), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_SEARCH_MEMORY: + { + KDPACKETMANIPULATE_SEARCHMEMORY SearchMemory; + cbCopied = RTSgBufCopyToBuf(pSgBuf, &SearchMemory, sizeof(SearchMemory)); + if (cbCopied == sizeof(SearchMemory)) + { + Log3((" u64GCPtr: %RX64\n" + " cbSearch: %RX64\n" + " cbPattern: %RX32\n", + SearchMemory.u64GCPtr, SearchMemory.cbSearch, SearchMemory.cbPattern)); + } + else + Log3((" Payload to small, expected %u, got %zu\n", sizeof(SearchMemory), cbCopied)); + break; + } + case KD_PACKET_MANIPULATE_REQ_SWITCH_PROCESSOR: + default: + break; + } + } + else + Log3((" MANIPULATE(Header too small, expected %u, got %zu)\n", sizeof(Hdr), cbCopied)); +} + + +/** + * Dumps the received packet to the debug log. + * + * @returns VBox status code. + * @param pPktHdr The packet header to dump. + * @param fRx Flag whether the packet was received (false indicates an outgoing packet). + */ +static void dbgcKdPktDump(PCKDPACKETHDR pPktHdr, PCRTSGSEG paSegs, uint32_t cSegs, bool fRx) +{ + RTSGBUF SgBuf; + + RTSgBufInit(&SgBuf, paSegs, cSegs); + + Log3(("%s KDPKTHDR(%#x, %#x (%s), %u, %#x, %#x)\n", + fRx ? "=>" : "<=", + pPktHdr->u32Signature, pPktHdr->u16SubType, dbgcKdPktDumpSubTypeToStr(pPktHdr->u16SubType), + pPktHdr->cbBody, pPktHdr->idPacket, pPktHdr->u32ChkSum)); + switch (pPktHdr->u16SubType) + { + case KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE: + dbgcKdPktDumpManipulate(&SgBuf); + break; + default: + break; + } +} +#endif + + +/** + * Resets the emulated hardware breakpoint state to a state similar after a reboot. + * + * @param pThis The KD context. + */ +static void dbgcKdCtxHwBpReset(PKDCTX pThis) +{ + pThis->fSingleStepped = false; + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aHwBp); i++) + { + PKDCTXHWBP pBp = &pThis->aHwBp[i]; + + if (pBp->hDbgfBp != NIL_DBGFBP) + { + int rc = DBGFR3BpClear(pThis->Dbgc.pUVM, pBp->hDbgfBp); + AssertRC(rc); + } + + pBp->hDbgfBp = NIL_DBGFBP; + pBp->GCPtrBp = 0; + pBp->fAcc = 0; + pBp->fLen = 0; + pBp->fLocal = false; + pBp->fGlobal = false; + pBp->fTriggered = false; + } +} + + +/** + * Updates the given breakpoint with the given properties. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pBp The breakpoint to update. + * @param fAcc Access mode. + * @param fLen Access length. + * @param fGlobal Global breakpoint. + * @param fLocal Local breakpoint. + * @param GCPtrBp Linear address of the breakpoint. + */ +static int dbgcKdCtxHwBpUpdate(PKDCTX pThis, PKDCTXHWBP pBp, uint8_t fAcc, uint8_t fLen, + bool fGlobal, bool fLocal, RTGCPTR GCPtrBp) +{ + int rc = VINF_SUCCESS; + + /* Did anything actually change?. */ + if ( pBp->fAcc != fAcc + || pBp->fLen != fLen + || pBp->fGlobal != fGlobal + || pBp->fLocal != fLocal + || pBp->GCPtrBp != GCPtrBp) + { + /* Clear the old breakpoint. */ + if (pBp->hDbgfBp != NIL_DBGFBP) + { + rc = DBGFR3BpClear(pThis->Dbgc.pUVM, pBp->hDbgfBp); + AssertRC(rc); + pBp->hDbgfBp = NIL_DBGFBP; + } + + pBp->fAcc = fAcc; + pBp->fLen = fLen; + pBp->fGlobal = fGlobal; + pBp->fLocal = fLocal; + pBp->GCPtrBp = GCPtrBp; + if (pBp->fGlobal || pBp->fLocal) + { + DBGFADDRESS AddrBp; + DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrBp, GCPtrBp); + + uint8_t cb = 0; + switch (pBp->fLen) + { + case X86_DR7_LEN_BYTE: + cb = 1; + break; + case X86_DR7_LEN_WORD: + cb = 2; + break; + case X86_DR7_LEN_DWORD: + cb = 4; + break; + case X86_DR7_LEN_QWORD: + cb = 8; + break; + default: + AssertFailed(); + return VERR_NET_PROTOCOL_ERROR; + } + + rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &AddrBp, 0 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/, + pBp->fAcc, cb, &pBp->hDbgfBp); + } + } + + return rc; +} + + +/** + * Updates emulated hardware breakpoints based on the written DR7 value. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param uDr7 The DR7 value which is written. + */ +static int dbgcKdCtxHwBpDr7Update(PKDCTX pThis, uint32_t uDr7) +{ + int rc = VINF_SUCCESS; + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aHwBp); i++) + { + PKDCTXHWBP pBp = &pThis->aHwBp[i]; + uint8_t fAcc = X86_DR7_GET_RW(uDr7, i); + uint8_t fLen = X86_DR7_GET_LEN(uDr7, i); + bool fGlobal = (uDr7 & RT_BIT_32(1 + i * 2)) ? true : false; + bool fLocal = (uDr7 & RT_BIT_32(i * 2)) ? true : false; + + int rc2 = dbgcKdCtxHwBpUpdate(pThis, pBp, fAcc, fLen, fGlobal, fLocal, pThis->aHwBp[i].GCPtrBp); + if ( RT_FAILURE(rc2) + && RT_SUCCESS(rc)) + rc = rc2; + } + + return rc; +} + + +/** + * Updates the linear guest pointer for the given hardware breakpoint. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pBp The breakpoint to update. + * @param GCPtrBp The linear breakpoint address. + */ +DECLINLINE(int) dbgcKdCtxHwBpGCPtrUpdate(PKDCTX pThis, PKDCTXHWBP pBp, RTGCPTR GCPtrBp) +{ + return dbgcKdCtxHwBpUpdate(pThis, pBp, pBp->fAcc, pBp->fLen, pBp->fGlobal, pBp->fLocal, GCPtrBp); +} + + +/** + * Calculates the DR7 value based on the emulated hardware breakpoint state and returns it. + * + * @returns The emulated DR7 value. + * @param pThis The KD context. + */ +static uint32_t dbgcKdCtxHwBpDr7Get(PKDCTX pThis) +{ + uint32_t uDr7 = 0; + + uDr7 |= X86_DR7_RW(0, pThis->aHwBp[0].fAcc); + uDr7 |= X86_DR7_RW(1, pThis->aHwBp[1].fAcc); + uDr7 |= X86_DR7_RW(2, pThis->aHwBp[2].fAcc); + uDr7 |= X86_DR7_RW(3, pThis->aHwBp[3].fAcc); + + uDr7 |= X86_DR7_LEN(0, pThis->aHwBp[0].fLen); + uDr7 |= X86_DR7_LEN(1, pThis->aHwBp[1].fLen); + uDr7 |= X86_DR7_LEN(2, pThis->aHwBp[2].fLen); + uDr7 |= X86_DR7_LEN(3, pThis->aHwBp[3].fLen); + + uDr7 |= pThis->aHwBp[0].fGlobal ? X86_DR7_G(0) : 0; + uDr7 |= pThis->aHwBp[1].fGlobal ? X86_DR7_G(1) : 0; + uDr7 |= pThis->aHwBp[2].fGlobal ? X86_DR7_G(2) : 0; + uDr7 |= pThis->aHwBp[3].fGlobal ? X86_DR7_G(3) : 0; + + uDr7 |= pThis->aHwBp[0].fLocal ? X86_DR7_L(0) : 0; + uDr7 |= pThis->aHwBp[1].fLocal ? X86_DR7_L(1) : 0; + uDr7 |= pThis->aHwBp[2].fLocal ? X86_DR7_L(2) : 0; + uDr7 |= pThis->aHwBp[3].fLocal ? X86_DR7_L(3) : 0; + + return uDr7; +} + + +/** + * Updates emulated hardware breakpoints based on the written DR6 value. + * + * @param pThis The KD context. + * @param uDr6 The DR7 value which is written. + */ +static void dbgcKdCtxHwBpDr6Update(PKDCTX pThis, uint32_t uDr6) +{ + pThis->aHwBp[0].fTriggered = (uDr6 & X86_DR6_B0) ? true : false; + pThis->aHwBp[1].fTriggered = (uDr6 & X86_DR6_B1) ? true : false; + pThis->aHwBp[2].fTriggered = (uDr6 & X86_DR6_B2) ? true : false; + pThis->aHwBp[3].fTriggered = (uDr6 & X86_DR6_B3) ? true : false; + pThis->fSingleStepped = (uDr6 & X86_DR6_BS) ? true : false; +} + + +/** + * Calculates the DR6 value based on the emulated hardware breakpoint state and returns it. + * + * @returns The emulated DR6 value. + * @param pThis The KD context. + */ +static uint32_t dbgcKdCtxHwBpDr6Get(PKDCTX pThis) +{ + uint32_t uDr6 = 0; + + if (pThis->aHwBp[0].fTriggered) + uDr6 |= X86_DR6_B0; + if (pThis->aHwBp[1].fTriggered) + uDr6 |= X86_DR6_B1; + if (pThis->aHwBp[2].fTriggered) + uDr6 |= X86_DR6_B2; + if (pThis->aHwBp[3].fTriggered) + uDr6 |= X86_DR6_B3; + if (pThis->fSingleStepped) + uDr6 |= X86_DR6_BS; + + return uDr6; +} + + +/** + * Wrapper for the I/O interface write callback. + * + * @returns Status code. + * @param pThis The KD context. + * @param pvPkt The packet data to send. + * @param cbPkt Size of the packet in bytes. + */ +DECLINLINE(int) dbgcKdCtxWrite(PKDCTX pThis, const void *pvPkt, size_t cbPkt) +{ + return pThis->Dbgc.pIo->pfnWrite(pThis->Dbgc.pIo, pvPkt, cbPkt, NULL /*pcbWritten*/); +} + + +/** + * Queries a given register set and stores it into the given context buffer. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param paRegs The register set to query. + * @param cRegs Number of entries in the register set. + * @param pvCtx The context buffer to store the data into. + */ +static int dbgcKdCtxQueryRegs(PKDCTX pThis, VMCPUID idCpu, PCKDREGDESC paRegs, uint32_t cRegs, void *pvCtx) +{ + int rc = VINF_SUCCESS; + + for (uint32_t i = 0; i < cRegs && rc == VINF_SUCCESS; i++) + { + void *pvStart = (uint8_t *)pvCtx + paRegs[i].offReg; + + switch (paRegs[i].enmValType) + { + case DBGFREGVALTYPE_U16: rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, paRegs[i].enmReg, (uint16_t *)pvStart); break; + case DBGFREGVALTYPE_U32: rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, paRegs[i].enmReg, (uint32_t *)pvStart); break; + case DBGFREGVALTYPE_U64: rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, paRegs[i].enmReg, (uint64_t *)pvStart); break; + //case DBGFREGVALTYPE_R80: rc = DBGFR3RegCpuQueryR80(pThis->Dbgc.pUVM, idCpu, paRegs[i].enmReg, (RTFLOAT80U *)pvStart); break; + //case DBGFREGVALTYPE_U128: rc = DBGFR3RegCpuQueryU128(pThis->Dbgc.pUVM, idCpu, paRegs[i].enmReg, (PRTUINT128U)pvStart); break; + default: AssertMsgFailedBreakStmt(("Register type %u not implemented\n", paRegs[i].enmValType), rc = VERR_NOT_IMPLEMENTED); + } + + if ( rc == VINF_DBGF_ZERO_EXTENDED_REGISTER + || ( rc == VINF_DBGF_TRUNCATED_REGISTER + && paRegs[i].enmReg == DBGFREG_RFLAGS)) /* KD protocol specifies 32bit but is really 64bit. */ + rc = VINF_SUCCESS; + } + + if ( RT_SUCCESS(rc) + && rc != VINF_SUCCESS) + rc = VERR_DBGF_UNSUPPORTED_CAST; + + return rc; +} + + +/** + * Fills in the given 64bit NT context structure with the requested values. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param pNtCtx The NT context structure to fill in. + * @param fCtxFlags Combination of NTCONTEXT_F_XXX determining what to fill in. + */ +static int dbgcKdCtxQueryNtCtx64(PKDCTX pThis, VMCPUID idCpu, PNTCONTEXT64 pNtCtx, uint32_t fCtxFlags) +{ + RT_BZERO(pNtCtx, sizeof(*pNtCtx)); + + pNtCtx->fContext = NTCONTEXT_F_AMD64; + int rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_MXCSR, &pNtCtx->u32RegMxCsr); + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_CONTROL) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsCtrl64[0], RT_ELEMENTS(g_aRegsCtrl64), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_CONTROL; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_INTEGER) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsInt64[0], RT_ELEMENTS(g_aRegsInt64), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_INTEGER; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_SEGMENTS) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsSegs64[0], RT_ELEMENTS(g_aRegsSegs64), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_SEGMENTS; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_FLOATING_POINT) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsFx64[0], RT_ELEMENTS(g_aRegsFx64), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_FLOATING_POINT; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_DEBUG) + { + /** @todo NTCONTEXT_F_DEBUG */ + } + + return rc; +} + + +/** + * Fills in the given 32bit NT context structure with the requested values. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param pNtCtx The NT context structure to fill in. + * @param fCtxFlags Combination of NTCONTEXT_F_XXX determining what to fill in. + */ +static int dbgcKdCtxQueryNtCtx32(PKDCTX pThis, VMCPUID idCpu, PNTCONTEXT32 pNtCtx, uint32_t fCtxFlags) +{ + RT_BZERO(pNtCtx, sizeof(*pNtCtx)); + + pNtCtx->fContext = NTCONTEXT_F_X86; + + int rc = VINF_SUCCESS; + if (fCtxFlags & NTCONTEXT_F_CONTROL) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsCtrl32[0], RT_ELEMENTS(g_aRegsCtrl32), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_CONTROL; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_INTEGER) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsInt32[0], RT_ELEMENTS(g_aRegsInt32), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_INTEGER; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_SEGMENTS) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsSegs32[0], RT_ELEMENTS(g_aRegsSegs32), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_SEGMENTS; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_FLOATING_POINT) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsFx32[0], RT_ELEMENTS(g_aRegsFx32), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_FLOATING_POINT; + } + + if ( RT_SUCCESS(rc) + && fCtxFlags & NTCONTEXT_F_DEBUG) + { + rc = dbgcKdCtxQueryRegs(pThis, idCpu, &g_aRegsDbg32[0], RT_ELEMENTS(g_aRegsDbg32), pNtCtx); + if (RT_SUCCESS(rc)) + pNtCtx->fContext |= NTCONTEXT_F_DEBUG; + } + + return rc; +} + + +#define KD_REG_INIT(a_pszName, a_enmType, a_ValMember, a_Val) \ + do \ + { \ + aRegsSet[idxReg].pszName = a_pszName; \ + aRegsSet[idxReg].enmType = a_enmType; \ + aRegsSet[idxReg].Val.a_ValMember = a_Val; \ + idxReg++; \ + } while (0) +#define KD_REG_INIT_DTR(a_pszName, a_Base, a_Limit) \ + do \ + { \ + aRegsSet[idxReg].pszName = a_pszName; \ + aRegsSet[idxReg].enmType = DBGFREGVALTYPE_DTR; \ + aRegsSet[idxReg].Val.dtr.u64Base = a_Base; \ + aRegsSet[idxReg].Val.dtr.u32Limit = a_Limit; \ + idxReg++; \ + } while (0) +#define KD_REG_INIT_U16(a_pszName, a_Val) KD_REG_INIT(a_pszName, DBGFREGVALTYPE_U16, u16, a_Val) +#define KD_REG_INIT_U32(a_pszName, a_Val) KD_REG_INIT(a_pszName, DBGFREGVALTYPE_U32, u32, a_Val) +#define KD_REG_INIT_U64(a_pszName, a_Val) KD_REG_INIT(a_pszName, DBGFREGVALTYPE_U64, u64, a_Val) + + +/** + * Writes the indicated values from the given context structure to the guests register set. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param pNtCtx The NT context structure to set. + * @param fCtxFlags Combination of NTCONTEXT_F_XXX determining what to set. + */ +static int dbgcKdCtxSetNtCtx64(PKDCTX pThis, VMCPUID idCpu, PCNTCONTEXT64 pNtCtx, uint32_t fCtxFlags) +{ + uint32_t idxReg = 0; + DBGFREGENTRYNM aRegsSet[64]; /** @todo Verify that this is enough when fully implemented. */ + + KD_REG_INIT_U32("mxcsr", pNtCtx->u32RegMxCsr); + + if (fCtxFlags & NTCONTEXT_F_CONTROL) + { +#if 0 /** @todo CPUM returns VERR_NOT_IMPLEMENTED */ + KD_REG_INIT_U16("cs", pNtCtx->u16SegCs); + KD_REG_INIT_U16("ss", pNtCtx->u16SegSs); +#endif + KD_REG_INIT_U64("rip", pNtCtx->u64RegRip); + KD_REG_INIT_U64("rsp", pNtCtx->u64RegRsp); + KD_REG_INIT_U64("rbp", pNtCtx->u64RegRbp); + KD_REG_INIT_U32("rflags", pNtCtx->u32RegEflags); + } + + if (fCtxFlags & NTCONTEXT_F_INTEGER) + { + KD_REG_INIT_U64("rax", pNtCtx->u64RegRax); + KD_REG_INIT_U64("rcx", pNtCtx->u64RegRcx); + KD_REG_INIT_U64("rdx", pNtCtx->u64RegRdx); + KD_REG_INIT_U64("rbx", pNtCtx->u64RegRbx); + KD_REG_INIT_U64("rsi", pNtCtx->u64RegRsi); + KD_REG_INIT_U64("rdi", pNtCtx->u64RegRdi); + KD_REG_INIT_U64("r8", pNtCtx->u64RegR8); + KD_REG_INIT_U64("r9", pNtCtx->u64RegR9); + KD_REG_INIT_U64("r10", pNtCtx->u64RegR10); + KD_REG_INIT_U64("r11", pNtCtx->u64RegR11); + KD_REG_INIT_U64("r12", pNtCtx->u64RegR12); + KD_REG_INIT_U64("r13", pNtCtx->u64RegR13); + KD_REG_INIT_U64("r14", pNtCtx->u64RegR14); + KD_REG_INIT_U64("r15", pNtCtx->u64RegR15); + } + + if (fCtxFlags & NTCONTEXT_F_SEGMENTS) + { +#if 0 /** @todo CPUM returns VERR_NOT_IMPLEMENTED */ + KD_REG_INIT_U16("ds", pNtCtx->u16SegDs); + KD_REG_INIT_U16("es", pNtCtx->u16SegEs); + KD_REG_INIT_U16("fs", pNtCtx->u16SegFs); + KD_REG_INIT_U16("gs", pNtCtx->u16SegGs); +#endif + } + + if (fCtxFlags & NTCONTEXT_F_FLOATING_POINT) + { + /** @todo NTCONTEXT_F_FLOATING_POINT. */ + } + + if (fCtxFlags & NTCONTEXT_F_DEBUG) + dbgcKdCtxMsgSend(pThis, true /*fWarning*/, "Setting local DR registers does not work!"); + + return DBGFR3RegNmSetBatch(pThis->Dbgc.pUVM, idCpu, &aRegsSet[0], idxReg); +} + + +/** + * Fills in the given 64bit NT kernel context structure with the requested values. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param pKNtCtx The NT context structure to fill in. + * @param fCtxFlags Combination of NTCONTEXT_F_XXX determining what to fill in. + */ +static int dbgcKdCtxQueryNtKCtx64(PKDCTX pThis, VMCPUID idCpu, PNTKCONTEXT64 pKNtCtx, uint32_t fCtxFlags) +{ + RT_BZERO(pKNtCtx, sizeof(*pKNtCtx)); + + int rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR0, &pKNtCtx->u64RegCr0); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR2, &pKNtCtx->u64RegCr2); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR3, &pKNtCtx->u64RegCr3); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR4, &pKNtCtx->u64RegCr4); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR8, &pKNtCtx->u64RegCr8); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_GDTR_LIMIT, &pKNtCtx->Gdtr.u16Limit); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_GDTR_BASE, &pKNtCtx->Gdtr.u64PtrBase); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_IDTR_LIMIT, &pKNtCtx->Idtr.u16Limit); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_IDTR_BASE, &pKNtCtx->Idtr.u64PtrBase); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_TR, &pKNtCtx->u16RegTr); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_LDTR, &pKNtCtx->u16RegLdtr); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_MXCSR, &pKNtCtx->u32RegMxCsr); + + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_MSR_K8_GS_BASE, &pKNtCtx->u64MsrGsBase); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_MSR_K8_KERNEL_GS_BASE, &pKNtCtx->u64MsrKernelGsBase); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_MSR_K6_STAR, &pKNtCtx->u64MsrStar); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_MSR_K8_LSTAR, &pKNtCtx->u64MsrLstar); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_MSR_K8_CSTAR, &pKNtCtx->u64MsrCstar); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, idCpu, DBGFREG_MSR_K8_SF_MASK, &pKNtCtx->u64MsrSfMask); + /** @todo XCR0 */ + + /* Get the emulated DR register state. */ + pKNtCtx->u64RegDr0 = pThis->aHwBp[0].GCPtrBp; + pKNtCtx->u64RegDr1 = pThis->aHwBp[1].GCPtrBp; + pKNtCtx->u64RegDr2 = pThis->aHwBp[2].GCPtrBp; + pKNtCtx->u64RegDr3 = pThis->aHwBp[3].GCPtrBp; + pKNtCtx->u64RegDr6 = dbgcKdCtxHwBpDr6Get(pThis); + pKNtCtx->u64RegDr7 = dbgcKdCtxHwBpDr7Get(pThis); + + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxQueryNtCtx64(pThis, idCpu, &pKNtCtx->Ctx, fCtxFlags); + + return rc; +} + + +/** + * Fills in the given 32bit NT kernel context structure with the requested values. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param pKNtCtx The NT context structure to fill in. + */ +static int dbgcKdCtxQueryNtKCtx32(PKDCTX pThis, VMCPUID idCpu, PNTKCONTEXT32 pKNtCtx) +{ + RT_BZERO(pKNtCtx, sizeof(*pKNtCtx)); + + int rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR0, &pKNtCtx->u32RegCr0); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR2, &pKNtCtx->u32RegCr2); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR3, &pKNtCtx->u32RegCr3); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_CR4, &pKNtCtx->u32RegCr4); + + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_GDTR_LIMIT, &pKNtCtx->Gdtr.u16Limit); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_GDTR_BASE, &pKNtCtx->Gdtr.u32PtrBase); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_IDTR_LIMIT, &pKNtCtx->Idtr.u16Limit); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, idCpu, DBGFREG_IDTR_BASE, &pKNtCtx->Idtr.u32PtrBase); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_TR, &pKNtCtx->u16RegTr); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, idCpu, DBGFREG_LDTR, &pKNtCtx->u16RegLdtr); + + /* Get the emulated DR register state. */ + pKNtCtx->u32RegDr0 = (uint32_t)pThis->aHwBp[0].GCPtrBp; + pKNtCtx->u32RegDr1 = (uint32_t)pThis->aHwBp[1].GCPtrBp; + pKNtCtx->u32RegDr2 = (uint32_t)pThis->aHwBp[2].GCPtrBp; + pKNtCtx->u32RegDr3 = (uint32_t)pThis->aHwBp[3].GCPtrBp; + pKNtCtx->u32RegDr6 = dbgcKdCtxHwBpDr6Get(pThis); + pKNtCtx->u32RegDr7 = dbgcKdCtxHwBpDr7Get(pThis); + + return rc; +} + + +/** + * Fills in the given 64bit NT kernel context structure with the requested values. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param idCpu The CPU to query the context for. + * @param pKNtCtx The NT context structure to fill in. + * @param cbSet How many bytes of the context are valid. + */ +static int dbgcKdCtxSetNtKCtx64(PKDCTX pThis, VMCPUID idCpu, PCNTKCONTEXT64 pKNtCtx, size_t cbSet) +{ + AssertReturn(cbSet >= RT_UOFFSETOF(NTKCONTEXT64, Ctx), VERR_INVALID_PARAMETER); + + uint32_t idxReg = 0; + DBGFREGENTRYNM aRegsSet[64]; /** @todo Verify that this is enough when fully implemented. */ + + KD_REG_INIT_U64("cr0", pKNtCtx->u64RegCr0); + KD_REG_INIT_U64("cr2", pKNtCtx->u64RegCr2); + KD_REG_INIT_U64("cr3", pKNtCtx->u64RegCr3); + KD_REG_INIT_U64("cr4", pKNtCtx->u64RegCr4); + KD_REG_INIT_U64("cr8", pKNtCtx->u64RegCr8); + + KD_REG_INIT_DTR("gdtr", pKNtCtx->Gdtr.u64PtrBase, pKNtCtx->Gdtr.u16Limit); + KD_REG_INIT_DTR("idtr", pKNtCtx->Idtr.u64PtrBase, pKNtCtx->Idtr.u16Limit); + +#if 0 /** @todo CPUM returns VERR_NOT_IMPLEMENTED */ + KD_REG_INIT_U16("tr", pKNtCtx->u16RegTr); + KD_REG_INIT_U16("ldtr", pKNtCtx->u16RegLdtr); +#endif + KD_REG_INIT_U32("mxcsr", pKNtCtx->u32RegMxCsr); + + KD_REG_INIT_U64("msr_gs_base", pKNtCtx->u64MsrGsBase); + KD_REG_INIT_U64("krnl_gs_base", pKNtCtx->u64MsrKernelGsBase); + KD_REG_INIT_U64("star", pKNtCtx->u64MsrStar); + KD_REG_INIT_U64("lstar", pKNtCtx->u64MsrLstar); + KD_REG_INIT_U64("cstar", pKNtCtx->u64MsrCstar); + KD_REG_INIT_U64("sf_mask", pKNtCtx->u64MsrSfMask); + + int rc = DBGFR3RegNmSetBatch(pThis->Dbgc.pUVM, idCpu, &aRegsSet[0], idxReg); + if ( RT_SUCCESS(rc) + && cbSet > RT_UOFFSETOF(NTKCONTEXT64, Ctx)) /** @todo Probably wrong. */ + rc = dbgcKdCtxSetNtCtx64(pThis, idCpu, &pKNtCtx->Ctx, pKNtCtx->Ctx.fContext); + + if (RT_SUCCESS(rc)) + { + /* Update emulated hardware breakpoint state. */ + dbgcKdCtxHwBpDr6Update(pThis, (uint32_t)pKNtCtx->u64RegDr6); + rc = dbgcKdCtxHwBpDr7Update(pThis, (uint32_t)pKNtCtx->u64RegDr7); + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxHwBpGCPtrUpdate(pThis, &pThis->aHwBp[0], pKNtCtx->u64RegDr0); + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxHwBpGCPtrUpdate(pThis, &pThis->aHwBp[1], pKNtCtx->u64RegDr1); + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxHwBpGCPtrUpdate(pThis, &pThis->aHwBp[2], pKNtCtx->u64RegDr2); + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxHwBpGCPtrUpdate(pThis, &pThis->aHwBp[3], pKNtCtx->u64RegDr3); + } + + return rc; +} + +#undef KD_REG_INIT_64 +#undef KD_REG_INIT_32 +#undef KD_REG_INIT_16 +#undef KD_REG_INIT_DTR +#undef KD_REG_INIT + + +/** + * Validates the given KD packet header. + * + * @returns Flag whether the packet header is valid, false if invalid. + * @param pPktHdr The packet header to validate. + */ +static bool dbgcKdPktHdrValidate(PCKDPACKETHDR pPktHdr) +{ + if ( pPktHdr->u32Signature != KD_PACKET_HDR_SIGNATURE_DATA + && pPktHdr->u32Signature != KD_PACKET_HDR_SIGNATURE_CONTROL + && pPktHdr->u32Signature != KD_PACKET_HDR_SIGNATURE_BREAKIN) + return false; + + if (pPktHdr->u16SubType >= KD_PACKET_HDR_SUB_TYPE_MAX) + return false; + + uint32_t idPacket = pPktHdr->idPacket & UINT32_C(0xfffffffe); + if ( idPacket != KD_PACKET_HDR_ID_INITIAL + && idPacket != KD_PACKET_HDR_ID_RESET + && idPacket != 0 /* Happens on the very first packet */) + return false; + + return true; +} + + +/** + * Generates a checksum from the given buffer. + * + * @returns Generated checksum. + * @param pv The data to generate a checksum from. + * @param cb Number of bytes to checksum. + */ +static uint32_t dbgcKdPktChkSumGen(const void *pv, size_t cb) +{ + const uint8_t *pb = (const uint8_t *)pv; + uint32_t u32ChkSum = 0; + + while (cb--) + u32ChkSum += *pb++; + + return u32ChkSum; +} + + +/** + * Generates a checksum from the given segments. + * + * @returns Generated checksum. + * @param paSegs Pointer to the array of segments containing the data. + * @param cSegs Number of segments. + * @param pcbChkSum Where to store the number of bytes checksummed, optional. + */ +static uint32_t dbgcKdPktChkSumGenSg(PCRTSGSEG paSegs, uint32_t cSegs, size_t *pcbChkSum) +{ + size_t cbChkSum = 0; + uint32_t u32ChkSum = 0; + + for (uint32_t i = 0; i < cSegs; i++) + { + u32ChkSum += dbgcKdPktChkSumGen(paSegs[i].pvSeg, paSegs[i].cbSeg); + cbChkSum += paSegs[i].cbSeg; + } + + if (pcbChkSum) + *pcbChkSum = cbChkSum; + + return u32ChkSum; +} + + +/** + * Waits for an acknowledgment. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param msWait Maximum number of milliseconds to wait for an acknowledge. + * @param pfResend Where to store the resend requested flag on success. + */ +static int dbgcKdCtxPktWaitForAck(PKDCTX pThis, RTMSINTERVAL msWait, bool *pfResend) +{ + KDPACKETHDR PktAck; + uint8_t *pbCur = (uint8_t *)&PktAck; + size_t cbLeft = sizeof(PktAck); + uint64_t tsStartMs = RTTimeMilliTS(); + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p msWait=%u pfResend=%p\n", pThis, msWait, pfResend)); + + RT_ZERO(PktAck); + + /* There might be breakin packets in the queue, read until we get something else. */ + while ( msWait + && RT_SUCCESS(rc)) + { + if (pThis->Dbgc.pIo->pfnInput(pThis->Dbgc.pIo, msWait)) + { + size_t cbRead = 0; + rc = pThis->Dbgc.pIo->pfnRead(pThis->Dbgc.pIo, pbCur, 1, &cbRead); + if ( RT_SUCCESS(rc) + && cbRead == 1) + { + uint64_t tsSpanMs = RTTimeMilliTS() - tsStartMs; + msWait -= RT_MIN(msWait, tsSpanMs); + tsStartMs = RTTimeMilliTS(); + + if (*pbCur == KD_PACKET_HDR_SIGNATURE_BREAKIN_BYTE) + pThis->fBreakinRecv = true; + else + { + pbCur++; + cbLeft--; + break; + } + } + } + else + rc = VERR_TIMEOUT; + } + + if ( RT_SUCCESS(rc) + && !msWait) + rc = VERR_TIMEOUT; + + if (RT_SUCCESS(rc)) + { + while ( msWait + && RT_SUCCESS(rc) + && cbLeft) + { + if (pThis->Dbgc.pIo->pfnInput(pThis->Dbgc.pIo, msWait)) + { + size_t cbRead = 0; + rc = pThis->Dbgc.pIo->pfnRead(pThis->Dbgc.pIo, pbCur, cbLeft, &cbRead); + if (RT_SUCCESS(rc)) + { + uint64_t tsSpanMs = RTTimeMilliTS() - tsStartMs; + msWait -= RT_MIN(msWait, tsSpanMs); + tsStartMs = RTTimeMilliTS(); + + cbLeft -= cbRead; + pbCur += cbRead; + } + } + else + rc = VERR_TIMEOUT; + } + + if (RT_SUCCESS(rc)) + { + if (PktAck.u32Signature == KD_PACKET_HDR_SIGNATURE_CONTROL) + { + if (PktAck.u16SubType == KD_PACKET_HDR_SUB_TYPE_ACKNOWLEDGE) + rc = VINF_SUCCESS; + else if (PktAck.u16SubType == KD_PACKET_HDR_SUB_TYPE_RESEND) + { + *pfResend = true; + rc = VINF_SUCCESS; + } + else + rc = VERR_NET_PROTOCOL_ERROR; + } + else + rc = VERR_NET_PROTOCOL_ERROR; + } + } + + LogFlowFunc(("returns rc=%Rrc *pfResend=%RTbool\n", rc, *pfResend)); + return rc; +} + + +/** + * Sends the given packet header and optional segmented body (the trailing byte is sent automatically). + * + * @returns VBox status code. + * @param pThis The KD context. + * @param u32Signature The signature to send. + * @param u16SubType The sub type to send. + * @param paSegs Pointer to the array of segments to send in the body, optional. + * @param cSegs Number of segments. + * @param fAck Flag whether to wait for an acknowledge. + */ +static int dbgcKdCtxPktSendSg(PKDCTX pThis, uint32_t u32Signature, uint16_t u16SubType, + PCRTSGSEG paSegs, uint32_t cSegs, bool fAck) +{ + int rc = VINF_SUCCESS; + uint32_t cRetriesLeft = 3; + uint8_t bTrailer = KD_PACKET_TRAILING_BYTE; + KDPACKETHDR Hdr; + + size_t cbChkSum = 0; + uint32_t u32ChkSum = dbgcKdPktChkSumGenSg(paSegs, cSegs, &cbChkSum); + + Hdr.u32Signature = u32Signature; + Hdr.u16SubType = u16SubType; + Hdr.cbBody = (uint16_t)cbChkSum; + Hdr.idPacket = pThis->idPktNext; + Hdr.u32ChkSum = u32ChkSum; + +#ifdef LOG_ENABLED + dbgcKdPktDump(&Hdr, paSegs, cSegs, false /*fRx*/); +#endif + + while (cRetriesLeft--) + { + bool fResend = false; + + if (pThis->Dbgc.pIo->pfnPktBegin) + { + rc = pThis->Dbgc.pIo->pfnPktBegin(pThis->Dbgc.pIo, 0 /*cbPktHint*/); + AssertRC(rc); + } + + rc = dbgcKdCtxWrite(pThis, &Hdr, sizeof(Hdr)); + if ( RT_SUCCESS(rc) + && paSegs + && cSegs) + { + for (uint32_t i = 0; i < cSegs && RT_SUCCESS(rc); i++) + rc = dbgcKdCtxWrite(pThis, paSegs[i].pvSeg, paSegs[i].cbSeg); + + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxWrite(pThis, &bTrailer, sizeof(bTrailer)); + } + + if ( RT_SUCCESS(rc) + && pThis->Dbgc.pIo->pfnPktEnd) + rc = pThis->Dbgc.pIo->pfnPktEnd(pThis->Dbgc.pIo); + + if (RT_SUCCESS(rc)) + { + if (fAck) + rc = dbgcKdCtxPktWaitForAck(pThis, 10 * 1000, &fResend); + + if ( RT_SUCCESS(rc) + && !fResend) + break; + } + } + + return rc; +} + + +/** + * Sends the given packet header and optional body (the trailing byte is sent automatically). + * + * @returns VBox status code. + * @param pThis The KD context. + * @param u32Signature The signature to send. + * @param u16SubType The sub type to send. + * @param pvBody The body to send, optional. + * @param cbBody Body size in bytes. + * @param fAck Flag whether to wait for an acknowledge. + */ +DECLINLINE(int) dbgcKdCtxPktSend(PKDCTX pThis, uint32_t u32Signature, uint16_t u16SubType, + const void *pvBody, size_t cbBody, + bool fAck) +{ + RTSGSEG Seg; + + Seg.pvSeg = (void *)pvBody; + Seg.cbSeg = cbBody; + return dbgcKdCtxPktSendSg(pThis, u32Signature, u16SubType, cbBody ? &Seg : NULL, cbBody ? 1 : 0, fAck); +} + + +/** + * Sends a resend packet answer. + * + * @returns VBox status code. + * @param pThis The KD context. + */ +DECLINLINE(int) dbgcKdCtxPktSendResend(PKDCTX pThis) +{ + return dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_CONTROL, KD_PACKET_HDR_SUB_TYPE_RESEND, + NULL /*pvBody*/, 0 /*cbBody*/, false /*fAck*/); +} + + +/** + * Sends a resend packet answer. + * + * @returns VBox status code. + * @param pThis The KD context. + */ +DECLINLINE(int) dbgcKdCtxPktSendReset(PKDCTX pThis) +{ + pThis->idPktNext = KD_PACKET_HDR_ID_INITIAL; + return dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_CONTROL, KD_PACKET_HDR_SUB_TYPE_RESET, + NULL /*pvBody*/, 0 /*cbBody*/, false /*fAck*/); +} + + +/** + * Sends an acknowledge packet answer. + * + * @returns VBox status code. + * @param pThis The KD context. + */ +DECLINLINE(int) dbgcKdCtxPktSendAck(PKDCTX pThis) +{ + return dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_CONTROL, KD_PACKET_HDR_SUB_TYPE_ACKNOWLEDGE, + NULL /*pvBody*/, 0 /*cbBody*/, false /*fAck*/); +} + + +/** + * Resets the packet receive state machine. + * + * @param pThis The KD context. + */ +static void dbgcKdCtxPktRecvReset(PKDCTX pThis) +{ + pThis->enmState = KDRECVSTATE_PACKET_HDR_FIRST_BYTE; + pThis->pbRecv = &pThis->PktHdr.ab[0]; + pThis->cbRecvLeft = sizeof(pThis->PktHdr.ab[0]); + pThis->msRecvTimeout = RT_INDEFINITE_WAIT; + pThis->tsRecvLast = RTTimeMilliTS(); +} + + +/** + * Sends a Debug I/O string packet. + * + * @returns VBox status code. + * @param pThis The KD context data. + * @param idCpu The CPU ID generating this packet. + * @param pachChars The characters to send (ASCII). + * @param cbChars Number of characters to send. + */ +static int dbgcKdCtxDebugIoStrSend(PKDCTX pThis, VMCPUID idCpu, const char *pachChars, size_t cbChars) +{ + KDPACKETDEBUGIO DebugIo; + RT_ZERO(DebugIo); + + /* Fix your damn log strings if this exceeds 4GB... */ + if (cbChars != (uint32_t)cbChars) + return VERR_BUFFER_OVERFLOW; + + DebugIo.u32Type = KD_PACKET_DEBUG_IO_STRING; + DebugIo.u16CpuLvl = 0x6; + DebugIo.idCpu = (uint16_t)idCpu; + DebugIo.u.Str.cbStr = (uint32_t)cbChars; + + RTSGSEG aRespSegs[2]; + + aRespSegs[0].pvSeg = &DebugIo; + aRespSegs[0].cbSeg = sizeof(DebugIo); + aRespSegs[1].pvSeg = (void *)pachChars; + aRespSegs[1].cbSeg = cbChars; + + int rc = dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_DEBUG_IO, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); + if (RT_SUCCESS(rc)) + pThis->idPktNext ^= 0x1; + + return rc; +} + + +/** + * Sends a message to the remotes end. + * + * @param pThis The KD context data. + * @param fWarning Flag whether this is a warning or an informational message. + * @param pszMsg The message to send. + */ +static void dbgcKdCtxMsgSend(PKDCTX pThis, bool fWarning, const char *pszMsg) +{ + size_t cchMsg = strlen(pszMsg); + + KDPACKETDEBUGIO DebugIo; + RT_ZERO(DebugIo); + + DebugIo.u32Type = KD_PACKET_DEBUG_IO_STRING; + DebugIo.u16CpuLvl = 0x6; + DebugIo.idCpu = 0; + + RTSGSEG aRespSegs[5]; + + aRespSegs[0].pvSeg = &DebugIo; + aRespSegs[0].cbSeg = sizeof(DebugIo); + aRespSegs[1].pvSeg = (void *)"VBoxDbg "; + aRespSegs[1].cbSeg = sizeof("VBoxDbg ") - 1; + if (fWarning) + { + aRespSegs[2].pvSeg = (void *)"WARNING "; + aRespSegs[2].cbSeg = sizeof("WARNING ") - 1; + } + else + { + aRespSegs[2].pvSeg = (void *)"INFO "; + aRespSegs[2].cbSeg = sizeof("INFO ") - 1; + } + aRespSegs[3].pvSeg = (void *)pszMsg; + aRespSegs[3].cbSeg = cchMsg; + aRespSegs[4].pvSeg = (void *)"\r\n"; + aRespSegs[4].cbSeg = 2; + + DebugIo.u.Str.cbStr = (uint32_t)( aRespSegs[1].cbSeg + + aRespSegs[2].cbSeg + + aRespSegs[3].cbSeg + + aRespSegs[4].cbSeg); + + int rc = dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_DEBUG_IO, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); + if (RT_SUCCESS(rc)) + pThis->idPktNext ^= 0x1; +} + + +/** + * Queries some user input from the remotes end. + * + * @returns VBox status code. + * @param pThis The KD context data. + * @param idCpu The CPU ID generating this packet. + * @param pachPrompt The prompt to send (ASCII). + * @param cbPrompt Number of characters to send for the prompt. + * @param cbResponseMax Maximum size for the response. + */ +static int dbgcKdCtxDebugIoGetStrSend(PKDCTX pThis, VMCPUID idCpu, const char *pachPrompt, size_t cbPrompt, + size_t cbResponseMax) +{ + KDPACKETDEBUGIO DebugIo; + RT_ZERO(DebugIo); + + /* Fix your damn log strings if this exceeds 4GB... */ + if ( cbPrompt != (uint32_t)cbPrompt + || cbResponseMax != (uint32_t)cbResponseMax) + return VERR_BUFFER_OVERFLOW; + + DebugIo.u32Type = KD_PACKET_DEBUG_IO_GET_STRING; + DebugIo.u16CpuLvl = 0x6; + DebugIo.idCpu = (uint16_t)idCpu; + DebugIo.u.Prompt.cbPrompt = (uint32_t)cbPrompt; + DebugIo.u.Prompt.cbReturn = (uint32_t)cbResponseMax; + + RTSGSEG aRespSegs[2]; + + aRespSegs[0].pvSeg = &DebugIo; + aRespSegs[0].cbSeg = sizeof(DebugIo); + aRespSegs[1].pvSeg = (void *)pachPrompt; + aRespSegs[1].cbSeg = cbPrompt; + + int rc = dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_DEBUG_IO, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); + if (RT_SUCCESS(rc)) + pThis->idPktNext ^= 0x1; + + return rc; +} + + +/** + * Sends a state change event packet. + * + * @returns VBox status code. + * @param pThis The KD context data. + * @param enmType The event type. + */ +static int dbgcKdCtxStateChangeSend(PKDCTX pThis, DBGFEVENTTYPE enmType) +{ + LogFlowFunc(("pThis=%p enmType=%u\n", pThis, enmType)); + + /* Select the record to send based on the CPU mode. */ + int rc = VINF_SUCCESS; + KDPACKETSTATECHANGE64 StateChange64; + RT_ZERO(StateChange64); + + StateChange64.u32StateNew = KD_PACKET_STATE_CHANGE_EXCEPTION; + StateChange64.u16CpuLvl = 0x6; /** @todo Figure this one out. */ + StateChange64.idCpu = pThis->Dbgc.idCpu; + StateChange64.cCpus = (uint16_t)DBGFR3CpuGetCount(pThis->Dbgc.pUVM); + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_RIP, &StateChange64.u64RipThread); + if (RT_SUCCESS(rc)) + { + DBGFADDRESS AddrRip; + DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrRip, StateChange64.u64RipThread); + + StateChange64.u64RipThread = KD_PTR_CREATE(pThis, StateChange64.u64RipThread); + + /** @todo Properly fill in the exception record. */ + switch (enmType) + { + case DBGFEVENT_HALT_DONE: + case DBGFEVENT_BREAKPOINT: + case DBGFEVENT_BREAKPOINT_IO: + case DBGFEVENT_BREAKPOINT_MMIO: + case DBGFEVENT_BREAKPOINT_HYPER: + StateChange64.u.Exception.ExcpRec.u32ExcpCode = KD_PACKET_EXCP_CODE_BKPT; + break; + case DBGFEVENT_STEPPED: + case DBGFEVENT_STEPPED_HYPER: + pThis->fSingleStepped = true; /* For emulation of DR6. */ + StateChange64.u.Exception.ExcpRec.u32ExcpCode = KD_PACKET_EXCP_CODE_SINGLE_STEP; + break; + default: + AssertMsgFailed(("Invalid DBGF event type for state change %d!\n", enmType)); + } + + StateChange64.u.Exception.ExcpRec.cExcpParms = 3; + StateChange64.u.Exception.u32FirstChance = 0x1; + + /** @todo Properly fill in the control report. */ + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_DR6, &StateChange64.uCtrlReport.Amd64.u64RegDr6); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_DR7, &StateChange64.uCtrlReport.Amd64.u64RegDr7); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_RFLAGS, &StateChange64.uCtrlReport.Amd64.u32RegEflags); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_CS, &StateChange64.uCtrlReport.Amd64.u16SegCs); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_DS, &StateChange64.uCtrlReport.Amd64.u16SegDs); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_ES, &StateChange64.uCtrlReport.Amd64.u16SegEs); + if (RT_SUCCESS(rc)) + rc = DBGFR3RegCpuQueryU16(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGFREG_FS, &StateChange64.uCtrlReport.Amd64.u16SegFs); + + /* Read instruction bytes. */ + StateChange64.uCtrlReport.Amd64.cbInsnStream = sizeof(StateChange64.uCtrlReport.Amd64.abInsn); + rc = DBGFR3MemRead(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrRip, + &StateChange64.uCtrlReport.Amd64.abInsn[0], StateChange64.uCtrlReport.Amd64.cbInsnStream); + if (RT_SUCCESS(rc)) + { + pThis->idPktNext = KD_PACKET_HDR_ID_INITIAL; + rc = dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_CHANGE64, + &StateChange64, sizeof(StateChange64), false /*fAck*/); + } + } + + LogFlowFunc(("returns %Rrc\n", rc)); + return rc; +} + + +/** + * Processes a get version 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64GetVersion(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATE64 Resp; + RT_ZERO(Resp); + + /* Fill in the generic part. */ + Resp.Hdr.idReq = KD_PACKET_MANIPULATE_REQ_GET_VERSION; + Resp.Hdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + Resp.Hdr.idCpu = pPktManip->Hdr.idCpu; + Resp.Hdr.u32NtStatus = NTSTATUS_SUCCESS; + + /* Build our own response in case there is no Windows interface available. */ + uint32_t NtBuildNumber = 0x0f2800; /* Used when there is no NT interface available, which probably breaks symbol loading. */ + bool f32Bit = false; + if (pThis->pIfWinNt) + { + int rc = pThis->pIfWinNt->pfnQueryVersion(pThis->pIfWinNt, pThis->Dbgc.pUVM, VMMR3GetVTable(), + NULL /*puVersMajor*/, NULL /*puVersMinor*/, + &NtBuildNumber, &f32Bit); + if (RT_SUCCESS(rc)) + rc = pThis->pIfWinNt->pfnQueryKernelPtrs(pThis->pIfWinNt, pThis->Dbgc.pUVM, VMMR3GetVTable(), + &Resp.u.GetVersion.u64PtrKernBase, + &Resp.u.GetVersion.u64PtrPsLoadedModuleList); + } + + /* Fill in the request specific part. */ + Resp.u.GetVersion.u16VersMaj = NtBuildNumber >> 16; + Resp.u.GetVersion.u16VersMin = NtBuildNumber & UINT32_C(0xffff); + Resp.u.GetVersion.u8VersProtocol = 0x6; /* From a Windows 10 guest. */ + Resp.u.GetVersion.u8VersKdSecondary = pThis->f32Bit ? 0 : 0x2; /* amd64 has a versioned context (0 and 1 are obsolete). */ + Resp.u.GetVersion.fFlags = KD_PACKET_MANIPULATE64_GET_VERSION_F_MP; + Resp.u.GetVersion.u8MaxPktType = KD_PACKET_HDR_SUB_TYPE_MAX; + Resp.u.GetVersion.u8MaxStateChange = KD_PACKET_STATE_CHANGE_MAX - KD_PACKET_STATE_CHANGE_MIN; + Resp.u.GetVersion.u8MaxManipulate = KD_PACKET_MANIPULATE_REQ_MAX - KD_PACKET_MANIPULATE_REQ_MIN; + Resp.u.GetVersion.u64PtrDebuggerDataList = 0; + + if (f32Bit) + { + Resp.u.GetVersion.u16MachineType = IMAGE_FILE_MACHINE_I386; + Resp.u.GetVersion.u64PtrKernBase = KD_PTR_CREATE(pThis, Resp.u.GetVersion.u64PtrKernBase); + Resp.u.GetVersion.u64PtrPsLoadedModuleList = KD_PTR_CREATE(pThis, Resp.u.GetVersion.u64PtrPsLoadedModuleList); + } + else + { + Resp.u.GetVersion.u16MachineType = IMAGE_FILE_MACHINE_AMD64; + Resp.u.GetVersion.fFlags |= KD_PACKET_MANIPULATE64_GET_VERSION_F_PTR64; + } + + return dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &Resp, sizeof(Resp), true /*fAck*/); +} + + +/** + * Processes a read memory 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64ReadMem(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_XFERMEM64 XferMem64; + uint8_t abMem[_4K]; + RT_ZERO(RespHdr); RT_ZERO(XferMem64); + + DBGFADDRESS AddrRead; + uint32_t cbRead = RT_MIN(sizeof(abMem), pPktManip->u.XferMem.cbXferReq); + if (pPktManip->Hdr.idReq == KD_PACKET_MANIPULATE_REQ_READ_VIRT_MEM) + DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrRead, KD_PTR_GET(pThis, pPktManip->u.XferMem.u64PtrTarget)); + else + DBGFR3AddrFromPhys(pThis->Dbgc.pUVM, &AddrRead, KD_PTR_GET(pThis, pPktManip->u.XferMem.u64PtrTarget)); + + RTSGSEG aRespSegs[3]; + uint32_t cSegs = 2; /* Gets incremented when read is successful. */ + RespHdr.idReq = pPktManip->Hdr.idReq; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + XferMem64.u64PtrTarget = pPktManip->u.XferMem.u64PtrTarget; + XferMem64.cbXferReq = pPktManip->u.XferMem.cbXferReq; + XferMem64.cbXfered = (uint32_t)cbRead; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &XferMem64; + aRespSegs[1].cbSeg = sizeof(XferMem64); + + int rc = DBGFR3MemRead(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrRead, &abMem[0], cbRead); + if (RT_SUCCESS(rc)) + { + cSegs++; + aRespSegs[2].pvSeg = &abMem[0]; + aRespSegs[2].cbSeg = cbRead; + } + else + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; /** @todo Convert to an appropriate NT status code. */ + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], cSegs, true /*fAck*/); +} + + +/** + * Processes a write memory 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64WriteMem(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_XFERMEM64 XferMem64; + RT_ZERO(RespHdr); RT_ZERO(XferMem64); + + DBGFADDRESS AddrWrite; + const void *pv = &pThis->abBody[sizeof(*pPktManip)]; /* Data comes directly after the manipulate state body. */ + uint32_t cbWrite = RT_MIN(sizeof(pThis->abBody) - sizeof(*pPktManip), pPktManip->u.XferMem.cbXferReq); + if (pPktManip->Hdr.idReq == KD_PACKET_MANIPULATE_REQ_WRITE_VIRT_MEM) + DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrWrite, KD_PTR_GET(pThis, pPktManip->u.XferMem.u64PtrTarget)); + else + DBGFR3AddrFromPhys(pThis->Dbgc.pUVM, &AddrWrite, KD_PTR_GET(pThis, pPktManip->u.XferMem.u64PtrTarget)); + + RTSGSEG aRespSegs[2]; + uint32_t cSegs = 2; + RespHdr.idReq = pPktManip->Hdr.idReq; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + XferMem64.u64PtrTarget = pPktManip->u.XferMem.u64PtrTarget; + XferMem64.cbXferReq = pPktManip->u.XferMem.cbXferReq; + XferMem64.cbXfered = (uint32_t)cbWrite; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &XferMem64; + aRespSegs[1].cbSeg = sizeof(XferMem64); + + int rc = DBGFR3MemWrite(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrWrite, pv, cbWrite); + if (RT_FAILURE(rc)) + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; /** @todo Convert to an appropriate NT status code. */ + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], cSegs, true /*fAck*/); +} + + +/** + * Processes a continue request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64Continue(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + RT_NOREF(pPktManip); + int rc = VINF_SUCCESS; + + /* No response, just resume. */ + if (DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL)) + rc = DBGFR3Resume(pThis->Dbgc.pUVM, VMCPUID_ALL); + + return rc; +} + + +/** + * Processes a continue request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64Continue2(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + int rc = VINF_SUCCESS; + + /* Update DR7. */ + if (pThis->f32Bit) + rc = dbgcKdCtxHwBpDr7Update(pThis, pPktManip->u.Continue2.u.x86.u32RegDr7); + else + rc = dbgcKdCtxHwBpDr7Update(pThis, (uint32_t)pPktManip->u.Continue2.u.amd64.u64RegDr7); + + /* Resume if not single stepping, the single step will get a state change when the VM stepped. */ + if (pPktManip->u.Continue2.fTrace) + { + PDBGFADDRESS pStackPop = NULL; + RTGCPTR cbStackPop = 0; + rc = DBGFR3StepEx(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGF_STEP_F_INTO, NULL, + pStackPop, cbStackPop, 1 /*cMaxSteps*/); + } + else if (DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL)) + rc = DBGFR3Resume(pThis->Dbgc.pUVM, VMCPUID_ALL); + + return rc; +} + + +/** + * Processes a set context request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64SetContext(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_SETCONTEXT SetContext; + RT_ZERO(RespHdr); RT_ZERO(SetContext); + + PCNTCONTEXT64 pNtCtx = (PCNTCONTEXT64)&pThis->abBody[sizeof(*pPktManip)]; /* Data comes directly after the manipulate state body. */ + + RTSGSEG aRespSegs[2]; + uint32_t cSegs = 2; + RespHdr.idReq = pPktManip->Hdr.idReq; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + /** @todo What do these flags mean? Can't be the context state to set because the valid one is + * in NTCONTEXT64::fContext (observed with WinDbg). */ + SetContext.u32CtxFlags = pPktManip->u.SetContext.u32CtxFlags; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &SetContext; + aRespSegs[1].cbSeg = sizeof(SetContext); + + int rc = dbgcKdCtxSetNtCtx64(pThis, pPktManip->Hdr.idCpu, pNtCtx, pNtCtx->fContext); + if (RT_FAILURE(rc)) + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; /** @todo Convert to an appropriate NT status code. */ + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], cSegs, true /*fAck*/); +} + + +/** + * Processes a read control space 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64ReadCtrlSpace(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_XFERCTRLSPACE64 XferCtrlSpace64; + uint8_t abResp[sizeof(NTKCONTEXT64)]; + uint32_t cbData = 0; + RT_ZERO(RespHdr); RT_ZERO(XferCtrlSpace64); + RT_ZERO(abResp); + + RTSGSEG aRespSegs[3]; + uint32_t cSegs = 2; /* Gets incremented when read is successful. */ + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + XferCtrlSpace64.u64IdXfer = pPktManip->u.XferCtrlSpace.u64IdXfer; + XferCtrlSpace64.cbXferReq = pPktManip->u.XferCtrlSpace.cbXferReq; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &XferCtrlSpace64; + aRespSegs[1].cbSeg = sizeof(XferCtrlSpace64); + + int rc = VINF_SUCCESS; + if (pThis->f32Bit) + { + if (pPktManip->u.XferCtrlSpace.u64IdXfer == sizeof(NTCONTEXT32)) + { + /* Queries the kernel context. */ + rc = dbgcKdCtxQueryNtKCtx32(pThis, RespHdr.idCpu, (PNTKCONTEXT32)&abResp[0]); + if (RT_SUCCESS(rc)) + cbData = sizeof(NTKCONTEXT32); + } + } + else + { + switch (pPktManip->u.XferCtrlSpace.u64IdXfer) + { + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KPCR: + { + if (pThis->pIfWinNt) + { + RTGCUINTPTR GCPtrKpcr = 0; + + rc = pThis->pIfWinNt->pfnQueryKpcrForVCpu(pThis->pIfWinNt, pThis->Dbgc.pUVM, VMMR3GetVTable(), RespHdr.idCpu, + &GCPtrKpcr, NULL /*pKpcrb*/); + if (RT_SUCCESS(rc)) + memcpy(&abResp[0], &GCPtrKpcr, sizeof(GCPtrKpcr)); + } + + cbData = sizeof(RTGCUINTPTR); + break; + } + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KPCRB: + { + if (pThis->pIfWinNt) + { + RTGCUINTPTR GCPtrKpcrb = 0; + + rc = pThis->pIfWinNt->pfnQueryKpcrForVCpu(pThis->pIfWinNt, pThis->Dbgc.pUVM, VMMR3GetVTable(), RespHdr.idCpu, + NULL /*pKpcr*/, &GCPtrKpcrb); + if (RT_SUCCESS(rc)) + memcpy(&abResp[0], &GCPtrKpcrb, sizeof(GCPtrKpcrb)); + } + + cbData = sizeof(RTGCUINTPTR); + break; + } + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KCTX: + { + rc = dbgcKdCtxQueryNtKCtx64(pThis, RespHdr.idCpu, (PNTKCONTEXT64)&abResp[0], NTCONTEXT64_F_FULL); + if (RT_SUCCESS(rc)) + cbData = sizeof(NTKCONTEXT64); + break; + } + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KTHRD: + { + if (pThis->pIfWinNt) + { + RTGCUINTPTR GCPtrCurThrd = 0; + + rc = pThis->pIfWinNt->pfnQueryCurThrdForVCpu(pThis->pIfWinNt, pThis->Dbgc.pUVM, VMMR3GetVTable(), + RespHdr.idCpu, &GCPtrCurThrd); + if (RT_SUCCESS(rc)) + memcpy(&abResp[0], &GCPtrCurThrd, sizeof(GCPtrCurThrd)); + } + + cbData = sizeof(RTGCUINTPTR); + break; + } + default: + rc = VERR_NOT_SUPPORTED; + break; + } + } + + if ( RT_SUCCESS(rc) + && cbData) + { + XferCtrlSpace64.cbXfered = RT_MIN(cbData, XferCtrlSpace64.cbXferReq); + + cSegs++; + aRespSegs[2].pvSeg = &abResp[0]; + aRespSegs[2].cbSeg = cbData; + } + else if (RT_FAILURE(rc)) + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; /** @todo Convert to an appropriate NT status code. */ + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], cSegs, true /*fAck*/); +} + + +/** + * Processes a write control space 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64WriteCtrlSpace(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_XFERCTRLSPACE64 XferCtrlSpace64; + uint32_t cbData = 0; + RT_ZERO(RespHdr); RT_ZERO(XferCtrlSpace64); + + RTSGSEG aRespSegs[2]; + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_WRITE_CTRL_SPACE; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + XferCtrlSpace64.u64IdXfer = pPktManip->u.XferCtrlSpace.u64IdXfer; + XferCtrlSpace64.cbXferReq = pPktManip->u.XferCtrlSpace.cbXferReq; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &XferCtrlSpace64; + aRespSegs[1].cbSeg = sizeof(XferCtrlSpace64); + + int rc = VINF_SUCCESS; + switch (pPktManip->u.XferCtrlSpace.u64IdXfer) + { + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KCTX: + { + PCNTKCONTEXT64 pNtKCtx = (PCNTKCONTEXT64)&pThis->abBody[sizeof(*pPktManip)]; /* Data comes directly after the manipulate state body. */ + rc = dbgcKdCtxSetNtKCtx64(pThis, RespHdr.idCpu, pNtKCtx, XferCtrlSpace64.cbXferReq); + if (RT_SUCCESS(rc)) + cbData = RT_MIN(XferCtrlSpace64.cbXferReq, sizeof(NTKCONTEXT64)); + break; + } + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KPCR: + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KPCRB: + case KD_PACKET_MANIPULATE64_CTRL_SPACE_ID_KTHRD: + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_FAILURE(rc)) + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; /** @todo Convert to an appropriate NT status code. */ + else + XferCtrlSpace64.cbXfered = cbData; + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); +} + + +/** + * Processes a restore breakpoint 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64RestoreBkpt(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_RESTOREBKPT64 RestoreBkpt64; + RT_ZERO(RespHdr); RT_ZERO(RestoreBkpt64); + + RTSGSEG aRespSegs[2]; + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_RESTORE_BKPT; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + RestoreBkpt64.u32HndBkpt = pPktManip->u.RestoreBkpt.u32HndBkpt; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &RestoreBkpt64; + aRespSegs[1].cbSeg = sizeof(RestoreBkpt64); + + int rc = DBGFR3BpClear(pThis->Dbgc.pUVM, pPktManip->u.RestoreBkpt.u32HndBkpt); + if (RT_SUCCESS(rc)) + { + rc = dbgcBpDelete(&pThis->Dbgc, pPktManip->u.RestoreBkpt.u32HndBkpt); + AssertRC(rc); + } + else if (rc != VERR_DBGF_BP_NOT_FOUND) + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); +} + + +/** + * Processes a write breakpoint 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64WriteBkpt(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_WRITEBKPT64 WriteBkpt64; + RT_ZERO(RespHdr); RT_ZERO(WriteBkpt64); + + RTSGSEG aRespSegs[2]; + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_WRITE_BKPT; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &WriteBkpt64; + aRespSegs[1].cbSeg = sizeof(WriteBkpt64); + + WriteBkpt64.u64PtrBkpt = pPktManip->u.WriteBkpt.u64PtrBkpt; + + DBGFADDRESS BpAddr; + DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &BpAddr, KD_PTR_GET(pThis, pPktManip->u.WriteBkpt.u64PtrBkpt)); + int rc = DBGFR3BpSetInt3(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &BpAddr, + 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/, &WriteBkpt64.u32HndBkpt); + if (RT_SUCCESS(rc)) + { + rc = dbgcBpAdd(&pThis->Dbgc, WriteBkpt64.u32HndBkpt, NULL /*pszCmd*/); + if (RT_FAILURE(rc)) + DBGFR3BpClear(pThis->Dbgc.pUVM, WriteBkpt64.u32HndBkpt); + } + else + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); +} + + +/** + * Processes a get context extended 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64GetContextEx(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_CONTEXTEX ContextEx; + union + { + NTCONTEXT64 v64; + NTCONTEXT32 v32; + } NtCtx; + RT_ZERO(RespHdr); RT_ZERO(ContextEx); RT_ZERO(NtCtx); + + RTSGSEG aRespSegs[3]; + uint32_t cSegs = 2; + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_GET_CONTEXT_EX; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; + + ContextEx.offStart = pPktManip->u.ContextEx.offStart; + ContextEx.cbXfer = pPktManip->u.ContextEx.cbXfer; + ContextEx.cbXfered = 0; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &ContextEx; + aRespSegs[1].cbSeg = sizeof(ContextEx); + + int rc = VINF_SUCCESS; + uint32_t cbCtx = pThis->f32Bit ? sizeof(NtCtx.v32) : sizeof(NtCtx.v64); + if (pThis->f32Bit) + dbgcKdCtxQueryNtCtx32(pThis, pPktManip->Hdr.idCpu, &NtCtx.v32, NTCONTEXT32_F_FULL); + else + dbgcKdCtxQueryNtCtx64(pThis, pPktManip->Hdr.idCpu, &NtCtx.v64, NTCONTEXT64_F_FULL); + if ( RT_SUCCESS(rc) + && pPktManip->u.ContextEx.offStart < cbCtx) + { + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + ContextEx.cbXfered = RT_MIN(cbCtx - ContextEx.offStart, ContextEx.cbXfer); + + aRespSegs[2].pvSeg = (uint8_t *)&NtCtx + ContextEx.offStart; + aRespSegs[2].cbSeg = ContextEx.cbXfered; + cSegs++; + } + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], cSegs, true /*fAck*/); +} + + +/** + * Processes a query memory 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64QueryMemory(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_QUERYMEMORY QueryMemory; + RT_ZERO(RespHdr); RT_ZERO(QueryMemory); + + RTSGSEG aRespSegs[2]; + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_QUERY_MEMORY; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + /** @todo Need DBGF API to query protection and privilege level from guest page tables. */ + QueryMemory.u64GCPtr = pPktManip->u.QueryMemory.u64GCPtr; + QueryMemory.u32AddrSpace = KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_SPACE_KERNEL; + QueryMemory.u32Flags = KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_READ + | KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_WRITE + | KD_PACKET_MANIPULATE64_QUERY_MEMORY_ADDR_F_EXEC; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &QueryMemory; + aRespSegs[1].cbSeg = sizeof(QueryMemory); + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); +} + + +/** + * Processes a search memory 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64SearchMemory(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + KDPACKETMANIPULATEHDR RespHdr; + KDPACKETMANIPULATE_SEARCHMEMORY SearchMemory; + RT_ZERO(RespHdr); RT_ZERO(SearchMemory); + + RTSGSEG aRespSegs[2]; + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_SEARCH_MEMORY; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_SUCCESS; + + SearchMemory.u64GCPtr = pPktManip->u.SearchMemory.u64GCPtr; + SearchMemory.cbSearch = pPktManip->u.SearchMemory.cbSearch; + SearchMemory.cbPattern = pPktManip->u.SearchMemory.cbPattern; + + /* Validate the pattern length and start searching. */ + if (pPktManip->u.SearchMemory.cbPattern < sizeof(pThis->abBody) - sizeof(*pPktManip)) + { + DBGFADDRESS StartAddress; + DBGFADDRESS HitAddress; + VMCPUID idCpu = pPktManip->Hdr.idCpu; + DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &StartAddress, pPktManip->u.SearchMemory.u64GCPtr); + + /** @todo WindDbg sends CPU ID 32 sometimes, maybe that means continue search on last used CPU?. */ + if (idCpu >= DBGFR3CpuGetCount(pThis->Dbgc.pUVM)) + idCpu = pThis->Dbgc.idCpu; + + int rc = DBGFR3MemScan(pThis->Dbgc.pUVM, idCpu, &StartAddress, pPktManip->u.SearchMemory.cbSearch, 1, + &pThis->abBody[sizeof(*pPktManip)], pPktManip->u.SearchMemory.cbPattern, &HitAddress); + if (RT_SUCCESS(rc)) + SearchMemory.u64GCPtr = HitAddress.FlatPtr; + else if (rc == VERR_DBGF_MEM_NOT_FOUND) + RespHdr.u32NtStatus = NTSTATUS_NOT_FOUND; + else + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; + } + else + RespHdr.u32NtStatus = NTSTATUS_BUFFER_OVERFLOW; + + aRespSegs[0].pvSeg = &RespHdr; + aRespSegs[0].cbSeg = sizeof(RespHdr); + aRespSegs[1].pvSeg = &SearchMemory; + aRespSegs[1].cbSeg = sizeof(SearchMemory); + + return dbgcKdCtxPktSendSg(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &aRespSegs[0], RT_ELEMENTS(aRespSegs), true /*fAck*/); +} + + +/** + * Processes a cause bugcheck 64 request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + * + * @note We abuse this request to initiate a native VBox debugger command prompt from the remote end + * (There is monitor/Rcmd equivalent like with GDB unfortunately). + */ +static int dbgcKdCtxPktManipulate64CauseBugCheck(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + RT_NOREF(pPktManip); + pThis->fInVBoxDbg = true; + return dbgcKdCtxDebugIoGetStrSend(pThis, pThis->Dbgc.idCpu, "VBoxDbg>", sizeof("VBoxDbg>") - 1, + 512 /*cbResponseMax*/); +} + + +/** + * Processes a switch processor request. + * + * @returns VBox status code. + * @param pThis The KD context. + * @param pPktManip The manipulate packet request. + */ +static int dbgcKdCtxPktManipulate64SwitchProcessor(PKDCTX pThis, PCKDPACKETMANIPULATE64 pPktManip) +{ + int rc = VINF_SUCCESS; + + if (RT_UNLIKELY(pPktManip->Hdr.idCpu >= DBGFR3CpuGetCount(pThis->Dbgc.pUVM))) + { + KDPACKETMANIPULATEHDR RespHdr; + RT_ZERO(RespHdr); + + RespHdr.idReq = KD_PACKET_MANIPULATE_REQ_SWITCH_PROCESSOR; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_UNSUCCESSFUL; /** @todo Test this path. */ + rc = dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &RespHdr, sizeof(RespHdr), true /*fAck*/); + } + else + { + pThis->Dbgc.idCpu = pPktManip->Hdr.idCpu; + rc = dbgcKdCtxStateChangeSend(pThis, DBGFEVENT_HALT_DONE); + } + + return rc; +} + + +/** + * Processes a manipulate packet. + * + * @returns VBox status code. + * @param pThis The KD context. + */ +static int dbgcKdCtxPktManipulate64Process(PKDCTX pThis) +{ + int rc = VINF_SUCCESS; + PCKDPACKETMANIPULATE64 pPktManip = (PCKDPACKETMANIPULATE64)&pThis->abBody[0]; + + switch (pPktManip->Hdr.idReq) + { + case KD_PACKET_MANIPULATE_REQ_GET_VERSION: + { + rc = dbgcKdCtxPktManipulate64GetVersion(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_READ_VIRT_MEM: + case KD_PACKET_MANIPULATE_REQ_READ_PHYS_MEM: + { + rc = dbgcKdCtxPktManipulate64ReadMem(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_WRITE_VIRT_MEM: + case KD_PACKET_MANIPULATE_REQ_WRITE_PHYS_MEM: + { + rc = dbgcKdCtxPktManipulate64WriteMem(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_CONTINUE: + { + rc = dbgcKdCtxPktManipulate64Continue(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_CONTINUE2: + { + rc = dbgcKdCtxPktManipulate64Continue2(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_SET_CONTEXT: + { + rc = dbgcKdCtxPktManipulate64SetContext(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_READ_CTRL_SPACE: + { + rc = dbgcKdCtxPktManipulate64ReadCtrlSpace(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_WRITE_CTRL_SPACE: + { + rc = dbgcKdCtxPktManipulate64WriteCtrlSpace(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_RESTORE_BKPT: + { + rc = dbgcKdCtxPktManipulate64RestoreBkpt(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_WRITE_BKPT: + { + rc = dbgcKdCtxPktManipulate64WriteBkpt(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_CLEAR_ALL_INTERNAL_BKPT: + /* WinDbg doesn't seem to expect an answer apart from the ACK here. */ + break; + case KD_PACKET_MANIPULATE_REQ_GET_CONTEXT_EX: + { + rc = dbgcKdCtxPktManipulate64GetContextEx(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_QUERY_MEMORY: + { + rc = dbgcKdCtxPktManipulate64QueryMemory(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_SEARCH_MEMORY: + { + rc = dbgcKdCtxPktManipulate64SearchMemory(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_CAUSE_BUGCHECK: + { + rc = dbgcKdCtxPktManipulate64CauseBugCheck(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_SWITCH_PROCESSOR: + { + rc = dbgcKdCtxPktManipulate64SwitchProcessor(pThis, pPktManip); + break; + } + case KD_PACKET_MANIPULATE_REQ_REBOOT: + { + rc = VMR3Reset(pThis->Dbgc.pUVM); /* Doesn't expect an answer here. */ + if ( RT_SUCCESS(rc) + && DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL)) + rc = DBGFR3Resume(pThis->Dbgc.pUVM, VMCPUID_ALL); + break; + } + default: + KDPACKETMANIPULATEHDR RespHdr; + RT_ZERO(RespHdr); + + RespHdr.idReq = pPktManip->Hdr.idReq; + RespHdr.u16CpuLvl = pPktManip->Hdr.u16CpuLvl; + RespHdr.idCpu = pPktManip->Hdr.idCpu; + RespHdr.u32NtStatus = NTSTATUS_NOT_IMPLEMENTED; + rc = dbgcKdCtxPktSend(pThis, KD_PACKET_HDR_SIGNATURE_DATA, KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE, + &RespHdr, sizeof(RespHdr), true /*fAck*/); + break; + } + + return rc; +} + + +/** + * Tries to detect the guest OS running in the VM looking specifically for the Windows NT kind. + * + * @param pThis The KD context. + */ +static void dbgcKdCtxDetectGstOs(PKDCTX pThis) +{ + pThis->pIfWinNt = NULL; + + /* Try detecting a Windows NT guest. */ + char szName[64]; + int rc = DBGFR3OSDetect(pThis->Dbgc.pUVM, szName, sizeof(szName)); + if (RT_SUCCESS(rc)) + { + pThis->pIfWinNt = (PDBGFOSIWINNT)DBGFR3OSQueryInterface(pThis->Dbgc.pUVM, DBGFOSINTERFACE_WINNT); + if (pThis->pIfWinNt) + LogRel(("DBGC/Kd: Detected Windows NT guest OS (%s)\n", &szName[0])); + else + LogRel(("DBGC/Kd: Detected guest OS is not of the Windows NT kind (%s)\n", &szName[0])); + } + else + { + LogRel(("DBGC/Kd: Unable to detect any guest operating system type, rc=%Rrc\n", rc)); + rc = VINF_SUCCESS; /* Try to continue nevertheless. */ + } + + if (pThis->pIfWinNt) + { + rc = pThis->pIfWinNt->pfnQueryVersion(pThis->pIfWinNt, pThis->Dbgc.pUVM, VMMR3GetVTable(), + NULL /*puVersMajor*/, NULL /*puVersMinor*/, + NULL /*puBuildNumber*/, &pThis->f32Bit); + AssertRC(rc); + } + else + { + /* + * Try to detect bitness based on the current CPU mode which might fool us (32bit process running + * inside of 64bit host). + */ + CPUMMODE enmMode = DBGCCmdHlpGetCpuMode(&pThis->Dbgc.CmdHlp); + if (enmMode == CPUMMODE_PROTECTED) + pThis->f32Bit = true; + else if (enmMode == CPUMMODE_LONG) + pThis->f32Bit = false; + else + LogRel(("DBGC/Kd: Heh, trying to debug real mode code with WinDbg are we? Good luck with that...\n")); + } +} + + +/** + * Processes a fully received packet. + * + * @returns VBox status code. + * @param pThis The KD context. + */ +static int dbgcKdCtxPktProcess(PKDCTX pThis) +{ + int rc = VINF_SUCCESS; + + pThis->fBreakinRecv = false; + + /* Verify checksum. */ + if (dbgcKdPktChkSumGen(&pThis->abBody[0], pThis->PktHdr.Fields.cbBody) == pThis->PktHdr.Fields.u32ChkSum) + { + /** @todo Check packet id. */ + if (pThis->PktHdr.Fields.u16SubType != KD_PACKET_HDR_SUB_TYPE_RESET) + { + pThis->idPktNext = pThis->PktHdr.Fields.idPacket; + rc = dbgcKdCtxPktSendAck(pThis); + } + if (RT_SUCCESS(rc)) + { +#ifdef LOG_ENABLED + RTSGSEG Seg; + Seg.pvSeg = &pThis->abBody[0]; + Seg.cbSeg = pThis->PktHdr.Fields.cbBody; + dbgcKdPktDump(&pThis->PktHdr.Fields, &Seg, 1 /*cSegs*/, true /*fRx*/); +#endif + + switch (pThis->PktHdr.Fields.u16SubType) + { + case KD_PACKET_HDR_SUB_TYPE_RESET: + { + dbgcKdCtxDetectGstOs(pThis); + + pThis->idPktNext = 0; + rc = dbgcKdCtxPktSendReset(pThis); + if (RT_SUCCESS(rc)) + { + rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL); + if (rc == VWRN_DBGF_ALREADY_HALTED) + rc = dbgcKdCtxStateChangeSend(pThis, DBGFEVENT_HALT_DONE); + } + pThis->idPktNext = KD_PACKET_HDR_ID_RESET; + break; + } + case KD_PACKET_HDR_SUB_TYPE_STATE_MANIPULATE: + { + pThis->idPktNext = pThis->PktHdr.Fields.idPacket ^ 0x1; + rc = dbgcKdCtxPktManipulate64Process(pThis); + break; + } + case KD_PACKET_HDR_SUB_TYPE_ACKNOWLEDGE: + case KD_PACKET_HDR_SUB_TYPE_RESEND: + { + /* Don't do anything. */ + rc = VINF_SUCCESS; + break; + } + case KD_PACKET_HDR_SUB_TYPE_DEBUG_IO: + { + if (pThis->fInVBoxDbg) + { + pThis->idPktNext = pThis->PktHdr.Fields.idPacket ^ 0x1; + /* Get the string and execute it. */ + PCKDPACKETDEBUGIO pPktDbgIo = (PCKDPACKETDEBUGIO)&pThis->abBody[0]; + if ( pPktDbgIo->u32Type == KD_PACKET_DEBUG_IO_GET_STRING + && pPktDbgIo->u.Prompt.cbReturn <= sizeof(pThis->abBody) - sizeof(*pPktDbgIo) - 1) + { + if (pPktDbgIo->u.Prompt.cbReturn) + { + /* Terminate return value. */ + pThis->abBody[sizeof(*pPktDbgIo) + pPktDbgIo->u.Prompt.cbReturn] = '\0'; + + const char *pszCmd = (const char *)&pThis->abBody[sizeof(*pPktDbgIo)]; + /* Filter out 'exit' which is handled here directly and exits the debug loop. */ + if (!strcmp(pszCmd, "exit")) + pThis->fInVBoxDbg = false; + else + { + rc = pThis->Dbgc.CmdHlp.pfnExec(&pThis->Dbgc.CmdHlp, pszCmd); + if (RT_SUCCESS(rc)) + rc = dbgcKdCtxDebugIoGetStrSend(pThis, pThis->Dbgc.idCpu, "VBoxDbg>", sizeof("VBoxDbg>") - 1, + 512 /*cbResponseMax*/); + else + LogRel(("DBGC/Kd: Executing command \"%s\" failed with rc=%Rrc\n", pszCmd, rc)); + } + } + else + rc = dbgcKdCtxDebugIoGetStrSend(pThis, pThis->Dbgc.idCpu, "VBoxDbg>", sizeof("VBoxDbg>") - 1, + 512 /*cbResponseMax*/); + } + else + LogRel(("DBGC/Kd: Received invalid DEBUG_IO packet from remote end, ignoring\n")); + } + else + LogRel(("DBGC/Kd: Received out of band DEBUG_IO packet from remote end, ignoring\n")); + break; + } + default: + rc = VERR_NOT_IMPLEMENTED; + } + } + } + else + { + pThis->idPktNext = pThis->PktHdr.Fields.idPacket; + rc = dbgcKdCtxPktSendResend(pThis); + } + + if (pThis->fBreakinRecv) + { + pThis->fBreakinRecv = false; + rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL); + if (rc == VWRN_DBGF_ALREADY_HALTED) + rc = dbgcKdCtxStateChangeSend(pThis, DBGFEVENT_HALT_DONE); + } + + /* Next packet. */ + dbgcKdCtxPktRecvReset(pThis); + return rc; +} + + +/** + * Processes the received data based on the current state. + * + * @returns VBox status code. + * @param pThis The KD context. + */ +static int dbgcKdCtxRecvDataProcess(PKDCTX pThis) +{ + int rc = VINF_SUCCESS; + + switch (pThis->enmState) + { + case KDRECVSTATE_PACKET_HDR_FIRST_BYTE: + { + /* Does it look like a valid packet start?. */ + if ( pThis->PktHdr.ab[0] == KD_PACKET_HDR_SIGNATURE_DATA_BYTE + || pThis->PktHdr.ab[0] == KD_PACKET_HDR_SIGNATURE_CONTROL_BYTE) + { + pThis->pbRecv = &pThis->PktHdr.ab[1]; + pThis->cbRecvLeft = sizeof(pThis->PktHdr.ab[1]); + pThis->enmState = KDRECVSTATE_PACKET_HDR_SECOND_BYTE; + pThis->msRecvTimeout = DBGC_KD_RECV_TIMEOUT_MS; + } + else if (pThis->PktHdr.ab[0] == KD_PACKET_HDR_SIGNATURE_BREAKIN_BYTE) + { + rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL); + if (rc == VWRN_DBGF_ALREADY_HALTED) + rc = dbgcKdCtxStateChangeSend(pThis, DBGFEVENT_HALT_DONE); + dbgcKdCtxPktRecvReset(pThis); + } + else + dbgcKdCtxPktRecvReset(pThis); /* Reset and continue. */ + break; + } + case KDRECVSTATE_PACKET_HDR_SECOND_BYTE: + { + /* + * If the first and second byte differ there might be a single breakin + * packet byte received and this is actually the start of a new packet. + */ + if (pThis->PktHdr.ab[0] != pThis->PktHdr.ab[1]) + { + if (pThis->PktHdr.ab[0] == KD_PACKET_HDR_SIGNATURE_BREAKIN_BYTE) + { + /* Halt the VM and rearrange the packet receiving state machine. */ + LogFlow(("DbgKd: Halting VM!\n")); + + rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL); + pThis->PktHdr.ab[0] = pThis->PktHdr.ab[1]; /* Overwrite the first byte with the new start. */ + pThis->pbRecv = &pThis->PktHdr.ab[1]; + pThis->cbRecvLeft = sizeof(pThis->PktHdr.ab[1]); + } + else + rc = VERR_NET_PROTOCOL_ERROR; /* Refuse talking to the remote end any further. */ + } + else + { + /* Normal packet receive continues with the rest of the header. */ + pThis->pbRecv = &pThis->PktHdr.ab[2]; + pThis->cbRecvLeft = sizeof(pThis->PktHdr.Fields) - 2; + pThis->enmState = KDRECVSTATE_PACKET_HDR; + } + break; + } + case KDRECVSTATE_PACKET_HDR: + { + if ( dbgcKdPktHdrValidate(&pThis->PktHdr.Fields) + && pThis->PktHdr.Fields.cbBody <= sizeof(pThis->abBody)) + { + /* Start receiving the body. */ + if (pThis->PktHdr.Fields.cbBody) + { + pThis->pbRecv = &pThis->abBody[0]; + pThis->cbRecvLeft = pThis->PktHdr.Fields.cbBody; + pThis->enmState = KDRECVSTATE_PACKET_BODY; + } + else /* No body means no trailer byte it looks like. */ + rc = dbgcKdCtxPktProcess(pThis); + } + else + rc = VERR_NET_PROTOCOL_ERROR; + break; + } + case KDRECVSTATE_PACKET_BODY: + { + pThis->enmState = KDRECVSTATE_PACKET_TRAILER; + pThis->bTrailer = 0; + pThis->pbRecv = &pThis->bTrailer; + pThis->cbRecvLeft = sizeof(pThis->bTrailer); + break; + } + case KDRECVSTATE_PACKET_TRAILER: + { + if (pThis->bTrailer == KD_PACKET_TRAILING_BYTE) + rc = dbgcKdCtxPktProcess(pThis); + else + rc = VERR_NET_PROTOCOL_ERROR; + break; + } + default: + AssertMsgFailed(("Invalid receive state %d\n", pThis->enmState)); + } + + return rc; +} + + +/** + * Receive data and processes complete packets. + * + * @returns Status code. + * @param pThis The KD context. + */ +static int dbgcKdCtxRecv(PKDCTX pThis) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p{.cbRecvLeft=%zu}\n", pThis, pThis->cbRecvLeft)); + + if (pThis->cbRecvLeft) + { + size_t cbRead = 0; + rc = pThis->Dbgc.pIo->pfnRead(pThis->Dbgc.pIo, pThis->pbRecv, pThis->cbRecvLeft, &cbRead); + if (RT_SUCCESS(rc)) + { + pThis->tsRecvLast = RTTimeMilliTS(); + pThis->cbRecvLeft -= cbRead; + pThis->pbRecv += cbRead; + if (!pThis->cbRecvLeft) + rc = dbgcKdCtxRecvDataProcess(pThis); + } + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Processes debugger events. + * + * @returns VBox status code. + * @param pThis The KD context data. + * @param pEvent Pointer to event data. + */ +static int dbgcKdCtxProcessEvent(PKDCTX pThis, PCDBGFEVENT pEvent) +{ + /* + * Process the event. + */ + PDBGC pDbgc = &pThis->Dbgc; + pThis->Dbgc.pszScratch = &pThis->Dbgc.achInput[0]; + pThis->Dbgc.iArg = 0; + int rc = VINF_SUCCESS; + VMCPUID idCpuOld = pDbgc->idCpu; + pDbgc->idCpu = pEvent->idCpu; + switch (pEvent->enmType) + { + /* + * The first part is events we have initiated with commands. + */ + case DBGFEVENT_HALT_DONE: + { + rc = dbgcKdCtxStateChangeSend(pThis, pEvent->enmType); + break; + } + + /* + * The second part is events which can occur at any time. + */ + case DBGFEVENT_FATAL_ERROR: + { + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n", + dbgcGetEventCtx(pEvent->enmCtx)); + if (RT_SUCCESS(rc)) + rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r"); + break; + } + + case DBGFEVENT_BREAKPOINT: + case DBGFEVENT_BREAKPOINT_IO: + case DBGFEVENT_BREAKPOINT_MMIO: + case DBGFEVENT_BREAKPOINT_HYPER: + { + rc = dbgcBpExec(pDbgc, pEvent->u.Bp.hBp); + switch (rc) + { + case VERR_DBGC_BP_NOT_FOUND: + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n", + pEvent->u.Bp.hBp, dbgcGetEventCtx(pEvent->enmCtx)); + break; + + case VINF_DBGC_BP_NO_COMMAND: + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n", + pEvent->u.Bp.hBp, dbgcGetEventCtx(pEvent->enmCtx)); + break; + + case VINF_BUFFER_OVERFLOW: + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n", + pEvent->u.Bp.hBp, dbgcGetEventCtx(pEvent->enmCtx)); + break; + + default: + break; + } + if (RT_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pUVM, VMCPUID_ALL)) + { + rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r"); + + /* Set the resume flag to ignore the breakpoint when resuming execution. */ + if ( RT_SUCCESS(rc) + && pEvent->enmType == DBGFEVENT_BREAKPOINT) + rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r eflags.rf = 1"); + } + + /* Figure out the breakpoint and set the triggered flag for emulation of DR6. */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aHwBp); i++) + { + if (pThis->aHwBp[i].hDbgfBp == pEvent->u.Bp.hBp) + { + pThis->aHwBp[i].fTriggered = true; + break; + } + } + + rc = dbgcKdCtxStateChangeSend(pThis, pEvent->enmType); + break; + } + + case DBGFEVENT_STEPPED: + case DBGFEVENT_STEPPED_HYPER: + { + pThis->fSingleStepped = true; /* For emulation of DR6. */ + rc = dbgcKdCtxStateChangeSend(pThis, pEvent->enmType); + break; + } + + case DBGFEVENT_ASSERTION_HYPER: + { + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, + "\ndbgf event: Hypervisor Assertion! (%s)\n" + "%s" + "%s" + "\n", + dbgcGetEventCtx(pEvent->enmCtx), + pEvent->u.Assert.pszMsg1, + pEvent->u.Assert.pszMsg2); + if (RT_SUCCESS(rc)) + rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r"); + break; + } + + case DBGFEVENT_DEV_STOP: + { + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, + "\n" + "dbgf event: DBGFSTOP (%s)\n" + "File: %s\n" + "Line: %d\n" + "Function: %s\n", + dbgcGetEventCtx(pEvent->enmCtx), + pEvent->u.Src.pszFile, + pEvent->u.Src.uLine, + pEvent->u.Src.pszFunction); + if (RT_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage) + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, + "Message: %s\n", + pEvent->u.Src.pszMessage); + if (RT_SUCCESS(rc)) + rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r"); + break; + } + + + case DBGFEVENT_INVALID_COMMAND: + { + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n"); + break; + } + + case DBGFEVENT_POWERING_OFF: + { + pThis->Dbgc.fReady = false; + pThis->Dbgc.pIo->pfnSetReady(pThis->Dbgc.pIo, false); + rc = VERR_GENERAL_FAILURE; + break; + } + + default: + { + /* + * Probably a generic event. Look it up to find its name. + */ + PCDBGCSXEVT pEvtDesc = dbgcEventLookup(pEvent->enmType); + if (pEvtDesc) + { + if (pEvtDesc->enmKind == kDbgcSxEventKind_Interrupt) + { + Assert(pEvtDesc->pszDesc); + Assert(pEvent->u.Generic.cArgs == 1); + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s no %#llx! (%s)\n", + pEvtDesc->pszDesc, pEvent->u.Generic.auArgs[0], pEvtDesc->pszName); + } + else if (pEvtDesc->fFlags & DBGCSXEVT_F_BUGCHECK) + { + Assert(pEvent->u.Generic.cArgs >= 5); + char szDetails[512]; + DBGFR3FormatBugCheck(pDbgc->pUVM, szDetails, sizeof(szDetails), pEvent->u.Generic.auArgs[0], + pEvent->u.Generic.auArgs[1], pEvent->u.Generic.auArgs[2], + pEvent->u.Generic.auArgs[3], pEvent->u.Generic.auArgs[4]); + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s %s%s!\n%s", pEvtDesc->pszName, + pEvtDesc->pszDesc ? "- " : "", pEvtDesc->pszDesc ? pEvtDesc->pszDesc : "", + szDetails); + } + else if ( (pEvtDesc->fFlags & DBGCSXEVT_F_TAKE_ARG) + || pEvent->u.Generic.cArgs > 1 + || ( pEvent->u.Generic.cArgs == 1 + && pEvent->u.Generic.auArgs[0] != 0)) + { + if (pEvtDesc->pszDesc) + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!", + pEvtDesc->pszName, pEvtDesc->pszDesc); + else + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!", pEvtDesc->pszName); + if (pEvent->u.Generic.cArgs <= 1) + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " arg=%#llx\n", pEvent->u.Generic.auArgs[0]); + else + { + for (uint32_t i = 0; i < pEvent->u.Generic.cArgs; i++) + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " args[%u]=%#llx", i, pEvent->u.Generic.auArgs[i]); + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n"); + } + } + else + { + if (pEvtDesc->pszDesc) + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!\n", + pEvtDesc->pszName, pEvtDesc->pszDesc); + else + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!\n", pEvtDesc->pszName); + } + } + else + rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType); + break; + } + } + + pDbgc->idCpu = idCpuOld; + return rc; +} + + +/** + * Handle a receive timeout. + * + * @returns VBox status code. + * @param pThis Pointer to the KD context. + */ +static int dbgcKdCtxRecvTimeout(PKDCTX pThis) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p\n", pThis)); + + /* + * If a single breakin packet byte was received but the header is otherwise incomplete + * the VM is halted and a state change will be sent in the event processing loop. + */ + if ( pThis->enmState == KDRECVSTATE_PACKET_HDR_SECOND_BYTE + && pThis->PktHdr.ab[0] == KD_PACKET_HDR_SIGNATURE_BREAKIN_BYTE) + { + LogFlow(("DbgKd: Halting VM!\n")); + rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL); + } + else /* Send a reset packet */ /** @todo Figure out the semantics in this case exactly. */ + rc = dbgcKdCtxPktSendReset(pThis); + + dbgcKdCtxPktRecvReset(pThis); + + LogFlowFunc(("rc=%Rrc\n", rc)); + return rc; +} + + +/** + * @copydoc DBGC::pfnOutput + */ +static DECLCALLBACK(int) dbgcKdOutput(void *pvUser, const char *pachChars, size_t cbChars) +{ + PKDCTX pThis = (PKDCTX)pvUser; + + return dbgcKdCtxDebugIoStrSend(pThis, pThis->Dbgc.idCpu, pachChars, cbChars); +} + + +/** + * Run the debugger console. + * + * @returns VBox status code. + * @param pThis Pointer to the KD context. + */ +int dbgcKdRun(PKDCTX pThis) +{ + /* + * We're ready for commands now. + */ + pThis->Dbgc.fReady = true; + pThis->Dbgc.pIo->pfnSetReady(pThis->Dbgc.pIo, true); + + /* + * Main Debugger Loop. + * + * This loop will either block on waiting for input or on waiting on + * debug events. If we're forwarding the log we cannot wait for long + * before we must flush the log. + */ + int rc; + for (;;) + { + rc = VERR_SEM_OUT_OF_TURN; + if (pThis->Dbgc.pUVM) + rc = DBGFR3QueryWaitable(pThis->Dbgc.pUVM); + + if (RT_SUCCESS(rc)) + { + /* + * Wait for a debug event. + */ + DBGFEVENT Evt; + rc = DBGFR3EventWait(pThis->Dbgc.pUVM, 32, &Evt); + if (RT_SUCCESS(rc)) + { + rc = dbgcKdCtxProcessEvent(pThis, &Evt); + if (RT_FAILURE(rc)) + break; + } + else if (rc != VERR_TIMEOUT) + break; + + /* + * Check for input. + */ + if (pThis->Dbgc.pIo->pfnInput(pThis->Dbgc.pIo, 0)) + { + rc = dbgcKdCtxRecv(pThis); + if (RT_FAILURE(rc)) + break; + } + } + else if (rc == VERR_SEM_OUT_OF_TURN) + { + /* + * Wait for input. + */ + if (pThis->Dbgc.pIo->pfnInput(pThis->Dbgc.pIo, 1000)) + { + rc = dbgcKdCtxRecv(pThis); + if (RT_FAILURE(rc)) + break; + } + else if ( pThis->msRecvTimeout != RT_INDEFINITE_WAIT + && (RTTimeMilliTS() - pThis->tsRecvLast >= pThis->msRecvTimeout)) + rc = dbgcKdCtxRecvTimeout(pThis); + } + else + break; + } + + return rc; +} + + +/** + * Creates a KD context instance with the given backend. + * + * @returns VBox status code. + * @param ppKdCtx Where to store the pointer to the KD stub context instance on success. + * @param pIo Pointer to the I/O callback table. + * @param fFlags Flags controlling the behavior. + */ +static int dbgcKdCtxCreate(PPKDCTX ppKdCtx, PCDBGCIO pIo, unsigned fFlags) +{ + /* + * Validate input. + */ + AssertPtrReturn(pIo, VERR_INVALID_POINTER); + AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER); + + /* + * Allocate and initialize. + */ + PKDCTX pThis = (PKDCTX)RTMemAllocZ(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + dbgcInitCmdHlp(&pThis->Dbgc); + /* + * This is compied from the native debug console (will be used for monitor commands) + * in DBGCConsole.cpp. Try to keep both functions in sync. + */ + pThis->Dbgc.pIo = pIo; + pThis->Dbgc.pfnOutput = dbgcKdOutput; + pThis->Dbgc.pvOutputUser = pThis; + pThis->Dbgc.pVM = NULL; + pThis->Dbgc.pUVM = NULL; + pThis->Dbgc.idCpu = 0; + pThis->Dbgc.hDbgAs = DBGF_AS_GLOBAL; + pThis->Dbgc.pszEmulation = "CodeView/WinDbg"; + pThis->Dbgc.paEmulationCmds = &g_aCmdsCodeView[0]; + pThis->Dbgc.cEmulationCmds = g_cCmdsCodeView; + pThis->Dbgc.paEmulationFuncs = &g_aFuncsCodeView[0]; + pThis->Dbgc.cEmulationFuncs = g_cFuncsCodeView; + //pThis->Dbgc.fLog = false; + pThis->Dbgc.fRegTerse = true; + pThis->Dbgc.fStepTraceRegs = true; + //pThis->Dbgc.cPagingHierarchyDumps = 0; + //pThis->Dbgc.DisasmPos = {0}; + //pThis->Dbgc.SourcePos = {0}; + //pThis->Dbgc.DumpPos = {0}; + pThis->Dbgc.pLastPos = &pThis->Dbgc.DisasmPos; + //pThis->Dbgc.cbDumpElement = 0; + //pThis->Dbgc.cVars = 0; + //pThis->Dbgc.paVars = NULL; + //pThis->Dbgc.pPlugInHead = NULL; + //pThis->Dbgc.pFirstBp = NULL; + //pThis->Dbgc.abSearch = {0}; + //pThis->Dbgc.cbSearch = 0; + pThis->Dbgc.cbSearchUnit = 1; + pThis->Dbgc.cMaxSearchHits = 1; + //pThis->Dbgc.SearchAddr = {0}; + //pThis->Dbgc.cbSearchRange = 0; + + //pThis->Dbgc.uInputZero = 0; + //pThis->Dbgc.iRead = 0; + //pThis->Dbgc.iWrite = 0; + //pThis->Dbgc.cInputLines = 0; + //pThis->Dbgc.fInputOverflow = false; + pThis->Dbgc.fReady = true; + pThis->Dbgc.pszScratch = &pThis->Dbgc.achScratch[0]; + //pThis->Dbgc.iArg = 0; + //pThis->Dbgc.rcOutput = 0; + //pThis->Dbgc.rcCmd = 0; + + //pThis->Dbgc.pszHistoryFile = NULL; + //pThis->Dbgc.pszGlobalInitScript = NULL; + //pThis->Dbgc.pszLocalInitScript = NULL; + + dbgcEvalInit(); + + pThis->fBreakinRecv = false; + pThis->fInVBoxDbg = false; + pThis->idPktNext = KD_PACKET_HDR_ID_INITIAL; + pThis->pIfWinNt = NULL; + pThis->f32Bit = false; + dbgcKdCtxPktRecvReset(pThis); + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aHwBp); i++) + { + PKDCTXHWBP pBp = &pThis->aHwBp[i]; + pBp->hDbgfBp = NIL_DBGFBP; + } + + dbgcKdCtxHwBpReset(pThis); + + *ppKdCtx = pThis; + return VINF_SUCCESS; +} + + +/** + * Destroys the given KD context. + * + * @param pThis The KD context to destroy. + */ +static void dbgcKdCtxDestroy(PKDCTX pThis) +{ + AssertPtr(pThis); + + pThis->pIfWinNt = NULL; + + /* Detach from the VM. */ + if (pThis->Dbgc.pUVM) + DBGFR3Detach(pThis->Dbgc.pUVM); + + /* Free config strings. */ + RTStrFree(pThis->Dbgc.pszGlobalInitScript); + pThis->Dbgc.pszGlobalInitScript = NULL; + RTStrFree(pThis->Dbgc.pszLocalInitScript); + pThis->Dbgc.pszLocalInitScript = NULL; + RTStrFree(pThis->Dbgc.pszHistoryFile); + pThis->Dbgc.pszHistoryFile = NULL; + + /* Finally, free the instance memory. */ + RTMemFree(pThis); +} + + +DECL_HIDDEN_CALLBACK(int) dbgcKdStubRunloop(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags) +{ + /* + * Validate input. + */ + AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE); + PVM pVM = NULL; + if (pUVM) + { + pVM = VMR3GetVM(pUVM); + AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE); + } + + /* + * Allocate and initialize instance data + */ + PKDCTX pThis; + int rc = dbgcKdCtxCreate(&pThis, pIo, fFlags); + if (RT_FAILURE(rc)) + return rc; + if (!HMR3IsEnabled(pUVM) && !NEMR3IsEnabled(pUVM)) + pThis->Dbgc.hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL; + + /* + * Attach to the specified VM. + */ + if (RT_SUCCESS(rc) && pUVM) + { + rc = DBGFR3Attach(pUVM); + if (RT_SUCCESS(rc)) + { + pThis->Dbgc.pVM = pVM; + pThis->Dbgc.pUVM = pUVM; + pThis->Dbgc.idCpu = 0; + } + else + rc = pThis->Dbgc.CmdHlp.pfnVBoxError(&pThis->Dbgc.CmdHlp, rc, "When trying to attach to VM %p\n", pThis->Dbgc.pVM); + } + + /* + * Load plugins. + */ + if (RT_SUCCESS(rc)) + { + if (pVM) + DBGFR3PlugInLoadAll(pThis->Dbgc.pUVM); + dbgcEventInit(&pThis->Dbgc); + + /* + * Run the debugger main loop. + */ + rc = dbgcKdRun(pThis); + dbgcEventTerm(&pThis->Dbgc); + } + + /* + * Cleanup console debugger session. + */ + dbgcKdCtxDestroy(pThis); + return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc; +} -- cgit v1.2.3