VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use