VirtualBox

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

Last change on this file since 87766 was 87307, checked in by vboxsync, 4 years ago

DevPlayground.cpp: Added some very simple MMIO handling code that uses one backing page per function to preserve some content written and make it possible to read back, just as an example. Fixed saved state.

  • 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 87307 2021-01-19 17:52:50Z 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 /** 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