VirtualBox

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

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