VirtualBox

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

Last change on this file since 91687 was 87236, checked in by vboxsync, 4 years ago

DevVirtualKD: typo fix

  • 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 87236 2021-01-13 13:29:01Z 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 RT_NOREF(iInstance);
216
217 pThis->fOpenChannelDetected = false;
218 pThis->fChannelDetectSuccessful = false;
219 pThis->hLib = NIL_RTLDRMOD;
220 pThis->pKDClient = NULL;
221 pThis->pbCmdBody = NULL;
222 pThis->fFencedCmdBody = false;
223
224 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Path", "");
225
226 /* This device is a bit unusual, after this point it will not fail to be
227 * constructed, but there will be a warning and it will not work. */
228
229 char szPath[RTPATH_MAX];
230 int rc = CFGMR3QueryStringDef(pCfg, "Path", szPath, sizeof(szPath) - sizeof("kdclient64.dll"), "");
231 if (RT_FAILURE(rc))
232 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Path\" value"));
233
234 rc = RTPathAppend(szPath, sizeof(szPath), HC_ARCH_BITS == 64 ? "kdclient64.dll" : "kdclient.dll");
235 AssertRCReturn(rc, rc);
236 rc = RTLdrLoad(szPath, &pThis->hLib);
237 if (RT_SUCCESS(rc))
238 {
239 PFNCreateVBoxKDClientEx pfnInit;
240 rc = RTLdrGetSymbol(pThis->hLib, "CreateVBoxKDClientEx", (void **)&pfnInit);
241 if (RT_SUCCESS(rc))
242 {
243 pThis->pKDClient = pfnInit(IKDClient_InterfaceVersion);
244 if (pThis->pKDClient)
245 {
246 /* We allocate a fenced buffer for reasons of paranoia. */
247 uint8_t *pbCmdBody = (uint8_t *)RTMemPageAlloc( RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE)
248 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE)
249 + RT_ALIGN_Z(VIRTUALKB_CMDBODY_POST_FENCE, PAGE_SIZE));
250 if (pbCmdBody)
251 {
252 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE), RTMEM_PROT_NONE);
253 pbCmdBody += RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE);
254
255 pThis->fFencedCmdBody = true;
256 pThis->pbCmdBody = (char *)pbCmdBody;
257 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE), RTMEM_PROT_READ | RTMEM_PROT_WRITE);
258 AssertLogRelRC(rc);
259 pbCmdBody += RT_ALIGN_Z(VIRTUALKB_CMDBODY_SIZE, PAGE_SIZE);
260
261 rc = RTMemProtect(pbCmdBody, RT_ALIGN_Z(VIRTUALKB_CMDBODY_PRE_FENCE, PAGE_SIZE), RTMEM_PROT_NONE);
262 AssertLogRelRC(rc);
263 }
264 else
265 {
266 LogRel(("VirtualKB: RTMemPageAlloc failed, falling back on regular alloc.\n"));
267 pThis->pbCmdBody = (char *)RTMemAllocZ(VIRTUALKB_CMDBODY_SIZE);
268 AssertLogRelReturn(pThis->pbCmdBody, VERR_NO_MEMORY);
269 }
270
271 IOMIOPORTHANDLE hIoPorts;
272 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x5658 /*uPort*/, 2 /*cPorts*/, vkdPortWrite, vkdPortRead,
273 "VirtualKD", NULL /*paExtDescs*/, &hIoPorts);
274 AssertRCReturn(rc, rc);
275 }
276 else
277 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_INIT",
278 N_("Failed to initialize VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
279 }
280 else
281 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_SYMBOL",
282 N_("Failed to find entry point for VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
283 }
284 else
285 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /* fFlags */, "VirtualKD_LOAD",
286 N_("Failed to load VirtualKD library '%s'. Fast kernel-mode debugging will not work"), szPath);
287 return VINF_SUCCESS;
288}
289
290
291/**
292 * The device registration structure.
293 */
294const PDMDEVREG g_DeviceVirtualKD =
295{
296 /* .u32Version = */ PDM_DEVREG_VERSION,
297 /* .uReserved0 = */ 0,
298 /* .szName = */ "VirtualKD",
299 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
300 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
301 /* .cMaxInstances = */ 1,
302 /* .uSharedVersion = */ 42,
303 /* .cbInstanceShared = */ sizeof(VIRTUALKD),
304 /* .cbInstanceCC = */ 0,
305 /* .cbInstanceRC = */ 0,
306 /* .cMaxPciDevices = */ 0,
307 /* .cMaxMsixVectors = */ 0,
308 /* .pszDescription = */ "Provides fast debugging interface when debugging Windows kernel",
309#if defined(IN_RING3)
310 /* .pszRCMod = */ "",
311 /* .pszR0Mod = */ "",
312 /* .pfnConstruct = */ vkdConstruct,
313 /* .pfnDestruct = */ vkdDestruct,
314 /* .pfnRelocate = */ NULL,
315 /* pfnIOCtl */ NULL,
316 /* .pfnPowerOn = */ NULL,
317 /* .pfnReset = */ NULL,
318 /* .pfnSuspend = */ NULL,
319 /* .pfnResume = */ NULL,
320 /* .pfnAttach = */ NULL,
321 /* .pfnDetach = */ NULL,
322 /* .pfnQueryInterface = */ NULL,
323 /* .pfnInitComplete = */ NULL,
324 /* .pfnPowerOff = */ NULL,
325 /* .pfnSoftReset = */ NULL,
326 /* .pfnReserved0 = */ NULL,
327 /* .pfnReserved1 = */ NULL,
328 /* .pfnReserved2 = */ NULL,
329 /* .pfnReserved3 = */ NULL,
330 /* .pfnReserved4 = */ NULL,
331 /* .pfnReserved5 = */ NULL,
332 /* .pfnReserved6 = */ NULL,
333 /* .pfnReserved7 = */ NULL,
334#elif defined(IN_RING0)
335 /* .pfnEarlyConstruct = */ NULL,
336 /* .pfnConstruct = */ NULL,
337 /* .pfnDestruct = */ NULL,
338 /* .pfnFinalDestruct = */ NULL,
339 /* .pfnRequest = */ NULL,
340 /* .pfnReserved0 = */ NULL,
341 /* .pfnReserved1 = */ NULL,
342 /* .pfnReserved2 = */ NULL,
343 /* .pfnReserved3 = */ NULL,
344 /* .pfnReserved4 = */ NULL,
345 /* .pfnReserved5 = */ NULL,
346 /* .pfnReserved6 = */ NULL,
347 /* .pfnReserved7 = */ NULL,
348#elif defined(IN_RC)
349 /* .pfnConstruct = */ NULL,
350 /* .pfnReserved0 = */ NULL,
351 /* .pfnReserved1 = */ NULL,
352 /* .pfnReserved2 = */ NULL,
353 /* .pfnReserved3 = */ NULL,
354 /* .pfnReserved4 = */ NULL,
355 /* .pfnReserved5 = */ NULL,
356 /* .pfnReserved6 = */ NULL,
357 /* .pfnReserved7 = */ NULL,
358#else
359# error "Not in IN_RING3, IN_RING0 or IN_RC!"
360#endif
361 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
362};
363
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