VirtualBox

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

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