VirtualBox

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

Last change on this file since 69500 was 69500, checked in by vboxsync, 7 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 69500 2017-10-28 15:14:05Z vboxsync $ */
2/** @file
3 * Guest Interface Manager Device.
4 */
5
6/*
7 * Copyright (C) 2014-2017 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/**
176 * @interface_method_impl{PDMDEVREG,pfnConstruct}
177 */
178static DECLCALLBACK(int) gimdevR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
179{
180 RT_NOREF2(iInstance, pCfg);
181 Assert(iInstance == 0);
182 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
183 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
184
185 /*
186 * Initialize relevant state bits.
187 */
188 pThis->pDevInsR3 = pDevIns;
189 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
190 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
191
192 /*
193 * Get debug setup requirements from GIM.
194 */
195 PVM pVM = PDMDevHlpGetVM(pDevIns);
196 int rc = GIMR3GetDebugSetup(pVM, &pThis->DbgSetup);
197 if ( RT_SUCCESS(rc)
198 && pThis->DbgSetup.cbDbgRecvBuf > 0)
199 {
200 /*
201 * Attach the stream driver for the debug connection.
202 */
203 PPDMISTREAM pDbgDrvStream = NULL;
204 pThis->IDbgBase.pfnQueryInterface = gimdevR3QueryInterface;
205 rc = PDMDevHlpDriverAttach(pDevIns, GIMDEV_DEBUG_LUN, &pThis->IDbgBase, &pThis->pDbgDrvBase, "GIM Debug Port");
206 if (RT_SUCCESS(rc))
207 {
208 pDbgDrvStream = PDMIBASE_QUERY_INTERFACE(pThis->pDbgDrvBase, PDMISTREAM);
209 if (pDbgDrvStream)
210 LogRel(("GIMDev: LUN#%u: Debug port configured\n", GIMDEV_DEBUG_LUN));
211 else
212 {
213 LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN));
214 rc = VERR_INTERNAL_ERROR_2;
215 }
216 }
217 else
218 {
219 pThis->pDbgDrvBase = NULL;
220 LogRel(("GIMDev: LUN#%u: No debug port configured! rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc));
221 }
222
223 if (!pDbgDrvStream)
224 {
225 Assert(rc != VINF_SUCCESS);
226 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
227 N_("Debug port configuration expected when GIM configured with debugging support"));
228 }
229
230 void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf);
231 if (RT_UNLIKELY(!pvDbgRecvBuf))
232 {
233 LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf));
234 return VERR_NO_MEMORY;
235 }
236
237 /*
238 * Update the shared debug struct.
239 */
240 pThis->Dbg.pDbgDrvStream = pDbgDrvStream;
241 pThis->Dbg.pvDbgRecvBuf = pvDbgRecvBuf;
242 pThis->Dbg.cbDbgRecvBufRead = 0;
243 pThis->Dbg.fDbgRecvBufRead = false;
244
245 /*
246 * Create the sempahore and the debug receive thread itself.
247 */
248 rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem);
249 if (RT_SUCCESS(rc))
250 {
251 rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO,
252 RTTHREADFLAGS_WAITABLE, "GIMDebugRecv");
253 if (RT_FAILURE(rc))
254 {
255 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
256 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
257
258 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
259 pThis->Dbg.pvDbgRecvBuf = NULL;
260 return rc;
261 }
262 }
263 else
264 return rc;
265 }
266
267 /*
268 * Register this device with the GIM component.
269 */
270 GIMR3GimDeviceRegister(pVM, pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL);
271
272 /*
273 * Get the MMIO2 regions from the GIM provider.
274 */
275 uint32_t cRegions = 0;
276 PGIMMMIO2REGION pRegionsR3 = GIMR3GetMmio2Regions(pVM, &cRegions);
277 if ( cRegions
278 && pRegionsR3)
279 {
280 /*
281 * Register the MMIO2 regions.
282 */
283 PGIMMMIO2REGION pCur = pRegionsR3;
284 for (uint32_t i = 0; i < cRegions; i++, pCur++)
285 {
286 Assert(!pCur->fRegistered);
287 rc = PDMDevHlpMMIO2Register(pDevIns, NULL, pCur->iRegion, pCur->cbRegion, 0 /* fFlags */, &pCur->pvPageR3,
288 pCur->szDescription);
289 if (RT_FAILURE(rc))
290 return rc;
291
292 pCur->fRegistered = true;
293
294#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
295 RTR0PTR pR0Mapping = 0;
296 rc = PDMDevHlpMMIO2MapKernel(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
297 &pR0Mapping);
298 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
299 pCur->pvPageR0 = pR0Mapping;
300#else
301 pCur->pvPageR0 = (RTR0PTR)pCur->pvPageR3;
302#endif
303
304 /*
305 * Map into RC if required.
306 */
307 if (pCur->fRCMapping)
308 {
309 RTRCPTR pRCMapping = 0;
310 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
311 &pRCMapping);
312 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
313 pCur->pvPageRC = pRCMapping;
314 }
315 else
316 pCur->pvPageRC = NIL_RTRCPTR;
317
318 LogRel(("GIMDev: Registered %s\n", pCur->szDescription));
319 }
320 }
321
322 /** @todo Register SSM: PDMDevHlpSSMRegister(). */
323 /** @todo Register statistics: STAM_REG(). */
324 /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */
325
326 return VINF_SUCCESS;
327}
328
329
330/**
331 * @interface_method_impl{PDMDEVREG,pfnDestruct}
332 */
333static DECLCALLBACK(int) gimdevR3Destruct(PPDMDEVINS pDevIns)
334{
335 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
336 PVM pVM = PDMDevHlpGetVM(pDevIns);
337 uint32_t cRegions = 0;
338
339 PGIMMMIO2REGION pCur = GIMR3GetMmio2Regions(pVM, &cRegions);
340 for (uint32_t i = 0; i < cRegions; i++, pCur++)
341 {
342 int rc = PDMDevHlpMMIOExDeregister(pDevIns, NULL, pCur->iRegion);
343 if (RT_FAILURE(rc))
344 return rc;
345 }
346
347 /*
348 * Signal and wait for the debug thread to terminate.
349 */
350 if (pThis->hDbgRecvThread != NIL_RTTHREAD)
351 {
352 pThis->fDbgRecvThreadShutdown = true;
353 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
354 RTSemEventMultiSignal(pThis->Dbg.hDbgRecvThreadSem);
355
356 int rc = RTThreadWait(pThis->hDbgRecvThread, 20000, NULL /*prc*/);
357 if (RT_SUCCESS(rc))
358 pThis->hDbgRecvThread = NIL_RTTHREAD;
359 else
360 {
361 LogRel(("GIMDev: Debug thread did not terminate, rc=%Rrc!\n", rc));
362 return VERR_RESOURCE_BUSY;
363 }
364 }
365
366 /*
367 * Now clean up the semaphore & buffer now that the thread is gone.
368 */
369 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
370 {
371 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
372 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
373 }
374 if (pThis->Dbg.pvDbgRecvBuf)
375 {
376 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
377 pThis->Dbg.pvDbgRecvBuf = NULL;
378 }
379
380 return VINF_SUCCESS;
381}
382
383
384/**
385 * @interface_method_impl{PDMDEVREG,pfnRelocate}
386 */
387static DECLCALLBACK(void) gimdevR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
388{
389 NOREF(pDevIns);
390 NOREF(offDelta);
391}
392
393
394/**
395 * @interface_method_impl{PDMDEVREG,pfnReset}
396 */
397static DECLCALLBACK(void) gimdevR3Reset(PPDMDEVINS pDevIns)
398{
399 NOREF(pDevIns);
400 /* We do not deregister any MMIO2 regions as the regions are expected to be static. */
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