VirtualBox

source: vbox/trunk/src/VBox/Devices/Misc/DevVirtualKD.cpp@ 91886

Last change on this file since 91886 was 91886, checked in by vboxsync, 3 years ago

Devices/Misc/DevVirtualKD.cpp: Access CFGM API only through the device helper callbacks, bugref:10074

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: DevVirtualKD.cpp 91886 2021-10-20 12:01:58Z vboxsync $ */
2/** @file
3 * VirtualKD - Device stub/loader for fast Windows kernel-mode debugging.
4 *
5 * Contributed by: Ivan Shcherbakov
6 * Heavily modified after the contribution.
7 */
8
9/*
10 * Copyright (C) 2010-2020 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#define LOG_GROUP LOG_GROUP_DEV // LOG_GROUP_DEV_VIRTUALKD
26#include <VBox/vmm/pdmdev.h>
27#include <VBox/log.h>
28#include <iprt/assert.h>
29#include <iprt/mem.h>
30#include <iprt/path.h>
31
32#include "VBoxDD.h"
33
34
35/*********************************************************************************************************************************
36* Defined Constants And Macros *
37*********************************************************************************************************************************/
38#define IKDClient_InterfaceVersion 3
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44typedef struct VKDREQUESTHDR
45{
46 uint32_t cbData;
47 uint32_t cbReplyMax;
48} VKDREQUESTHDR;
49AssertCompileSize(VKDREQUESTHDR, 8);
50
51#pragma pack(1)
52typedef struct VKDREPLYHDR
53{
54 uint32_t cbData;
55 char chOne;
56 char chSpace;
57} VKDREPLYHDR;
58#pragma pack()
59AssertCompileSize(VKDREPLYHDR, 6);
60
61class IKDClient
62{
63public:
64 virtual unsigned OnRequest(const char *pRequestIncludingRpcHeader, unsigned RequestSizeWithRpcHeader, char **ppReply) = 0;
65 virtual ~IKDClient() {}
66};
67
68typedef IKDClient *(*PFNCreateVBoxKDClientEx)(unsigned version);
69
70typedef struct VIRTUALKD
71{
72 bool fOpenChannelDetected;
73 bool fChannelDetectSuccessful;
74 RTLDRMOD hLib;
75 IKDClient *pKDClient;
76 char *pbCmdBody;
77 bool fFencedCmdBody; /**< Set if pbCmdBody was allocated using RTMemPageAlloc rather than RTMemAlloc. */
78} VIRTUALKD;
79
80#define VIRTUALKB_CMDBODY_SIZE _256K /**< Size of buffer pointed to by VIRTUALKB::pbCmdBody */
81#define VIRTUALKB_CMDBODY_PRE_FENCE (PAGE_SIZE * 4) /**< Size of the eletrict fence before the command body. */
82#define VIRTUALKB_CMDBODY_POST_FENCE (PAGE_SIZE * 8) /**< Size of the eletrict fence after the command body. */
83
84
85
86
87static DECLCALLBACK(VBOXSTRICTRC) vkdPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
88{
89 RT_NOREF(pvUser, offPort, cb);
90 VIRTUALKD *pThis = PDMDEVINS_2_DATA(pDevIns, VIRTUALKD *);
91
92 if (pThis->fOpenChannelDetected)
93 {
94 *pu32 = RT_MAKE_U32_FROM_U8('V', 'B', 'O', 'X'); /* 'XOBV', checked in VMWRPC.H */
95 pThis->fOpenChannelDetected = false;
96 pThis->fChannelDetectSuccessful = true;
97 }
98 else
99 *pu32 = UINT32_MAX;
100
101 return VINF_SUCCESS;
102}
103
104static DECLCALLBACK(VBOXSTRICTRC) vkdPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
105{
106 RT_NOREF(pvUser, cb);
107 VIRTUALKD *pThis = PDMDEVINS_2_DATA(pDevIns, VIRTUALKD *);
108
109 if (offPort == 1)
110 {
111 /*
112 * Read the request and request body. Ignore empty requests.
113 */
114 RTGCPHYS GCPhys = u32;
115 VKDREQUESTHDR RequestHeader = { 0, 0 };
116 int rc = PDMDevHlpPhysRead(pDevIns, GCPhys, &RequestHeader, sizeof(RequestHeader));
117 if ( RT_SUCCESS(rc)
118 && RequestHeader.cbData > 0)
119 {
120 uint32_t cbData = RT_MIN(RequestHeader.cbData, VIRTUALKB_CMDBODY_SIZE);
121 rc = PDMDevHlpPhysRead(pDevIns, GCPhys + sizeof(RequestHeader), pThis->pbCmdBody, cbData);
122 if (RT_SUCCESS(rc))
123 {
124 /*
125 * Call the plugin module.
126 */
127 char *pbReply = NULL;
128 unsigned cbReply;
129 try
130 {
131 cbReply = pThis->pKDClient->OnRequest(pThis->pbCmdBody, cbData, &pbReply);
132 if (!pbReply)
133 cbReply = 0;
134 }
135 catch (...)
136 {
137 LogRel(("DevVirtualKB: OnRequest threw exception. sigh.\n"));
138 cbReply = 0;
139 pbReply = NULL;
140 }
141
142 /*
143 * Write the reply to guest memory (overwriting the request):
144 */
145 cbReply = RT_MIN(cbReply + 2, RequestHeader.cbReplyMax);
146 VKDREPLYHDR ReplyHeader;
147 ReplyHeader.cbData = cbReply; /* The '1' and ' ' bytes count towards reply size. */
148 ReplyHeader.chOne = '1';
149 ReplyHeader.chSpace = ' ';
150 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, &ReplyHeader, sizeof(ReplyHeader.cbData) + RT_MIN(cbReply, 2));
151 if (cbReply > 2 && RT_SUCCESS(rc))
152 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys + sizeof(ReplyHeader), pbReply, cbReply - 2);
153 }
154 }
155 }
156 else
157 {
158 Assert(offPort == 0);
159 if (u32 == UINT32_C(0x564D5868) /* 'VMXh' */)
160 pThis->fOpenChannelDetected = true;
161 else
162 pThis->fOpenChannelDetected = false;
163 }
164
165 return VINF_SUCCESS;
166}
167
168
169/**
170 * @interface_method_impl{PDMDEVREG,pfnDestruct}
171 */
172static DECLCALLBACK(int) vkdDestruct(PPDMDEVINS pDevIns)
173{
174 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
175 VIRTUALKD *pThis = PDMDEVINS_2_DATA(pDevIns, VIRTUALKD *);
176
177 if (pThis->pKDClient)
178 {
179 /** @todo r=bird: This interface is not safe as the object doesn't overload the
180 * delete operator, thus making our runtime free it rather than that of
181 * the plug-in module IIRC. */
182 delete pThis->pKDClient;
183 pThis->pKDClient = NULL;
184 }
185
186 if (pThis->hLib != NIL_RTLDRMOD)
187 {
188 RTLdrClose(pThis->hLib);
189 pThis->hLib = NIL_RTLDRMOD;
190 }
191
192 if (pThis->pbCmdBody)
193 {
194 if (pThis->fFencedCmdBody)
195 RTMemPageFree((uint8_t *)pThis->pbCmdBody - RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE),
196 RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE)
197 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE)
198 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_POST_FENCE, PAGE_SIZE));
199 else
200 RTMemFree(pThis->pbCmdBody);
201 pThis->pbCmdBody = NULL;
202 }
203
204 return VINF_SUCCESS;
205}
206
207
208/**
209 * @interface_method_impl{PDMDEVREG,pfnConstruct}
210 */
211static DECLCALLBACK(int) vkdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
212{
213 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
214 VIRTUALKD *pThis = PDMDEVINS_2_DATA(pDevIns, VIRTUALKD *);
215 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
216 RT_NOREF(iInstance);
217
218 pThis->fOpenChannelDetected = false;
219 pThis->fChannelDetectSuccessful = false;
220 pThis->hLib = NIL_RTLDRMOD;
221 pThis->pKDClient = NULL;
222 pThis->pbCmdBody = NULL;
223 pThis->fFencedCmdBody = false;
224
225 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Path", "");
226
227 /* This device is a bit unusual, after this point it will not fail to be
228 * constructed, but there will be a warning and it will not work. */
229
230 char szPath[RTPATH_MAX];
231 int rc = pHlp->pfnCFGMQueryStringDef(pCfg, "Path", szPath, sizeof(szPath) - sizeof("kdclient64.dll"), "");
232 if (RT_FAILURE(rc))
233 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Path\" value"));
234
235 rc = RTPathAppend(szPath, sizeof(szPath), HC_ARCH_BITS == 64 ? "kdclient64.dll" : "kdclient.dll");
236 AssertRCReturn(rc, rc);
237 rc = RTLdrLoad(szPath, &pThis->hLib);
238 if (RT_SUCCESS(rc))
239 {
240 PFNCreateVBoxKDClientEx pfnInit;
241 rc = RTLdrGetSymbol(pThis->hLib, "CreateVBoxKDClientEx", (void **)&pfnInit);
242 if (RT_SUCCESS(rc))
243 {
244 pThis->pKDClient = pfnInit(IKDClient_InterfaceVersion);
245 if (pThis->pKDClient)
246 {
247 /* We allocate a fenced buffer for reasons of paranoia. */
248 uint8_t *pbCmdBody = (uint8_t *)RTMemPageAlloc( RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE)
249 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE)
250 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_POST_FENCE, PAGE_SIZE));
251 if (pbCmdBody)
252 {
253 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE), RTMEM_PROT_NONE);
254 pbCmdBody += RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE);
255
256 pThis->fFencedCmdBody = true;
257 pThis->pbCmdBody = (char *)pbCmdBody;
258 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE), RTMEM_PROT_READ | RTMEM_PROT_WRITE);
259 AssertLogRelRC(rc);
260 pbCmdBody += RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE);
261
262 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE), RTMEM_PROT_NONE);
263 AssertLogRelRC(rc);
264 }
265 else
266 {
267 LogRel(("VirtualKB: RTMemPageAlloc failed, falling back on regular alloc.\n"));
268 pThis->pbCmdBody = (char *)RTMemAllocZ(VIRTUALKB_CMDBODY_SIZE);
269 AssertLogRelReturn(pThis->pbCmdBody, VERR_NO_MEMORY);
270 }
271
272 IOMIOPORTHANDLE hIoPorts;
273 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x5658 /*uPort*/, 2 /*cPorts*/, vkdPortWrite, vkdPortRead,
274 "VirtualKD", NULL /*paExtDescs*/, &hIoPorts);
275 AssertRCReturn(rc, rc);
276 }
277 else
278 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_INIT",
279 N_("Failed to initialize VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
280 }
281 else
282 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_SYMBOL",
283 N_("Failed to find entry point for VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
284 }
285 else
286 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_LOAD",
287 N_("Failed to load VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
288 return VINF_SUCCESS;
289}
290
291
292/**
293 * The device registration structure.
294 */
295const PDMDEVREG g_DeviceVirtualKD =
296{
297 /* .u32Version = */ PDM_DEVREG_VERSION,
298 /* .uReserved0 = */ 0,
299 /* .szName = */ "VirtualKD",
300 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
301 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
302 /* .cMaxInstances = */ 1,
303 /* .uSharedVersion = */ 42,
304 /* .cbInstanceShared = */ sizeof(VIRTUALKD),
305 /* .cbInstanceCC = */ 0,
306 /* .cbInstanceRC = */ 0,
307 /* .cMaxPciDevices = */ 0,
308 /* .cMaxMsixVectors = */ 0,
309 /* .pszDescription = */ "Provides fast debugging interface when debugging Windows kernel",
310#if defined(IN_RING3)
311 /* .pszRCMod = */ "",
312 /* .pszR0Mod = */ "",
313 /* .pfnConstruct = */ vkdConstruct,
314 /* .pfnDestruct = */ vkdDestruct,
315 /* .pfnRelocate = */ NULL,
316 /* pfnIOCtl */ NULL,
317 /* .pfnPowerOn = */ NULL,
318 /* .pfnReset = */ NULL,
319 /* .pfnSuspend = */ NULL,
320 /* .pfnResume = */ NULL,
321 /* .pfnAttach = */ NULL,
322 /* .pfnDetach = */ NULL,
323 /* .pfnQueryInterface = */ NULL,
324 /* .pfnInitComplete = */ NULL,
325 /* .pfnPowerOff = */ NULL,
326 /* .pfnSoftReset = */ NULL,
327 /* .pfnReserved0 = */ NULL,
328 /* .pfnReserved1 = */ NULL,
329 /* .pfnReserved2 = */ NULL,
330 /* .pfnReserved3 = */ NULL,
331 /* .pfnReserved4 = */ NULL,
332 /* .pfnReserved5 = */ NULL,
333 /* .pfnReserved6 = */ NULL,
334 /* .pfnReserved7 = */ NULL,
335#elif defined(IN_RING0)
336 /* .pfnEarlyConstruct = */ NULL,
337 /* .pfnConstruct = */ NULL,
338 /* .pfnDestruct = */ NULL,
339 /* .pfnFinalDestruct = */ NULL,
340 /* .pfnRequest = */ NULL,
341 /* .pfnReserved0 = */ NULL,
342 /* .pfnReserved1 = */ NULL,
343 /* .pfnReserved2 = */ NULL,
344 /* .pfnReserved3 = */ NULL,
345 /* .pfnReserved4 = */ NULL,
346 /* .pfnReserved5 = */ NULL,
347 /* .pfnReserved6 = */ NULL,
348 /* .pfnReserved7 = */ NULL,
349#elif defined(IN_RC)
350 /* .pfnConstruct = */ NULL,
351 /* .pfnReserved0 = */ NULL,
352 /* .pfnReserved1 = */ NULL,
353 /* .pfnReserved2 = */ NULL,
354 /* .pfnReserved3 = */ NULL,
355 /* .pfnReserved4 = */ NULL,
356 /* .pfnReserved5 = */ NULL,
357 /* .pfnReserved6 = */ NULL,
358 /* .pfnReserved7 = */ NULL,
359#else
360# error "Not in IN_RING3, IN_RING0 or IN_RC!"
361#endif
362 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
363};
364
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette