VirtualBox

source: vbox/trunk/src/VBox/Devices/GIMDev/GIMDev.cpp@ 58424

Last change on this file since 58424 was 58390, checked in by vboxsync, 9 years ago

VMM/GIM: Implement Hyper-V debug receive thread optimization. Added a few statistics to GIM.
Fixed a bug in Windows guests' debug DHCP handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: GIMDev.cpp 58390 2015-10-23 12:35:35Z vboxsync $ */
2/** @file
3 * Guest Interface Manager Device.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_GIM
23#include <VBox/vmm/pdmdev.h>
24#include <VBox/vmm/gim.h>
25#include <VBox/vmm/vm.h>
26
27#include "VBoxDD.h"
28#include <iprt/alloc.h>
29#include <iprt/semaphore.h>
30#include <iprt/uuid.h>
31
32#define GIMDEV_DEBUG_LUN 998
33
34/**
35 * GIM device.
36 */
37typedef struct GIMDEV
38{
39 /** Pointer to the device instance - R3 Ptr. */
40 PPDMDEVINSR3 pDevInsR3;
41 /** Pointer to the device instance - R0 Ptr. */
42 PPDMDEVINSR0 pDevInsR0;
43 /** Pointer to the device instance - RC Ptr. */
44 PPDMDEVINSRC pDevInsRC;
45 /** Alignment. */
46 RTRCPTR Alignment0;
47
48 /** LUN\#998: The debug interface. */
49 PDMIBASE IDbgBase;
50 /** LUN\#998: The stream port interface. */
51 PDMISTREAM IDbgStreamPort;
52 /** Pointer to the attached base debug driver. */
53 R3PTRTYPE(PPDMIBASE) pDbgDrvBase;
54 /** The debug receive thread. */
55 RTTHREAD hDbgRecvThread;
56 /** Flag to indicate shutdown of the debug receive thread. */
57 bool volatile fDbgRecvThreadShutdown;
58 /** The debug setup parameters. */
59 GIMDEBUGSETUP DbgSetup;
60 /** The debug transfer struct. */
61 GIMDEBUG Dbg;
62} GIMDEV;
63/** Pointer to the GIM device state. */
64typedef GIMDEV *PGIMDEV;
65AssertCompileMemberAlignment(GIMDEV, IDbgBase, 8);
66
67#ifndef VBOX_DEVICE_STRUCT_TESTCASE
68
69#ifdef IN_RING3
70
71
72/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#GIMDEV_DEBUG_LUN -=-=-=-=-=-=-=-=- */
73
74/**
75 * @interface_method_impl{PDMIBASE, pfnQueryInterface}
76 */
77static DECLCALLBACK(void *) gimdevR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
78{
79 PGIMDEV pThis = RT_FROM_MEMBER(pInterface, GIMDEV, IDbgBase);
80 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IDbgBase);
81 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IDbgStreamPort);
82 return NULL;
83}
84
85
86static DECLCALLBACK(int) gimDevR3DbgRecvThread(RTTHREAD ThreadSelf, void *pvUser)
87{
88 /*
89 * Validate.
90 */
91 PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
92 AssertReturn(pDevIns, VERR_INVALID_PARAMETER);
93 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
94
95 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
96 AssertReturn(pThis, VERR_INVALID_POINTER);
97 AssertReturn(pThis->DbgSetup.cbDbgRecvBuf, VERR_INTERNAL_ERROR);
98 AssertReturn(pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENTMULTI, VERR_INTERNAL_ERROR_2);
99 AssertReturn(pThis->Dbg.pvDbgRecvBuf, VERR_INTERNAL_ERROR_3);
100
101 PVM pVM = PDMDevHlpGetVM(pDevIns);
102 AssertReturn(pVM, VERR_INVALID_POINTER);
103
104 PPDMISTREAM pDbgDrvStream = pThis->Dbg.pDbgDrvStream;
105 AssertReturn(pDbgDrvStream, VERR_INVALID_POINTER);
106
107 for (;;)
108 {
109 /*
110 * Read incoming debug data.
111 */
112 size_t cbRead = pThis->DbgSetup.cbDbgRecvBuf;
113 int rc = pDbgDrvStream->pfnRead(pDbgDrvStream, pThis->Dbg.pvDbgRecvBuf, &cbRead);
114 if ( RT_SUCCESS(rc)
115 && cbRead > 0)
116 {
117 /*
118 * Notify the consumer thread.
119 */
120 if (ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == false)
121 {
122 if (pThis->DbgSetup.pfnDbgRecvBufAvail)
123 pThis->DbgSetup.pfnDbgRecvBufAvail(pVM);
124 pThis->Dbg.cbDbgRecvBufRead = cbRead;
125 RTSemEventMultiReset(pThis->Dbg.hDbgRecvThreadSem);
126 ASMAtomicWriteBool(&pThis->Dbg.fDbgRecvBufRead, true);
127 }
128
129 /*
130 * Wait until the consumer thread has acknowledged reading of the
131 * current buffer or we're asked to shut down.
132 *
133 * It is important that we do NOT re-invoke 'pfnRead' before the
134 * current buffer is consumed, otherwise we risk data corruption.
135 */
136 while ( ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == true
137 && !pThis->fDbgRecvThreadShutdown)
138 {
139 RTSemEventMultiWait(pThis->Dbg.hDbgRecvThreadSem, RT_INDEFINITE_WAIT);
140 }
141 }
142 else if ( rc != VINF_TRY_AGAIN
143 && rc != VERR_TRY_AGAIN)
144 {
145 LogRel(("GIMDev: Debug thread terminating with rc=%Rrc\n", rc));
146 break;
147 }
148
149 if (pThis->fDbgRecvThreadShutdown)
150 {
151 LogRel(("GIMDev: Debug thread shutting down\n"));
152 break;
153 }
154 }
155
156 return VINF_SUCCESS;
157}
158
159
160/**
161 * @interface_method_impl{PDMDEVREG,pfnConstruct}
162 */
163static DECLCALLBACK(int) gimdevR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
164{
165 Assert(iInstance == 0);
166 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
167 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
168
169 /*
170 * Initialize relevant state bits.
171 */
172 pThis->pDevInsR3 = pDevIns;
173 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
174 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
175
176 /*
177 * Get debug setup requirements from GIM.
178 */
179 PVM pVM = PDMDevHlpGetVM(pDevIns);
180 int rc = GIMR3GetDebugSetup(pVM, &pThis->DbgSetup);
181 if ( RT_SUCCESS(rc)
182 && pThis->DbgSetup.cbDbgRecvBuf > 0)
183 {
184 /*
185 * Attach the stream driver for the debug connection.
186 */
187 PPDMISTREAM pDbgDrvStream = NULL;
188 pThis->IDbgBase.pfnQueryInterface = gimdevR3QueryInterface;
189 rc = PDMDevHlpDriverAttach(pDevIns, GIMDEV_DEBUG_LUN, &pThis->IDbgBase, &pThis->pDbgDrvBase, "GIM Debug Port");
190 if (RT_SUCCESS(rc))
191 {
192 pDbgDrvStream = PDMIBASE_QUERY_INTERFACE(pThis->pDbgDrvBase, PDMISTREAM);
193 if (pDbgDrvStream)
194 LogRel(("GIMDev: LUN#%u: Debug port configured\n", GIMDEV_DEBUG_LUN));
195 else
196 LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN));
197 }
198 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
199 {
200 pThis->pDbgDrvBase = NULL;
201 LogRel(("GIMDev: LUN#%u: No debug port configured\n", GIMDEV_DEBUG_LUN));
202 }
203 else
204 {
205 AssertLogRelMsgFailed(("GIMDev: LUN#%u: Failed to attach to driver on debug port. rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc));
206 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
207 return rc;
208 }
209
210 void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf);
211 if (RT_UNLIKELY(!pvDbgRecvBuf))
212 {
213 LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf));
214 return VERR_NO_MEMORY;
215 }
216
217 /*
218 * Update the shared debug struct.
219 */
220 pThis->Dbg.pDbgDrvStream = pDbgDrvStream;
221 pThis->Dbg.pvDbgRecvBuf = pvDbgRecvBuf;
222 pThis->Dbg.cbDbgRecvBufRead = 0;
223 pThis->Dbg.fDbgRecvBufRead = false;
224
225 /*
226 * Create the sempahore and the debug receive thread itself.
227 */
228 rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem);
229 if (RT_SUCCESS(rc))
230 {
231 rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO,
232 RTTHREADFLAGS_WAITABLE, "GIMDebugRecv");
233 if (RT_FAILURE(rc))
234 {
235 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
236 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
237
238 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
239 pThis->Dbg.pvDbgRecvBuf = NULL;
240 return rc;
241 }
242 }
243 else
244 return rc;
245 }
246
247 /*
248 * Register this device with the GIM component.
249 */
250 GIMR3GimDeviceRegister(pVM, pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL);
251
252 /*
253 * Get the MMIO2 regions from the GIM provider.
254 */
255 uint32_t cRegions = 0;
256 PGIMMMIO2REGION pRegionsR3 = GIMR3GetMmio2Regions(pVM, &cRegions);
257 if ( cRegions
258 && pRegionsR3)
259 {
260 /*
261 * Register the MMIO2 regions.
262 */
263 PGIMMMIO2REGION pCur = pRegionsR3;
264 for (uint32_t i = 0; i < cRegions; i++, pCur++)
265 {
266 Assert(!pCur->fRegistered);
267 rc = PDMDevHlpMMIO2Register(pDevIns, pCur->iRegion, pCur->cbRegion, 0 /* fFlags */, &pCur->pvPageR3,
268 pCur->szDescription);
269 if (RT_FAILURE(rc))
270 return rc;
271
272 pCur->fRegistered = true;
273
274#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
275 RTR0PTR pR0Mapping = 0;
276 rc = PDMDevHlpMMIO2MapKernel(pDevIns, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
277 &pR0Mapping);
278 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
279 pCur->pvPageR0 = pR0Mapping;
280#else
281 pCur->pvPageR0 = (RTR0PTR)pCur->pvPageR3;
282#endif
283
284 /*
285 * Map into RC if required.
286 */
287 if (pCur->fRCMapping)
288 {
289 RTRCPTR pRCMapping = 0;
290 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
291 &pRCMapping);
292 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
293 pCur->pvPageRC = pRCMapping;
294 }
295 else
296 pCur->pvPageRC = NIL_RTRCPTR;
297
298 LogRel(("GIMDev: Registered %s\n", pCur->szDescription));
299 }
300 }
301
302 /** @todo Register SSM: PDMDevHlpSSMRegister(). */
303 /** @todo Register statistics: STAM_REG(). */
304 /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */
305
306 return VINF_SUCCESS;
307}
308
309
310/**
311 * @interface_method_impl{PDMDEVREG,pfnDestruct}
312 */
313static DECLCALLBACK(int) gimdevR3Destruct(PPDMDEVINS pDevIns)
314{
315 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
316 PVM pVM = PDMDevHlpGetVM(pDevIns);
317 uint32_t cRegions = 0;
318
319 PGIMMMIO2REGION pCur = GIMR3GetMmio2Regions(pVM, &cRegions);
320 for (uint32_t i = 0; i < cRegions; i++, pCur++)
321 {
322 int rc = PDMDevHlpMMIO2Deregister(pDevIns, pCur->iRegion);
323 if (RT_FAILURE(rc))
324 return rc;
325 }
326
327 /*
328 * Signal and wait for the debug thread to terminate.
329 */
330 if (pThis->hDbgRecvThread != NIL_RTTHREAD)
331 {
332 pThis->fDbgRecvThreadShutdown = true;
333 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
334 RTSemEventMultiSignal(pThis->Dbg.hDbgRecvThreadSem);
335
336 int rc = RTThreadWait(pThis->hDbgRecvThread, 20000, NULL /*prc*/);
337 if (RT_SUCCESS(rc))
338 pThis->hDbgRecvThread = NIL_RTTHREAD;
339 else
340 {
341 LogRel(("GIMDev: Debug thread did not terminate, rc=%Rrc!\n", rc));
342 return VERR_RESOURCE_BUSY;
343 }
344 }
345
346 /*
347 * Now clean up the semaphore & buffer now that the thread is gone.
348 */
349 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
350 {
351 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
352 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
353 }
354 if (pThis->Dbg.pvDbgRecvBuf)
355 {
356 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
357 pThis->Dbg.pvDbgRecvBuf = NULL;
358 }
359
360 return VINF_SUCCESS;
361}
362
363
364/**
365 * @interface_method_impl{PDMDEVREG,pfnRelocate}
366 */
367static DECLCALLBACK(void) gimdevR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
368{
369 NOREF(pDevIns);
370 NOREF(offDelta);
371}
372
373
374/**
375 * @interface_method_impl{PDMDEVREG,pfnReset}
376 */
377static DECLCALLBACK(void) gimdevR3Reset(PPDMDEVINS pDevIns)
378{
379 NOREF(pDevIns);
380 /* We do not deregister any MMIO2 regions as the regions are expected to be static. */
381}
382
383
384/**
385 * The device registration structure.
386 */
387const PDMDEVREG g_DeviceGIMDev =
388{
389 /* u32Version */
390 PDM_DEVREG_VERSION,
391 /* szName */
392 "GIMDev",
393 /* szRCMod */
394 "VBoxDDRC.rc",
395 /* szR0Mod */
396 "VBoxDDR0.r0",
397 /* pszDescription */
398 "VirtualBox GIM Device",
399 /* fFlags */
400 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_RC,
401 /* fClass */
402 PDM_DEVREG_CLASS_MISC,
403 /* cMaxInstances */
404 1,
405 /* cbInstance */
406 sizeof(GIMDEV),
407 /* pfnConstruct */
408 gimdevR3Construct,
409 /* pfnDestruct */
410 gimdevR3Destruct,
411 /* pfnRelocate */
412 gimdevR3Relocate,
413 /* pfnMemSetup */
414 NULL,
415 /* pfnPowerOn */
416 NULL,
417 /* pfnReset */
418 gimdevR3Reset,
419 /* pfnSuspend */
420 NULL,
421 /* pfnResume */
422 NULL,
423 /* pfnAttach */
424 NULL,
425 /* pfnDetach */
426 NULL,
427 /* pfnQueryInterface. */
428 NULL,
429 /* pfnInitComplete */
430 NULL,
431 /* pfnPowerOff */
432 NULL,
433 /* pfnSoftReset */
434 NULL,
435 /* u32VersionEnd */
436 PDM_DEVREG_VERSION
437};
438#endif /* IN_RING3 */
439
440#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
441
Note: See TracBrowser for help on using the repository browser.

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