VirtualBox

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

Last change on this file since 61541 was 59631, checked in by vboxsync, 9 years ago

Devices/GIMDev: indent.

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