VirtualBox

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

Last change on this file since 78392 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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