VirtualBox

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

Last change on this file since 80118 was 80118, checked in by vboxsync, 5 years ago

VMM: Kicking out raw-mode and 32-bit hosts - MM, PGM, ++. bugref:9517 bugref:9511

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/* $Id: GIMDev.cpp 80118 2019-08-04 02:39:54Z 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#ifdef VBOX_WITH_RAW_MODE_KEEP
193# error relocate pvPageRC
194#endif
195}
196
197
198/**
199 * @interface_method_impl{PDMDEVREG,pfnConstruct}
200 */
201static DECLCALLBACK(int) gimdevR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
202{
203 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
204 RT_NOREF2(iInstance, pCfg);
205 Assert(iInstance == 0);
206 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
207
208 /*
209 * Initialize relevant state bits.
210 */
211 pThis->pDevInsR3 = pDevIns;
212 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
213 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
214
215 /*
216 * Get debug setup requirements from GIM.
217 */
218 PVM pVM = PDMDevHlpGetVM(pDevIns);
219 int rc = GIMR3GetDebugSetup(pVM, &pThis->DbgSetup);
220 if ( RT_SUCCESS(rc)
221 && pThis->DbgSetup.cbDbgRecvBuf > 0)
222 {
223 /*
224 * Attach the stream driver for the debug connection.
225 */
226 PPDMISTREAM pDbgDrvStream = NULL;
227 pThis->IDbgBase.pfnQueryInterface = gimdevR3QueryInterface;
228 rc = PDMDevHlpDriverAttach(pDevIns, GIMDEV_DEBUG_LUN, &pThis->IDbgBase, &pThis->pDbgDrvBase, "GIM Debug Port");
229 if (RT_SUCCESS(rc))
230 {
231 pDbgDrvStream = PDMIBASE_QUERY_INTERFACE(pThis->pDbgDrvBase, PDMISTREAM);
232 if (pDbgDrvStream)
233 LogRel(("GIMDev: LUN#%u: Debug port configured\n", GIMDEV_DEBUG_LUN));
234 else
235 {
236 LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN));
237 rc = VERR_INTERNAL_ERROR_2;
238 }
239 }
240 else
241 {
242 pThis->pDbgDrvBase = NULL;
243 LogRel(("GIMDev: LUN#%u: No debug port configured! rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc));
244 }
245
246 if (!pDbgDrvStream)
247 {
248 Assert(rc != VINF_SUCCESS);
249 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
250 N_("Debug port configuration expected when GIM configured with debugging support"));
251 }
252
253 void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf);
254 if (RT_UNLIKELY(!pvDbgRecvBuf))
255 {
256 LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf));
257 return VERR_NO_MEMORY;
258 }
259
260 /*
261 * Update the shared debug struct.
262 */
263 pThis->Dbg.pDbgDrvStream = pDbgDrvStream;
264 pThis->Dbg.pvDbgRecvBuf = pvDbgRecvBuf;
265 pThis->Dbg.cbDbgRecvBufRead = 0;
266 pThis->Dbg.fDbgRecvBufRead = false;
267
268 /*
269 * Create the sempahore and the debug receive thread itself.
270 */
271 rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem);
272 if (RT_SUCCESS(rc))
273 {
274 rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO,
275 RTTHREADFLAGS_WAITABLE, "GIMDebugRecv");
276 if (RT_FAILURE(rc))
277 {
278 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
279 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
280
281 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
282 pThis->Dbg.pvDbgRecvBuf = NULL;
283 return rc;
284 }
285 }
286 else
287 return rc;
288 }
289
290 /*
291 * Register this device with the GIM component.
292 */
293 GIMR3GimDeviceRegister(pVM, pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL);
294
295 /*
296 * Get the MMIO2 regions from the GIM provider.
297 */
298 uint32_t cRegions = 0;
299 PGIMMMIO2REGION pRegionsR3 = GIMR3GetMmio2Regions(pVM, &cRegions);
300 if ( cRegions
301 && pRegionsR3)
302 {
303 /*
304 * Register the MMIO2 regions.
305 */
306 PGIMMMIO2REGION pCur = pRegionsR3;
307 for (uint32_t i = 0; i < cRegions; i++, pCur++)
308 {
309 Assert(!pCur->fRegistered);
310 rc = PDMDevHlpMMIO2Register(pDevIns, NULL, pCur->iRegion, pCur->cbRegion, 0 /* fFlags */, &pCur->pvPageR3,
311 pCur->szDescription);
312 if (RT_FAILURE(rc))
313 return rc;
314
315 pCur->fRegistered = true;
316
317#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
318 RTR0PTR pR0Mapping = 0;
319 rc = PDMDevHlpMMIO2MapKernel(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
320 &pR0Mapping);
321 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
322 pCur->pvPageR0 = pR0Mapping;
323#else
324 pCur->pvPageR0 = (RTR0PTR)pCur->pvPageR3;
325#endif
326
327#ifdef VBOX_WITH_RAW_MODE_KEEP
328 /*
329 * Map into RC if required.
330 */
331 if (pCur->fRCMapping)
332 {
333 RTRCPTR pRCMapping = 0;
334 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
335 &pRCMapping);
336 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
337 pCur->pvPageRC = pRCMapping;
338 }
339 else
340 pCur->pvPageRC = NIL_RTRCPTR;
341#endif
342
343 LogRel(("GIMDev: Registered %s\n", pCur->szDescription));
344 }
345 }
346
347 /** @todo Register SSM: PDMDevHlpSSMRegister(). */
348 /** @todo Register statistics: STAM_REG(). */
349 /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */
350
351 return VINF_SUCCESS;
352}
353
354
355/**
356 * @interface_method_impl{PDMDEVREG,pfnDestruct}
357 */
358static DECLCALLBACK(int) gimdevR3Destruct(PPDMDEVINS pDevIns)
359{
360 PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
361 PVM pVM = PDMDevHlpGetVM(pDevIns);
362 uint32_t cRegions = 0;
363
364 PGIMMMIO2REGION pCur = GIMR3GetMmio2Regions(pVM, &cRegions);
365 for (uint32_t i = 0; i < cRegions; i++, pCur++)
366 {
367 int rc = PDMDevHlpMMIOExDeregister(pDevIns, NULL, pCur->iRegion);
368 if (RT_FAILURE(rc))
369 return rc;
370 }
371
372 /*
373 * Signal and wait for the debug thread to terminate.
374 */
375 if (pThis->hDbgRecvThread != NIL_RTTHREAD)
376 {
377 pThis->fDbgRecvThreadShutdown = true;
378 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
379 RTSemEventMultiSignal(pThis->Dbg.hDbgRecvThreadSem);
380
381 int rc = RTThreadWait(pThis->hDbgRecvThread, 20000, NULL /*prc*/);
382 if (RT_SUCCESS(rc))
383 pThis->hDbgRecvThread = NIL_RTTHREAD;
384 else
385 {
386 LogRel(("GIMDev: Debug thread did not terminate, rc=%Rrc!\n", rc));
387 return VERR_RESOURCE_BUSY;
388 }
389 }
390
391 /*
392 * Now clean up the semaphore & buffer now that the thread is gone.
393 */
394 if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT)
395 {
396 RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
397 pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;
398 }
399 if (pThis->Dbg.pvDbgRecvBuf)
400 {
401 RTMemFree(pThis->Dbg.pvDbgRecvBuf);
402 pThis->Dbg.pvDbgRecvBuf = NULL;
403 }
404
405 return VINF_SUCCESS;
406}
407
408
409/**
410 * The device registration structure.
411 */
412const PDMDEVREG g_DeviceGIMDev =
413{
414 /* u32Version */
415 PDM_DEVREG_VERSION,
416 /* szName */
417 "GIMDev",
418 /* szRCMod */
419 "VBoxDDRC.rc",
420 /* szR0Mod */
421 "VBoxDDR0.r0",
422 /* pszDescription */
423 "VirtualBox GIM Device",
424 /* fFlags */
425 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_RC,
426 /* fClass */
427 PDM_DEVREG_CLASS_MISC,
428 /* cMaxInstances */
429 1,
430 /* cbInstance */
431 sizeof(GIMDEV),
432 /* pfnConstruct */
433 gimdevR3Construct,
434 /* pfnDestruct */
435 gimdevR3Destruct,
436 /* pfnRelocate */
437 gimdevR3Relocate,
438 /* pfnMemSetup */
439 NULL,
440 /* pfnPowerOn */
441 NULL,
442 /* pfnReset */
443 gimdevR3Reset,
444 /* pfnSuspend */
445 NULL,
446 /* pfnResume */
447 NULL,
448 /* pfnAttach */
449 NULL,
450 /* pfnDetach */
451 NULL,
452 /* pfnQueryInterface. */
453 NULL,
454 /* pfnInitComplete */
455 NULL,
456 /* pfnPowerOff */
457 NULL,
458 /* pfnSoftReset */
459 NULL,
460 /* u32VersionEnd */
461 PDM_DEVREG_VERSION
462};
463#endif /* IN_RING3 */
464
465#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
466
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