VirtualBox

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

Last change on this file since 78233 was 77828, checked in by vboxsync, 6 years ago

DevPlayground: Made the playground device somewhat configurable via CFGM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
Line 
1/* $Id: DevPlayground.cpp 77828 2019-03-21 15:01:15Z 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-2019 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 PCI devices. */
54 PDMPCIDEV PciDev;
55 /** The function number. */
56 uint8_t iFun;
57 /** Device function name. */
58 char szName[31];
59} VBOXPLAYGROUNDDEVICEFUNCTION;
60/** Pointer to a PCI function of the playground device. */
61typedef VBOXPLAYGROUNDDEVICEFUNCTION *PVBOXPLAYGROUNDDEVICEFUNCTION;
62
63/**
64 * Playground device instance data.
65 */
66typedef struct VBOXPLAYGROUNDDEVICE
67{
68 /** PCI device functions. */
69 VBOXPLAYGROUNDDEVICEFUNCTION aPciFuns[8];
70} VBOXPLAYGROUNDDEVICE;
71/** Pointer to the instance data of a playground device instance. */
72typedef VBOXPLAYGROUNDDEVICE *PVBOXPLAYGROUNDDEVICE;
73
74
75#define PLAYGROUND_SSM_VERSION 3
76
77
78/*********************************************************************************************************************************
79* Device Functions *
80*********************************************************************************************************************************/
81
82PDMBOTHCBDECL(int) devPlaygroundMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
83{
84 NOREF(pDevIns);
85 NOREF(pvUser);
86 NOREF(GCPhysAddr);
87 NOREF(pv);
88 NOREF(cb);
89 return VINF_SUCCESS;
90}
91
92
93PDMBOTHCBDECL(int) devPlaygroundMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
94{
95 NOREF(pDevIns);
96 NOREF(pvUser);
97 NOREF(GCPhysAddr);
98 NOREF(pv);
99 NOREF(cb);
100 return VINF_SUCCESS;
101}
102
103
104/**
105 * @callback_method_impl{FNPCIIOREGIONMAP}
106 */
107static DECLCALLBACK(int) devPlaygroundMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
108 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
109{
110 RT_NOREF(pPciDev, enmType, cb);
111
112 switch (iRegion)
113 {
114 case 0:
115 case 2:
116 Assert( enmType == (PCIADDRESSSPACE)(PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR64)
117 || enmType == (PCIADDRESSSPACE)(PCI_ADDRESS_SPACE_MEM_PREFETCH | PCI_ADDRESS_SPACE_BAR64));
118 if (GCPhysAddress == NIL_RTGCPHYS)
119 return VINF_SUCCESS; /* We ignore the unmap notification. */
120 return PDMDevHlpMMIOExMap(pDevIns, pPciDev, iRegion, GCPhysAddress);
121
122 default:
123 /* We should never get here */
124 AssertMsgFailedReturn(("Invalid PCI region param in map callback"), VERR_INTERNAL_ERROR);
125 }
126}
127
128
129/**
130 * @callback_method_impl{FNSSMDEVSAVEEXEC}
131 */
132static DECLCALLBACK(int) devPlaygroundSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
133{
134 PVBOXPLAYGROUNDDEVICE pThis = PDMINS_2_DATA(pDevIns, PVBOXPLAYGROUNDDEVICE);
135
136 /* dummy (real devices would need to save their state here) */
137 RT_NOREF(pThis);
138
139 /* Demo of some API stuff - very unusual, think twice if there's no better
140 * solution which doesn't need API interaction. */
141 HRESULT hrc = S_OK;
142 com::Bstr bstrSnapName;
143 com::Guid uuid(COM_IIDOF(ISnapshot));
144 ISnapshot *pSnap = (ISnapshot *)PDMDevHlpQueryGenericUserObject(pDevIns, uuid.raw());
145 if (pSnap)
146 {
147 hrc = pSnap->COMGETTER(Name)(bstrSnapName.asOutParam());
148 AssertComRCReturn(hrc, VERR_INVALID_STATE);
149 }
150 com::Utf8Str strSnapName(bstrSnapName);
151 SSMR3PutStrZ(pSSM, strSnapName.c_str());
152 LogRel(("Playground: saving state of snapshot '%s', hrc=%Rhrc\n", strSnapName.c_str(), hrc));
153
154 return VINF_SUCCESS;
155}
156
157/**
158 * @callback_method_impl{FNSSMDEVLOADEXEC}
159 */
160static DECLCALLBACK(int) devPlaygroundLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
161{
162 PVBOXPLAYGROUNDDEVICE pThis = PDMINS_2_DATA(pDevIns, PVBOXPLAYGROUNDDEVICE);
163
164 if (uVersion > PLAYGROUND_SSM_VERSION)
165 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
166 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
167
168 /* dummy (real devices would need to load their state here) */
169 RT_NOREF(pThis);
170
171 /* Reading the stuff written to saved state, just a demo. */
172 char szSnapName[256];
173 int rc = SSMR3GetStrZ(pSSM, szSnapName, sizeof(szSnapName));
174 AssertRCReturn(rc, rc);
175 LogRel(("Playground: loading state of snapshot '%s'\n", szSnapName));
176
177 return VINF_SUCCESS;
178}
179
180
181/**
182 * @interface_method_impl{PDMDEVREG,pfnConstruct}
183 */
184static DECLCALLBACK(int) devPlaygroundConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
185{
186 RT_NOREF(iInstance, pCfg);
187 int rc = VINF_SUCCESS;
188
189 /*
190 * Check that the device instance and device helper structures are compatible.
191 */
192 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
193
194 /*
195 * Initialize the instance data so that the destructor won't mess up.
196 */
197 PVBOXPLAYGROUNDDEVICE pThis = PDMINS_2_DATA(pDevIns, PVBOXPLAYGROUNDDEVICE);
198
199 /*
200 * Validate and read the configuration.
201 */
202 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Whatever1|NumFunctions|BigBAR0MB|BigBAR0GB|BigBAR2MB|BigBAR2GB", "");
203
204 uint8_t uNumFunctions;
205 rc = CFGMR3QueryU8Def(pCfg, "NumFunctions", &uNumFunctions, RT_ELEMENTS(pThis->aPciFuns));
206 if (RT_FAILURE(rc))
207 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"NumFunctions\""));
208 if ((uNumFunctions < 1) || (uNumFunctions > RT_ELEMENTS(pThis->aPciFuns)))
209 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"NumFunctions\" value (must be between 1 and 8)"));
210
211 RTGCPHYS cbFirstBAR;
212 uint16_t uBigBAR0GB;
213 rc = CFGMR3QueryU16Def(pCfg, "BigBAR0GB", &uBigBAR0GB, 0); /* Default to nothing. */
214 if (RT_FAILURE(rc))
215 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR0GB\""));
216 if (uBigBAR0GB > 512)
217 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR0GB\" value (must be 512 or less)"));
218
219 if (uBigBAR0GB)
220 cbFirstBAR = uBigBAR0GB * _1G64;
221 else
222 {
223 uint16_t uBigBAR0MB;
224 rc = CFGMR3QueryU16Def(pCfg, "BigBAR0MB", &uBigBAR0MB, 8); /* 8 MB default. */
225 if (RT_FAILURE(rc))
226 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR0MB\""));
227 if (uBigBAR0MB < 1 || uBigBAR0MB > 4095)
228 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR0MB\" value (must be between 1 and 4095)"));
229 cbFirstBAR = uBigBAR0MB * _1M;
230 }
231
232 RTGCPHYS cbSecondBAR;
233 uint16_t uBigBAR2GB;
234 rc = CFGMR3QueryU16Def(pCfg, "BigBAR2GB", &uBigBAR2GB, 0); /* Default to nothing. */
235 if (RT_FAILURE(rc))
236 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR2GB\""));
237 if (uBigBAR2GB > 512)
238 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR2GB\" value (must be 512 or less)"));
239
240 if (uBigBAR2GB)
241 cbSecondBAR = uBigBAR2GB * _1G64;
242 else
243 {
244 uint16_t uBigBAR2MB;
245 rc = CFGMR3QueryU16Def(pCfg, "BigBAR2MB", &uBigBAR2MB, 16); /* 16 MB default. */
246 if (RT_FAILURE(rc))
247 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"BigBAR2MB\""));
248 if (uBigBAR2MB < 1 || uBigBAR2MB > 4095)
249 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Invalid \"BigBAR2MB\" value (must be between 1 and 4095)"));
250 cbSecondBAR = uBigBAR2MB * _1M;
251 }
252
253
254 /*
255 * PCI device setup.
256 */
257 uint32_t iPciDevNo = PDMPCIDEVREG_DEV_NO_FIRST_UNUSED;
258 for (uint32_t iPciFun = 0; iPciFun < uNumFunctions; iPciFun++)
259 {
260 PVBOXPLAYGROUNDDEVICEFUNCTION pFun = &pThis->aPciFuns[iPciFun];
261 RTStrPrintf(pFun->szName, sizeof(pThis->aPciFuns[iPciFun].PciDev), "playground%u", iPciFun);
262 pFun->iFun = iPciFun;
263
264 PCIDevSetVendorId( &pFun->PciDev, 0x80ee);
265 PCIDevSetDeviceId( &pFun->PciDev, 0xde4e);
266 PCIDevSetClassBase(&pFun->PciDev, 0x07); /* communications device */
267 PCIDevSetClassSub( &pFun->PciDev, 0x80); /* other communications device */
268 if (iPciFun == 0) /* only for the primary function */
269 PCIDevSetHeaderType(&pFun->PciDev, 0x80); /* normal, multifunction device */
270
271 rc = PDMDevHlpPCIRegisterEx(pDevIns, &pFun->PciDev, iPciFun, 0 /*fFlags*/, iPciDevNo, iPciFun,
272 pThis->aPciFuns[iPciFun].szName);
273 AssertLogRelRCReturn(rc, rc);
274
275 /* First region. */
276 RTGCPHYS const cbFirst = iPciFun == 0 ? cbFirstBAR : iPciFun * _4K;
277 rc = PDMDevHlpPCIIORegionRegisterEx(pDevIns, &pFun->PciDev, 0, cbFirst,
278 (PCIADDRESSSPACE)( PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR64
279 | (iPciFun == 0 ? PCI_ADDRESS_SPACE_MEM_PREFETCH : 0)),
280 devPlaygroundMap);
281 AssertLogRelRCReturn(rc, rc);
282 char *pszRegionName = NULL;
283 RTStrAPrintf(&pszRegionName, "PG-F%d-BAR0", iPciFun);
284 Assert(pszRegionName);
285 rc = PDMDevHlpMMIOExPreRegister(pDevIns, &pFun->PciDev, 0, cbFirst,
286 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, pszRegionName,
287 NULL /*pvUser*/, devPlaygroundMMIOWrite, devPlaygroundMMIORead, NULL /*pfnFill*/,
288 NIL_RTR0PTR /*pvUserR0*/, NULL /*pszWriteR0*/, NULL /*pszReadR0*/, NULL /*pszFillR0*/,
289 NIL_RTRCPTR /*pvUserRC*/, NULL /*pszWriteRC*/, NULL /*pszReadRC*/, NULL /*pszFillRC*/);
290 AssertLogRelRCReturn(rc, rc);
291
292 /* Second region. */
293 RTGCPHYS const cbSecond = iPciFun == 0 ? cbSecondBAR : iPciFun * _32K;
294 rc = PDMDevHlpPCIIORegionRegisterEx(pDevIns, &pFun->PciDev, 2, cbSecond,
295 (PCIADDRESSSPACE)( PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR64
296 | (iPciFun == 0 ? PCI_ADDRESS_SPACE_MEM_PREFETCH : 0)),
297 devPlaygroundMap);
298 AssertLogRelRCReturn(rc, rc);
299 pszRegionName = NULL;
300 RTStrAPrintf(&pszRegionName, "PG-F%d-BAR2", iPciFun);
301 Assert(pszRegionName);
302 rc = PDMDevHlpMMIOExPreRegister(pDevIns, &pFun->PciDev, 2, cbSecond,
303 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, pszRegionName,
304 NULL /*pvUser*/, devPlaygroundMMIOWrite, devPlaygroundMMIORead, NULL /*pfnFill*/,
305 NIL_RTR0PTR /*pvUserR0*/, NULL /*pszWriteR0*/, NULL /*pszReadR0*/, NULL /*pszFillR0*/,
306 NIL_RTRCPTR /*pvUserRC*/, NULL /*pszWriteRC*/, NULL /*pszReadRC*/, NULL /*pszFillRC*/);
307 AssertLogRelRCReturn(rc, rc);
308
309 /* Subsequent function should use the same major as the previous one. */
310 iPciDevNo = PDMPCIDEVREG_DEV_NO_SAME_AS_PREV;
311 }
312
313 /*
314 * Save state handling.
315 */
316 rc = PDMDevHlpSSMRegister(pDevIns, PLAYGROUND_SSM_VERSION, sizeof(*pThis), devPlaygroundSaveExec, devPlaygroundLoadExec);
317 if (RT_FAILURE(rc))
318 return rc;
319
320 return VINF_SUCCESS;
321}
322
323
324/**
325 * @interface_method_impl{PDMDEVREG,pfnDestruct}
326 */
327static DECLCALLBACK(int) devPlaygroundDestruct(PPDMDEVINS pDevIns)
328{
329 /*
330 * Check the versions here as well since the destructor is *always* called.
331 */
332 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
333
334 return VINF_SUCCESS;
335}
336
337
338/**
339 * The device registration structure.
340 */
341static const PDMDEVREG g_DevicePlayground =
342{
343 /* u32Version */
344 PDM_DEVREG_VERSION,
345 /* szName */
346 "playground",
347 /* szRCMod */
348 "",
349 /* szR0Mod */
350 "",
351 /* pszDescription */
352 "VBox Playground Device.",
353 /* fFlags */
354 PDM_DEVREG_FLAGS_DEFAULT_BITS,
355 /* fClass */
356 PDM_DEVREG_CLASS_MISC,
357 /* cMaxInstances */
358 1,
359 /* cbInstance */
360 sizeof(VBOXPLAYGROUNDDEVICE),
361 /* pfnConstruct */
362 devPlaygroundConstruct,
363 /* pfnDestruct */
364 devPlaygroundDestruct,
365 /* pfnRelocate */
366 NULL,
367 /* pfnMemSetup */
368 NULL,
369 /* pfnPowerOn */
370 NULL,
371 /* pfnReset */
372 NULL,
373 /* pfnSuspend */
374 NULL,
375 /* pfnResume */
376 NULL,
377 /* pfnAttach */
378 NULL,
379 /* pfnDetach */
380 NULL,
381 /* pfnQueryInterface */
382 NULL,
383 /* pfnInitComplete */
384 NULL,
385 /* pfnPowerOff */
386 NULL,
387 /* pfnSoftReset */
388 NULL,
389 /* u32VersionEnd */
390 PDM_DEVREG_VERSION
391};
392
393
394/**
395 * Register devices provided by the plugin.
396 *
397 * @returns VBox status code.
398 * @param pCallbacks Pointer to the callback table.
399 * @param u32Version VBox version number.
400 */
401extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
402{
403 LogFlow(("VBoxPlaygroundDevice::VBoxDevicesRegister: u32Version=%#x pCallbacks->u32Version=%#x\n", u32Version, pCallbacks->u32Version));
404
405 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
406 ("VirtualBox version %#x, expected %#x or higher\n", u32Version, VBOX_VERSION),
407 VERR_VERSION_MISMATCH);
408 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
409 ("callback version %#x, expected %#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
410 VERR_VERSION_MISMATCH);
411
412 return pCallbacks->pfnRegister(pCallbacks, &g_DevicePlayground);
413}
414
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