VirtualBox

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

Last change on this file since 81077 was 81031, checked in by vboxsync, 5 years ago

PDM,Devices: Moving the PDMPCIDEV structures into the PDMDEVINS allocation. Preps for extending the config space to 4KB. bugref:9218

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