VirtualBox

source: vbox/trunk/src/VBox/Devices/Samples/DevPlayground.cpp@ 86405

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.8 KB
Line 
1/* $Id: DevPlayground.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * DevPlayground - Device for making PDM/PCI/... experiments.
4 *
5 * This device uses big PCI BAR64 resources, which needs the ICH9 chipset.
6 * The device works without any PCI config (because the default setup with the
7 * ICH9 chipset doesn't have anything at bus=0, device=0, function=0.
8 *
9 * To enable this device for a particular VM:
10 * VBoxManage setextradata vmname VBoxInternal/PDM/Devices/playground/Path .../obj/VBoxPlaygroundDevice/VBoxPlaygroundDevice
11 * VBoxManage setextradata vmname VBoxInternal/Devices/playground/0/Config/Whatever1 0
12 */
13
14/*
15 * Copyright (C) 2009-2020 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#define LOG_GROUP LOG_GROUP_MISC
31#include <VBox/vmm/pdmdev.h>
32#include <VBox/version.h>
33#include <VBox/err.h>
34#include <VBox/log.h>
35
36#include <VBox/com/assert.h>
37#include <VBox/com/defs.h>
38#include <VBox/com/string.h>
39#include <VBox/com/Guid.h>
40#include <VBox/com/VirtualBox.h>
41
42#include <iprt/assert.h>
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48/**
49 * Playground device per function (sub-device) data.
50 */
51typedef struct VBOXPLAYGROUNDDEVICEFUNCTION
52{
53 /** The function number. */
54 uint8_t iFun;
55 /** Device function name. */
56 char szName[31];
57 /** MMIO region \#0 name. */
58 char szMmio0[32];
59 /** MMIO region \#2 name. */
60 char szMmio2[32];
61 /** The MMIO region \#0 handle. */
62 IOMMMIOHANDLE hMmio0;
63 /** The MMIO region \#2 handle. */
64 IOMMMIOHANDLE hMmio2;
65} VBOXPLAYGROUNDDEVICEFUNCTION;
66/** Pointer to a PCI function of the playground device. */
67typedef VBOXPLAYGROUNDDEVICEFUNCTION *PVBOXPLAYGROUNDDEVICEFUNCTION;
68
69/**
70 * Playground device instance data.
71 */
72typedef struct VBOXPLAYGROUNDDEVICE
73{
74 /** PCI device functions. */
75 VBOXPLAYGROUNDDEVICEFUNCTION aPciFuns[8];
76} VBOXPLAYGROUNDDEVICE;
77/** Pointer to the instance data of a playground device instance. */
78typedef VBOXPLAYGROUNDDEVICE *PVBOXPLAYGROUNDDEVICE;
79
80
81#define PLAYGROUND_SSM_VERSION 3
82
83
84/*********************************************************************************************************************************
85* Device Functions *
86*********************************************************************************************************************************/
87
88static DECLCALLBACK(VBOXSTRICTRC) devPlaygroundMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
89{
90 NOREF(pDevIns);
91 NOREF(pvUser);
92 NOREF(off);
93 NOREF(pv);
94 NOREF(cb);
95 return VINF_SUCCESS;
96}
97
98
99static DECLCALLBACK(VBOXSTRICTRC) devPlaygroundMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
100{
101 NOREF(pDevIns);
102 NOREF(pvUser);
103 NOREF(off);
104 NOREF(pv);
105 NOREF(cb);
106 return VINF_SUCCESS;
107}
108
109
110/**
111 * @callback_method_impl{FNSSMDEVSAVEEXEC}
112 */
113static DECLCALLBACK(int) devPlaygroundSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
114{
115 PVBOXPLAYGROUNDDEVICE pThis = PDMDEVINS_2_DATA(pDevIns, PVBOXPLAYGROUNDDEVICE);
116 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
117
118 /* dummy (real devices would need to save their state here) */
119 RT_NOREF(pThis);
120
121 /* Demo of some API stuff - very unusual, think twice if there's no better
122 * solution which doesn't need API interaction. */
123#if 0
124 try
125 {
126 HRESULT hrc = S_OK;
127 com::Bstr bstrSnapName;
128 com::Guid uuid(COM_IIDOF(ISnapshot));
129 ISnapshot *pSnap = (ISnapshot *)PDMDevHlpQueryGenericUserObject(pDevIns, uuid.raw());
130 if (pSnap)
131 {
132 hrc = pSnap->COMGETTER(Name)(bstrSnapName.asOutParam());
133 AssertComRCReturn(hrc, VERR_INVALID_STATE);
134 }
135 com::Utf8Str strSnapName(bstrSnapName);
136 pHlp->pfnSSMPutStrZ(pSSM, strSnapName.c_str());
137 LogRel(("Playground: saving state of snapshot '%s', hrc=%Rhrc\n", strSnapName.c_str(), hrc));
138 }
139 catch (...)
140 {
141 AssertLogRelFailed();
142 return VERR_UNEXPECTED_EXCEPTION;
143 }
144#else
145RT_NOREF(pSSM, pHlp);
146#endif
147
148 return VINF_SUCCESS;
149}
150
151
152/**
153 * @callback_method_impl{FNSSMDEVLOADEXEC}
154 */
155static DECLCALLBACK(int) devPlaygroundLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
156{
157 PVBOXPLAYGROUNDDEVICE pThis = PDMDEVINS_2_DATA(pDevIns, PVBOXPLAYGROUNDDEVICE);
158 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
159
160 if (uVersion > PLAYGROUND_SSM_VERSION)
161 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
162 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
163
164 /* dummy (real devices would need to load their state here) */
165 RT_NOREF(pThis);
166
167 /* Reading the stuff written to saved state, just a demo. */
168 char szSnapName[256];
169 int rc = pHlp->pfnSSMGetStrZ(pSSM, szSnapName, sizeof(szSnapName));
170 AssertRCReturn(rc, rc);
171 LogRel(("Playground: loading state of snapshot '%s'\n", szSnapName));
172
173 return VINF_SUCCESS;
174}
175
176
177/**
178 * @interface_method_impl{PDMDEVREG,pfnDestruct}
179 */
180static DECLCALLBACK(int) devPlaygroundDestruct(PPDMDEVINS pDevIns)
181{
182 /*
183 * Check the versions here as well since the destructor is *always* called.
184 * THIS IS ALWAYS THE FIRST STATEMENT IN A DESTRUCTOR!
185 */
186 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
187
188 return VINF_SUCCESS;
189}
190
191
192/**
193 * @interface_method_impl{PDMDEVREG,pfnConstruct}
194 */
195static DECLCALLBACK(int) devPlaygroundConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
196{
197 /*
198 * Check that the device instance and device helper structures are compatible.
199 * THIS IS ALWAYS THE FIRST STATEMENT IN A CONSTRUCTOR!
200 */
201 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* This must come first. */
202 Assert(iInstance == 0); RT_NOREF(iInstance);
203
204 /*
205 * Initialize the instance data so that the destructor won't mess up.
206 */
207 PVBOXPLAYGROUNDDEVICE pThis = PDMDEVINS_2_DATA(pDevIns, PVBOXPLAYGROUNDDEVICE);
208
209 /*
210 * Validate and read the configuration.
211 */
212 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Whatever1|NumFunctions|BigBAR0MB|BigBAR0GB|BigBAR2MB|BigBAR2GB", "");
213
214 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
215
216 uint8_t uNumFunctions;
217 AssertCompile(RT_ELEMENTS(pThis->aPciFuns) <= RT_ELEMENTS(pDevIns->apPciDevs));
218 int rc = pHlp->pfnCFGMQueryU8Def(pCfg, "NumFunctions", &uNumFunctions, RT_ELEMENTS(pThis->aPciFuns));
219 if (RT_FAILURE(rc))
220 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"NumFunctions\""));
221 if ((uNumFunctions < 1) || (uNumFunctions > RT_ELEMENTS(pThis->aPciFuns)))
222 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"NumFunctions\" value (must be between 1 and 8)"));
223
224 RTGCPHYS cbFirstBAR;
225 uint16_t uBigBAR0GB;
226 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BigBAR0GB", &uBigBAR0GB, 0); /* Default to nothing. */
227 if (RT_FAILURE(rc))
228 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR0GB\""));
229 if (uBigBAR0GB > 512)
230 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR0GB\" value (must be 512 or less)"));
231
232 if (uBigBAR0GB)
233 cbFirstBAR = uBigBAR0GB * _1G64;
234 else
235 {
236 uint16_t uBigBAR0MB;
237 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BigBAR0MB", &uBigBAR0MB, 8); /* 8 MB default. */
238 if (RT_FAILURE(rc))
239 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR0MB\""));
240 if (uBigBAR0MB < 1 || uBigBAR0MB > 4095)
241 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR0MB\" value (must be between 1 and 4095)"));
242 cbFirstBAR = uBigBAR0MB * _1M;
243 }
244
245 RTGCPHYS cbSecondBAR;
246 uint16_t uBigBAR2GB;
247 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BigBAR2GB", &uBigBAR2GB, 0); /* Default to nothing. */
248 if (RT_FAILURE(rc))
249 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR2GB\""));
250 if (uBigBAR2GB > 512)
251 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR2GB\" value (must be 512 or less)"));
252
253 if (uBigBAR2GB)
254 cbSecondBAR = uBigBAR2GB * _1G64;
255 else
256 {
257 uint16_t uBigBAR2MB;
258 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BigBAR2MB", &uBigBAR2MB, 16); /* 16 MB default. */
259 if (RT_FAILURE(rc))
260 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR2MB\""));
261 if (uBigBAR2MB < 1 || uBigBAR2MB > 4095)
262 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR2MB\" value (must be between 1 and 4095)"));
263 cbSecondBAR = uBigBAR2MB * _1M;
264 }
265
266
267 /*
268 * PCI device setup.
269 */
270 uint32_t iPciDevNo = PDMPCIDEVREG_DEV_NO_FIRST_UNUSED;
271 for (uint32_t iPciFun = 0; iPciFun < uNumFunctions; iPciFun++)
272 {
273 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[iPciFun];
274 PVBOXPLAYGROUNDDEVICEFUNCTION pFun = &pThis->aPciFuns[iPciFun];
275 RTStrPrintf(pFun->szName, sizeof(pFun->szName), "playground%u", iPciFun);
276 pFun->iFun = iPciFun;
277
278 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
279
280 PDMPciDevSetVendorId(pPciDev, 0x80ee);
281 PDMPciDevSetDeviceId(pPciDev, 0xde4e);
282 PDMPciDevSetClassBase(pPciDev, 0x07); /* communications device */
283 PDMPciDevSetClassSub(pPciDev, 0x80); /* other communications device */
284 if (iPciFun == 0) /* only for the primary function */
285 PDMPciDevSetHeaderType(pPciDev, 0x80); /* normal, multifunction device */
286
287 rc = PDMDevHlpPCIRegisterEx(pDevIns, pPciDev, 0 /*fFlags*/, iPciDevNo, iPciFun, pThis->aPciFuns[iPciFun].szName);
288 AssertLogRelRCReturn(rc, rc);
289
290 /* First region. */
291 RTGCPHYS const cbFirst = iPciFun == 0 ? cbFirstBAR : iPciFun * _4K;
292 RTStrPrintf(pFun->szMmio0, sizeof(pFun->szMmio0), "PG-F%d-BAR0", iPciFun);
293 rc = PDMDevHlpMmioCreate(pDevIns, cbFirst, pPciDev, 0 /*iPciRegion*/,
294 devPlaygroundMMIOWrite, devPlaygroundMMIORead, NULL /*pvUser*/,
295 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, pFun->szMmio0, &pFun->hMmio0);
296 AssertLogRelRCReturn(rc, rc);
297
298 rc = PDMDevHlpPCIIORegionRegisterMmioEx(pDevIns, pPciDev, 0, cbFirst,
299 (PCIADDRESSSPACE)( PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR64
300 | (iPciFun == 0 ? PCI_ADDRESS_SPACE_MEM_PREFETCH : 0)),
301 pFun->hMmio0, NULL);
302 AssertLogRelRCReturn(rc, rc);
303
304 /* Second region. */
305 RTGCPHYS const cbSecond = iPciFun == 0 ? cbSecondBAR : iPciFun * _32K;
306 RTStrPrintf(pFun->szMmio2, sizeof(pFun->szMmio2), "PG-F%d-BAR2", iPciFun);
307 rc = PDMDevHlpMmioCreate(pDevIns, cbSecond, pPciDev, 2 << 16 /*iPciRegion*/,
308 devPlaygroundMMIOWrite, devPlaygroundMMIORead, NULL /*pvUser*/,
309 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, pFun->szMmio2, &pFun->hMmio2);
310 AssertLogRelRCReturn(rc, rc);
311
312 rc = PDMDevHlpPCIIORegionRegisterMmioEx(pDevIns, pPciDev, 2, cbSecond,
313 (PCIADDRESSSPACE)( PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR64
314 | (iPciFun == 0 ? PCI_ADDRESS_SPACE_MEM_PREFETCH : 0)),
315 pFun->hMmio2, NULL);
316 AssertLogRelRCReturn(rc, rc);
317
318 /* Subsequent function should use the same major as the previous one. */
319 iPciDevNo = PDMPCIDEVREG_DEV_NO_SAME_AS_PREV;
320 }
321
322 /*
323 * Save state handling.
324 */
325 rc = PDMDevHlpSSMRegister(pDevIns, PLAYGROUND_SSM_VERSION, sizeof(*pThis), devPlaygroundSaveExec, devPlaygroundLoadExec);
326 if (RT_FAILURE(rc))
327 return rc;
328
329 return VINF_SUCCESS;
330}
331
332
333/**
334 * The device registration structure.
335 */
336static const PDMDEVREG g_DevicePlayground =
337{
338 /* .u32Version = */ PDM_DEVREG_VERSION,
339 /* .uReserved0 = */ 0,
340 /* .szName = */ "playground",
341 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
342 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
343 /* .cMaxInstances = */ 1,
344 /* .uSharedVersion = */ 42,
345 /* .cbInstanceShared = */ sizeof(VBOXPLAYGROUNDDEVICE),
346 /* .cbInstanceCC = */ 0,
347 /* .cbInstanceRC = */ 0,
348 /* .cMaxPciDevices = */ 8,
349 /* .cMaxMsixVectors = */ 0,
350 /* .pszDescription = */ "VBox Playground Device.",
351#if defined(IN_RING3)
352 /* .pszRCMod = */ "",
353 /* .pszR0Mod = */ "",
354 /* .pfnConstruct = */ devPlaygroundConstruct,
355 /* .pfnDestruct = */ devPlaygroundDestruct,
356 /* .pfnRelocate = */ NULL,
357 /* .pfnMemSetup = */ NULL,
358 /* .pfnPowerOn = */ NULL,
359 /* .pfnReset = */ NULL,
360 /* .pfnSuspend = */ NULL,
361 /* .pfnResume = */ NULL,
362 /* .pfnAttach = */ NULL,
363 /* .pfnDetach = */ NULL,
364 /* .pfnQueryInterface = */ NULL,
365 /* .pfnInitComplete = */ NULL,
366 /* .pfnPowerOff = */ NULL,
367 /* .pfnSoftReset = */ NULL,
368 /* .pfnReserved0 = */ NULL,
369 /* .pfnReserved1 = */ NULL,
370 /* .pfnReserved2 = */ NULL,
371 /* .pfnReserved3 = */ NULL,
372 /* .pfnReserved4 = */ NULL,
373 /* .pfnReserved5 = */ NULL,
374 /* .pfnReserved6 = */ NULL,
375 /* .pfnReserved7 = */ NULL,
376#elif defined(IN_RING0)
377 /* .pfnEarlyConstruct = */ NULL,
378 /* .pfnConstruct = */ NULL,
379 /* .pfnDestruct = */ NULL,
380 /* .pfnFinalDestruct = */ NULL,
381 /* .pfnRequest = */ NULL,
382 /* .pfnReserved0 = */ NULL,
383 /* .pfnReserved1 = */ NULL,
384 /* .pfnReserved2 = */ NULL,
385 /* .pfnReserved3 = */ NULL,
386 /* .pfnReserved4 = */ NULL,
387 /* .pfnReserved5 = */ NULL,
388 /* .pfnReserved6 = */ NULL,
389 /* .pfnReserved7 = */ NULL,
390#elif defined(IN_RC)
391 /* .pfnConstruct = */ NULL,
392 /* .pfnReserved0 = */ NULL,
393 /* .pfnReserved1 = */ NULL,
394 /* .pfnReserved2 = */ NULL,
395 /* .pfnReserved3 = */ NULL,
396 /* .pfnReserved4 = */ NULL,
397 /* .pfnReserved5 = */ NULL,
398 /* .pfnReserved6 = */ NULL,
399 /* .pfnReserved7 = */ NULL,
400#else
401# error "Not in IN_RING3, IN_RING0 or IN_RC!"
402#endif
403 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
404};
405
406
407/**
408 * Register devices provided by the plugin.
409 *
410 * @returns VBox status code.
411 * @param pCallbacks Pointer to the callback table.
412 * @param u32Version VBox version number.
413 */
414extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
415{
416 LogFlow(("VBoxPlaygroundDevice::VBoxDevicesRegister: u32Version=%#x pCallbacks->u32Version=%#x\n", u32Version, pCallbacks->u32Version));
417
418 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
419 ("VirtualBox version %#x, expected %#x or higher\n", u32Version, VBOX_VERSION),
420 VERR_VERSION_MISMATCH);
421 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
422 ("callback version %#x, expected %#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
423 VERR_VERSION_MISMATCH);
424
425 return pCallbacks->pfnRegister(pCallbacks, &g_DevicePlayground);
426}
427
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