VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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