VirtualBox

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

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

Devices: Must not use PAGE_SIZE, PAGE_SHIFT, PAGE_OFFSET_MASK, PAGE_ADDRESS or PHYS_PAGE_ADDRESS here either. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: DevVirtualKD.cpp 93944 2022-02-24 21:15:14Z 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-2022 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 (HOST_PAGE_SIZE * 4) /**< Size of the eletrict fence before the command body. */
82#define VIRTUALKB_CMDBODY_POST_FENCE (HOST_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, HOST_PAGE_SIZE),
196 RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, HOST_PAGE_SIZE)
197 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, HOST_PAGE_SIZE)
198 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_POST_FENCE, HOST_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, HOST_PAGE_SIZE)
249 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, HOST_PAGE_SIZE)
250 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_POST_FENCE, HOST_PAGE_SIZE));
251 if (pbCmdBody)
252 {
253 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, HOST_PAGE_SIZE), RTMEM_PROT_NONE);
254 pbCmdBody += RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, HOST_PAGE_SIZE);
255
256 pThis->fFencedCmdBody = true;
257 pThis->pbCmdBody = (char *)pbCmdBody;
258 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, HOST_PAGE_SIZE),
259 RTMEM_PROT_READ | RTMEM_PROT_WRITE);
260 AssertLogRelRC(rc);
261 pbCmdBody += RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, HOST_PAGE_SIZE);
262
263 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, HOST_PAGE_SIZE),
264 RTMEM_PROT_NONE);
265 AssertLogRelRC(rc);
266 }
267 else
268 {
269 LogRel(("VirtualKB: RTMemPageAlloc failed, falling back on regular alloc.\n"));
270 pThis->pbCmdBody = (char *)RTMemAllocZ(VIRTUALKB_CMDBODY_SIZE);
271 AssertLogRelReturn(pThis->pbCmdBody, VERR_NO_MEMORY);
272 }
273
274 IOMIOPORTHANDLE hIoPorts;
275 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x5658 /*uPort*/, 2 /*cPorts*/, vkdPortWrite, vkdPortRead,
276 "VirtualKD", NULL /*paExtDescs*/, &hIoPorts);
277 AssertRCReturn(rc, rc);
278 }
279 else
280 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_INIT",
281 N_("Failed to initialize VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
282 }
283 else
284 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_SYMBOL",
285 N_("Failed to find entry point for VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
286 }
287 else
288 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_LOAD",
289 N_("Failed to load VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
290 return VINF_SUCCESS;
291}
292
293
294/**
295 * The device registration structure.
296 */
297const PDMDEVREG g_DeviceVirtualKD =
298{
299 /* .u32Version = */ PDM_DEVREG_VERSION,
300 /* .uReserved0 = */ 0,
301 /* .szName = */ "VirtualKD",
302 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
303 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
304 /* .cMaxInstances = */ 1,
305 /* .uSharedVersion = */ 42,
306 /* .cbInstanceShared = */ sizeof(VIRTUALKD),
307 /* .cbInstanceCC = */ 0,
308 /* .cbInstanceRC = */ 0,
309 /* .cMaxPciDevices = */ 0,
310 /* .cMaxMsixVectors = */ 0,
311 /* .pszDescription = */ "Provides fast debugging interface when debugging Windows kernel",
312#if defined(IN_RING3)
313 /* .pszRCMod = */ "",
314 /* .pszR0Mod = */ "",
315 /* .pfnConstruct = */ vkdConstruct,
316 /* .pfnDestruct = */ vkdDestruct,
317 /* .pfnRelocate = */ NULL,
318 /* pfnIOCtl */ NULL,
319 /* .pfnPowerOn = */ NULL,
320 /* .pfnReset = */ NULL,
321 /* .pfnSuspend = */ NULL,
322 /* .pfnResume = */ NULL,
323 /* .pfnAttach = */ NULL,
324 /* .pfnDetach = */ NULL,
325 /* .pfnQueryInterface = */ NULL,
326 /* .pfnInitComplete = */ NULL,
327 /* .pfnPowerOff = */ NULL,
328 /* .pfnSoftReset = */ NULL,
329 /* .pfnReserved0 = */ NULL,
330 /* .pfnReserved1 = */ NULL,
331 /* .pfnReserved2 = */ NULL,
332 /* .pfnReserved3 = */ NULL,
333 /* .pfnReserved4 = */ NULL,
334 /* .pfnReserved5 = */ NULL,
335 /* .pfnReserved6 = */ NULL,
336 /* .pfnReserved7 = */ NULL,
337#elif defined(IN_RING0)
338 /* .pfnEarlyConstruct = */ NULL,
339 /* .pfnConstruct = */ NULL,
340 /* .pfnDestruct = */ NULL,
341 /* .pfnFinalDestruct = */ NULL,
342 /* .pfnRequest = */ NULL,
343 /* .pfnReserved0 = */ NULL,
344 /* .pfnReserved1 = */ NULL,
345 /* .pfnReserved2 = */ NULL,
346 /* .pfnReserved3 = */ NULL,
347 /* .pfnReserved4 = */ NULL,
348 /* .pfnReserved5 = */ NULL,
349 /* .pfnReserved6 = */ NULL,
350 /* .pfnReserved7 = */ NULL,
351#elif defined(IN_RC)
352 /* .pfnConstruct = */ 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#else
362# error "Not in IN_RING3, IN_RING0 or IN_RC!"
363#endif
364 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
365};
366
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