VirtualBox

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

Last change on this file since 59381 was 58532, checked in by vboxsync, 9 years ago

GIMDev: Don't terminate thread prematurely on Windows.

  • 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 58532 2015-10-30 13:03:19Z 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 LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN));
210 }
211 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
212 {
213 pThis->pDbgDrvBase = NULL;
214 LogRel(("GIMDev: LUN#%u: No debug port configured\n", GIMDEV_DEBUG_LUN));
215 }
216 else
217 {
218 AssertLogRelMsgFailed(("GIMDev: LUN#%u: Failed to attach to driver on debug port. rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc));
219 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
220 return rc;
221 }
222
223 void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf);
224 if (RT_UNLIKELY(!pvDbgRecvBuf))
225 {
226 LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf));
227 return VERR_NO_MEMORY;
228 }
229
230 /*
231 * Update the shared debug struct.
232 */
233 pThis->Dbg.pDbgDrvStream = pDbgDrvStream;
234 pThis->Dbg.pvDbgRecvBuf = pvDbgRecvBuf;
235 pThis->Dbg.cbDbgRecvBufRead = 0;
236 pThis->Dbg.fDbgRecvBufRead = false;
237
238 /*
239 * Create the sempahore and the debug receive thread itself.
240 */
241 rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem);
242 if (RT_SUCCESS(rc))
243 {
244 rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO,
245 RTTHREADFLAGS_WAITABLE, "GIMDebugRecv");
246 if (RT_FAILURE(rc))
247 {
248 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
249 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
250
251 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
252 pThis->Dbg.pvDbgRecvBuf = NULL;
253 return rc;
254 }
255 }
256 else
257 return rc;
258 }
259
260 /*
261 * Register this device with the GIM component.
262 */
263 GIMR3GimDeviceRegister(pVM, pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL);
264
265 /*
266 * Get the MMIO2 regions from the GIM provider.
267 */
268 uint32_t cRegions = 0;
269 PGIMMMIO2REGION pRegionsR3 = GIMR3GetMmio2Regions(pVM, &cRegions);
270 if ( cRegions
271 && pRegionsR3)
272 {
273 /*
274 * Register the MMIO2 regions.
275 */
276 PGIMMMIO2REGION pCur = pRegionsR3;
277 for (uint32_t i = 0; i < cRegions; i++, pCur++)
278 {
279 Assert(!pCur->fRegistered);
280 rc = PDMDevHlpMMIO2Register(pDevIns, pCur->iRegion, pCur->cbRegion, 0 /* fFlags */, &pCur->pvPageR3,
281 pCur->szDescription);
282 if (RT_FAILURE(rc))
283 return rc;
284
285 pCur->fRegistered = true;
286
287#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
288 RTR0PTR pR0Mapping = 0;
289 rc = PDMDevHlpMMIO2MapKernel(pDevIns, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
290 &pR0Mapping);
291 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
292 pCur->pvPageR0 = pR0Mapping;
293#else
294 pCur->pvPageR0 = (RTR0PTR)pCur->pvPageR3;
295#endif
296
297 /*
298 * Map into RC if required.
299 */
300 if (pCur->fRCMapping)
301 {
302 RTRCPTR pRCMapping = 0;
303 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
304 &pRCMapping);
305 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
306 pCur->pvPageRC = pRCMapping;
307 }
308 else
309 pCur->pvPageRC = NIL_RTRCPTR;
310
311 LogRel(("GIMDev: Registered %s\n", pCur->szDescription));
312 }
313 }
314
315 /** @todo Register SSM: PDMDevHlpSSMRegister(). */
316 /** @todo Register statistics: STAM_REG(). */
317 /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */
318
319 return VINF_SUCCESS;
320}
321
322
323/**
324 * @interface_method_impl{PDMDEVREG,pfnDestruct}
325 */
326static DECLCALLBACK(int) gimdevR3Destruct(PPDMDEVINS pDevIns)
327{
328 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
329 PVM pVM = PDMDevHlpGetVM(pDevIns);
330 uint32_t cRegions = 0;
331
332 PGIMMMIO2REGION pCur = GIMR3GetMmio2Regions(pVM, &cRegions);
333 for (uint32_t i = 0; i < cRegions; i++, pCur++)
334 {
335 int rc = PDMDevHlpMMIO2Deregister(pDevIns, pCur->iRegion);
336 if (RT_FAILURE(rc))
337 return rc;
338 }
339
340 /*
341 * Signal and wait for the debug thread to terminate.
342 */
343 if (pThis->hDbgRecvThread != NIL_RTTHREAD)
344 {
345 pThis->fDbgRecvThreadShutdown = true;
346 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
347 RTSemEventMultiSignal(pThis->Dbg.hDbgRecvThreadSem);
348
349 int rc = RTThreadWait(pThis->hDbgRecvThread, 20000, NULL /*prc*/);
350 if (RT_SUCCESS(rc))
351 pThis->hDbgRecvThread = NIL_RTTHREAD;
352 else
353 {
354 LogRel(("GIMDev: Debug thread did not terminate, rc=%Rrc!\n", rc));
355 return VERR_RESOURCE_BUSY;
356 }
357 }
358
359 /*
360 * Now clean up the semaphore & buffer now that the thread is gone.
361 */
362 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
363 {
364 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
365 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
366 }
367 if (pThis->Dbg.pvDbgRecvBuf)
368 {
369 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
370 pThis->Dbg.pvDbgRecvBuf = NULL;
371 }
372
373 return VINF_SUCCESS;
374}
375
376
377/**
378 * @interface_method_impl{PDMDEVREG,pfnRelocate}
379 */
380static DECLCALLBACK(void) gimdevR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
381{
382 NOREF(pDevIns);
383 NOREF(offDelta);
384}
385
386
387/**
388 * @interface_method_impl{PDMDEVREG,pfnReset}
389 */
390static DECLCALLBACK(void) gimdevR3Reset(PPDMDEVINS pDevIns)
391{
392 NOREF(pDevIns);
393 /* We do not deregister any MMIO2 regions as the regions are expected to be static. */
394}
395
396
397/**
398 * The device registration structure.
399 */
400const PDMDEVREG g_DeviceGIMDev =
401{
402 /* u32Version */
403 PDM_DEVREG_VERSION,
404 /* szName */
405 "GIMDev",
406 /* szRCMod */
407 "VBoxDDRC.rc",
408 /* szR0Mod */
409 "VBoxDDR0.r0",
410 /* pszDescription */
411 "VirtualBox GIM Device",
412 /* fFlags */
413 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_RC,
414 /* fClass */
415 PDM_DEVREG_CLASS_MISC,
416 /* cMaxInstances */
417 1,
418 /* cbInstance */
419 sizeof(GIMDEV),
420 /* pfnConstruct */
421 gimdevR3Construct,
422 /* pfnDestruct */
423 gimdevR3Destruct,
424 /* pfnRelocate */
425 gimdevR3Relocate,
426 /* pfnMemSetup */
427 NULL,
428 /* pfnPowerOn */
429 NULL,
430 /* pfnReset */
431 gimdevR3Reset,
432 /* pfnSuspend */
433 NULL,
434 /* pfnResume */
435 NULL,
436 /* pfnAttach */
437 NULL,
438 /* pfnDetach */
439 NULL,
440 /* pfnQueryInterface. */
441 NULL,
442 /* pfnInitComplete */
443 NULL,
444 /* pfnPowerOff */
445 NULL,
446 /* pfnSoftReset */
447 NULL,
448 /* u32VersionEnd */
449 PDM_DEVREG_VERSION
450};
451#endif /* IN_RING3 */
452
453#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
454
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