VirtualBox

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

Last change on this file since 81591 was 81591, checked in by vboxsync, 5 years ago

Devices: Use PDMDEVINS_2_DATA and PDMDEVINS_2_DATA. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: GIMDev.cpp 81591 2019-10-30 14:14:10Z vboxsync $ */
2/** @file
3 * Guest Interface Manager Device.
4 */
5
6/*
7 * Copyright (C) 2014-2019 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
26#include "VBoxDD.h"
27#include <iprt/alloc.h>
28#include <iprt/semaphore.h>
29#include <iprt/uuid.h>
30
31#define GIMDEV_DEBUG_LUN 998
32
33/**
34 * GIM device.
35 */
36typedef struct GIMDEV
37{
38 /** Pointer to the device instance - R3 Ptr. */
39 PPDMDEVINSR3 pDevInsR3;
40 /** Pointer to the device instance - R0 Ptr. */
41 PPDMDEVINSR0 pDevInsR0;
42 /** Pointer to the device instance - RC Ptr. */
43 PPDMDEVINSRC pDevInsRC;
44 /** Alignment. */
45 RTRCPTR Alignment0;
46
47 /** LUN\#998: The debug interface. */
48 PDMIBASE IDbgBase;
49 /** LUN\#998: The stream port interface. */
50 PDMISTREAM IDbgStreamPort;
51 /** Pointer to the attached base debug driver. */
52 R3PTRTYPE(PPDMIBASE) pDbgDrvBase;
53 /** The debug receive thread. */
54 RTTHREAD hDbgRecvThread;
55 /** Flag to indicate shutdown of the debug receive thread. */
56 bool volatile fDbgRecvThreadShutdown;
57 /** The debug setup parameters. */
58 GIMDEBUGSETUP DbgSetup;
59 /** The debug transfer struct. */
60 GIMDEBUG Dbg;
61} GIMDEV;
62/** Pointer to the GIM device state. */
63typedef GIMDEV *PGIMDEV;
64AssertCompileMemberAlignment(GIMDEV, IDbgBase, 8);
65
66#ifndef VBOX_DEVICE_STRUCT_TESTCASE
67
68#ifdef IN_RING3
69
70
71/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#GIMDEV_DEBUG_LUN -=-=-=-=-=-=-=-=- */
72
73/**
74 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
75 */
76static DECLCALLBACK(void *) gimdevR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
77{
78 PGIMDEV pThis = RT_FROM_MEMBER(pInterface, GIMDEV, IDbgBase);
79 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IDbgBase);
80 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IDbgStreamPort);
81 return NULL;
82}
83
84
85static DECLCALLBACK(int) gimDevR3DbgRecvThread(RTTHREAD hThreadSelf, void *pvUser)
86{
87 RT_NOREF1(hThreadSelf);
88
89 /*
90 * Validate.
91 */
92 PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
93 AssertReturn(pDevIns, VERR_INVALID_PARAMETER);
94 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
95
96 PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV);
97 AssertReturn(pThis, VERR_INVALID_POINTER);
98 AssertReturn(pThis->DbgSetup.cbDbgRecvBuf, VERR_INTERNAL_ERROR);
99 AssertReturn(pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENTMULTI, VERR_INTERNAL_ERROR_2);
100 AssertReturn(pThis->Dbg.pvDbgRecvBuf, VERR_INTERNAL_ERROR_3);
101
102 PVM pVM = PDMDevHlpGetVM(pDevIns);
103 AssertReturn(pVM, VERR_INVALID_POINTER);
104
105 PPDMISTREAM pDbgDrvStream = pThis->Dbg.pDbgDrvStream;
106 AssertReturn(pDbgDrvStream, VERR_INVALID_POINTER);
107
108 for (;;)
109 {
110 /*
111 * Read incoming debug data.
112 */
113 size_t cbRead = pThis->DbgSetup.cbDbgRecvBuf;
114 int rc = pDbgDrvStream->pfnRead(pDbgDrvStream, pThis->Dbg.pvDbgRecvBuf, &cbRead);
115 if ( RT_SUCCESS(rc)
116 && cbRead > 0)
117 {
118 /*
119 * Notify the consumer thread.
120 */
121 if (ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == false)
122 {
123 if (pThis->DbgSetup.pfnDbgRecvBufAvail)
124 pThis->DbgSetup.pfnDbgRecvBufAvail(pVM);
125 pThis->Dbg.cbDbgRecvBufRead = cbRead;
126 RTSemEventMultiReset(pThis->Dbg.hDbgRecvThreadSem);
127 ASMAtomicWriteBool(&pThis->Dbg.fDbgRecvBufRead, true);
128 }
129
130 /*
131 * Wait until the consumer thread has acknowledged reading of the
132 * current buffer or we're asked to shut down.
133 *
134 * It is important that we do NOT re-invoke 'pfnRead' before the
135 * current buffer is consumed, otherwise we risk data corruption.
136 */
137 while ( ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == true
138 && !pThis->fDbgRecvThreadShutdown)
139 {
140 RTSemEventMultiWait(pThis->Dbg.hDbgRecvThreadSem, RT_INDEFINITE_WAIT);
141 }
142 }
143#ifdef RT_OS_LINUX
144 else if (rc == VERR_NET_CONNECTION_REFUSED)
145 {
146 /*
147 * With the current, simplistic PDMISTREAM interface, this is the best we can do.
148 * Even using RTSocketSelectOne[Ex] on Linux returns immediately with 'ready-to-read'
149 * on localhost UDP sockets that are not connected on the other end.
150 */
151 /** @todo Fix socket waiting semantics on localhost Linux unconnected UDP sockets. */
152 RTThreadSleep(400);
153 }
154#endif
155 else if ( rc != VINF_TRY_AGAIN
156 && rc != VERR_TRY_AGAIN
157 && rc != VERR_NET_CONNECTION_RESET_BY_PEER)
158 {
159 LogRel(("GIMDev: Debug thread terminating with rc=%Rrc\n", rc));
160 break;
161 }
162
163 if (pThis->fDbgRecvThreadShutdown)
164 {
165 LogRel(("GIMDev: Debug thread shutting down\n"));
166 break;
167 }
168 }
169
170 return VINF_SUCCESS;
171}
172
173/**
174 * @interface_method_impl{PDMDEVREG,pfnReset}
175 */
176static DECLCALLBACK(void) gimdevR3Reset(PPDMDEVINS pDevIns)
177{
178 NOREF(pDevIns);
179 /* We do not deregister any MMIO2 regions as the regions are expected to be static. */
180}
181
182
183
184/**
185 * @interface_method_impl{PDMDEVREG,pfnRelocate}
186 */
187static DECLCALLBACK(void) gimdevR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
188{
189 NOREF(pDevIns);
190 NOREF(offDelta);
191#ifdef VBOX_WITH_RAW_MODE_KEEP
192# error relocate pvPageRC
193#endif
194}
195
196
197/**
198 * @interface_method_impl{PDMDEVREG,pfnConstruct}
199 */
200static DECLCALLBACK(int) gimdevR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
201{
202 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
203 RT_NOREF2(iInstance, pCfg);
204 Assert(iInstance == 0);
205 PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV);
206
207 /*
208 * Initialize relevant state bits.
209 */
210 pThis->pDevInsR3 = pDevIns;
211 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
212 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
213
214 /*
215 * Get debug setup requirements from GIM.
216 */
217 PVM pVM = PDMDevHlpGetVM(pDevIns);
218 int rc = GIMR3GetDebugSetup(pVM, &pThis->DbgSetup);
219 if ( RT_SUCCESS(rc)
220 && pThis->DbgSetup.cbDbgRecvBuf > 0)
221 {
222 /*
223 * Attach the stream driver for the debug connection.
224 */
225 PPDMISTREAM pDbgDrvStream = NULL;
226 pThis->IDbgBase.pfnQueryInterface = gimdevR3QueryInterface;
227 rc = PDMDevHlpDriverAttach(pDevIns, GIMDEV_DEBUG_LUN, &pThis->IDbgBase, &pThis->pDbgDrvBase, "GIM Debug Port");
228 if (RT_SUCCESS(rc))
229 {
230 pDbgDrvStream = PDMIBASE_QUERY_INTERFACE(pThis->pDbgDrvBase, PDMISTREAM);
231 if (pDbgDrvStream)
232 LogRel(("GIMDev: LUN#%u: Debug port configured\n", GIMDEV_DEBUG_LUN));
233 else
234 {
235 LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN));
236 rc = VERR_INTERNAL_ERROR_2;
237 }
238 }
239 else
240 {
241 pThis->pDbgDrvBase = NULL;
242 LogRel(("GIMDev: LUN#%u: No debug port configured! rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc));
243 }
244
245 if (!pDbgDrvStream)
246 {
247 Assert(rc != VINF_SUCCESS);
248 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
249 N_("Debug port configuration expected when GIM configured with debugging support"));
250 }
251
252 void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf);
253 if (RT_UNLIKELY(!pvDbgRecvBuf))
254 {
255 LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf));
256 return VERR_NO_MEMORY;
257 }
258
259 /*
260 * Update the shared debug struct.
261 */
262 pThis->Dbg.pDbgDrvStream = pDbgDrvStream;
263 pThis->Dbg.pvDbgRecvBuf = pvDbgRecvBuf;
264 pThis->Dbg.cbDbgRecvBufRead = 0;
265 pThis->Dbg.fDbgRecvBufRead = false;
266
267 /*
268 * Create the sempahore and the debug receive thread itself.
269 */
270 rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem);
271 if (RT_SUCCESS(rc))
272 {
273 rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO,
274 RTTHREADFLAGS_WAITABLE, "GIMDebugRecv");
275 if (RT_FAILURE(rc))
276 {
277 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
278 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
279
280 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
281 pThis->Dbg.pvDbgRecvBuf = NULL;
282 return rc;
283 }
284 }
285 else
286 return rc;
287 }
288
289 /*
290 * Register this device with the GIM component.
291 */
292 GIMR3GimDeviceRegister(pVM, pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL);
293
294 /*
295 * Get the MMIO2 regions from the GIM provider.
296 */
297 uint32_t cRegions = 0;
298 PGIMMMIO2REGION pRegionsR3 = GIMR3GetMmio2Regions(pVM, &cRegions);
299 if ( cRegions
300 && pRegionsR3)
301 {
302 /*
303 * Register the MMIO2 regions.
304 */
305 PGIMMMIO2REGION pCur = pRegionsR3;
306 for (uint32_t i = 0; i < cRegions; i++, pCur++)
307 {
308 Assert(!pCur->fRegistered);
309 rc = PDMDevHlpMMIO2Register(pDevIns, NULL, pCur->iRegion, pCur->cbRegion, 0 /* fFlags */, &pCur->pvPageR3,
310 pCur->szDescription);
311 if (RT_FAILURE(rc))
312 return rc;
313
314 pCur->fRegistered = true;
315
316#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
317 RTR0PTR pR0Mapping = 0;
318 rc = PDMDevHlpMMIO2MapKernel(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
319 &pR0Mapping);
320 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
321 pCur->pvPageR0 = pR0Mapping;
322#else
323 pCur->pvPageR0 = (RTR0PTR)pCur->pvPageR3;
324#endif
325
326#ifdef VBOX_WITH_RAW_MODE_KEEP
327 /*
328 * Map into RC if required.
329 */
330 if (pCur->fRCMapping)
331 {
332 RTRCPTR pRCMapping = 0;
333 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
334 &pRCMapping);
335 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
336 pCur->pvPageRC = pRCMapping;
337 }
338 else
339 pCur->pvPageRC = NIL_RTRCPTR;
340#endif
341
342 LogRel(("GIMDev: Registered %s\n", pCur->szDescription));
343 }
344 }
345
346 /** @todo Register SSM: PDMDevHlpSSMRegister(). */
347 /** @todo Register statistics: STAM_REG(). */
348 /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */
349
350 return VINF_SUCCESS;
351}
352
353
354/**
355 * @interface_method_impl{PDMDEVREG,pfnDestruct}
356 */
357static DECLCALLBACK(int) gimdevR3Destruct(PPDMDEVINS pDevIns)
358{
359 PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV);
360 PVM pVM = PDMDevHlpGetVM(pDevIns);
361 uint32_t cRegions = 0;
362
363 PGIMMMIO2REGION pCur = GIMR3GetMmio2Regions(pVM, &cRegions);
364 for (uint32_t i = 0; i < cRegions; i++, pCur++)
365 {
366 int rc = PDMDevHlpMMIOExDeregister(pDevIns, NULL, pCur->iRegion);
367 if (RT_FAILURE(rc))
368 return rc;
369 }
370
371 /*
372 * Signal and wait for the debug thread to terminate.
373 */
374 if (pThis->hDbgRecvThread != NIL_RTTHREAD)
375 {
376 pThis->fDbgRecvThreadShutdown = true;
377 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
378 RTSemEventMultiSignal(pThis->Dbg.hDbgRecvThreadSem);
379
380 int rc = RTThreadWait(pThis->hDbgRecvThread, 20000, NULL /*prc*/);
381 if (RT_SUCCESS(rc))
382 pThis->hDbgRecvThread = NIL_RTTHREAD;
383 else
384 {
385 LogRel(("GIMDev: Debug thread did not terminate, rc=%Rrc!\n", rc));
386 return VERR_RESOURCE_BUSY;
387 }
388 }
389
390 /*
391 * Now clean up the semaphore & buffer now that the thread is gone.
392 */
393 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
394 {
395 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
396 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
397 }
398 if (pThis->Dbg.pvDbgRecvBuf)
399 {
400 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
401 pThis->Dbg.pvDbgRecvBuf = NULL;
402 }
403
404 return VINF_SUCCESS;
405}
406
407#endif /* IN_RING3 */
408
409/**
410 * The device registration structure.
411 */
412const PDMDEVREG g_DeviceGIMDev =
413{
414 /* .u32Version = */ PDM_DEVREG_VERSION,
415 /* .uReserved0 = */ 0,
416 /* .szName = */ "GIMDev",
417 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ,
418 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
419 /* .cMaxInstances = */ 1,
420 /* .uSharedVersion = */ 42,
421 /* .cbInstanceShared = */ sizeof(GIMDEV),
422 /* .cbInstanceCC = */ 0,
423 /* .cbInstanceRC = */ 0,
424 /* .cMaxPciDevices = */ 0,
425 /* .cMaxMsixVectors = */ 0,
426 /* .pszDescription = */ "VirtualBox GIM Device",
427#if defined(IN_RING3)
428 /* .pszRCMod = */ "VBoxDDRC.rc",
429 /* .pszR0Mod = */ "VBoxDDR0.r0",
430 /* .pfnConstruct = */ gimdevR3Construct,
431 /* .pfnDestruct = */ gimdevR3Destruct,
432 /* .pfnRelocate = */ gimdevR3Relocate,
433 /* .pfnMemSetup = */ NULL,
434 /* .pfnPowerOn = */ NULL,
435 /* .pfnReset = */ gimdevR3Reset,
436 /* .pfnSuspend = */ NULL,
437 /* .pfnResume = */ NULL,
438 /* .pfnAttach = */ NULL,
439 /* .pfnDetach = */ NULL,
440 /* .pfnQueryInterface = */ NULL,
441 /* .pfnInitComplete = */ NULL,
442 /* .pfnPowerOff = */ NULL,
443 /* .pfnSoftReset = */ NULL,
444 /* .pfnReserved0 = */ NULL,
445 /* .pfnReserved1 = */ NULL,
446 /* .pfnReserved2 = */ NULL,
447 /* .pfnReserved3 = */ NULL,
448 /* .pfnReserved4 = */ NULL,
449 /* .pfnReserved5 = */ NULL,
450 /* .pfnReserved6 = */ NULL,
451 /* .pfnReserved7 = */ NULL,
452#elif defined(IN_RING0)
453 /* .pfnEarlyConstruct = */ NULL,
454 /* .pfnConstruct = */ NULL,
455 /* .pfnDestruct = */ NULL,
456 /* .pfnFinalDestruct = */ NULL,
457 /* .pfnRequest = */ NULL,
458 /* .pfnReserved0 = */ NULL,
459 /* .pfnReserved1 = */ NULL,
460 /* .pfnReserved2 = */ NULL,
461 /* .pfnReserved3 = */ NULL,
462 /* .pfnReserved4 = */ NULL,
463 /* .pfnReserved5 = */ NULL,
464 /* .pfnReserved6 = */ NULL,
465 /* .pfnReserved7 = */ NULL,
466#elif defined(IN_RC)
467 /* .pfnConstruct = */ NULL,
468 /* .pfnReserved0 = */ NULL,
469 /* .pfnReserved1 = */ NULL,
470 /* .pfnReserved2 = */ NULL,
471 /* .pfnReserved3 = */ NULL,
472 /* .pfnReserved4 = */ NULL,
473 /* .pfnReserved5 = */ NULL,
474 /* .pfnReserved6 = */ NULL,
475 /* .pfnReserved7 = */ NULL,
476#else
477# error "Not in IN_RING3, IN_RING0 or IN_RC!"
478#endif
479 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
480};
481
482#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
483
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