VirtualBox

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

Last change on this file since 100836 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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