VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevQemuFwCfg.cpp@ 99535

最後變更 在這個檔案從99535是 99532,由 vboxsync 提交於 23 月 前

Devies/DevQemuFwCfg: Implement support for the RAM based framebuffer, bugref:10431 [doxygen fix]

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 78.1 KB
 
1/* $Id: DevQemuFwCfg.cpp 99532 2023-04-26 06:28:04Z vboxsync $ */
2/** @file
3 * DevQemuFwCfg - QEMU firmware configuration compatible device.
4 */
5
6/*
7 * Copyright (C) 2020-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_qemufwcfg The QEMU firmware configuration Device.
29 *
30 * The QEMU firmware configuration device is a custom device emulation
31 * to convey information about the VM to the guests firmware (UEFI for example).
32 * In the case of VirtualBox it is used to directly load a compatible kernel
33 * and initrd image like Linux from the host into the guest and boot it. This allows
34 * efficiently testing/debugging of multiple Linux kernels without having to install
35 * a guest OS. On VirtualBox the EFI firmware supports this interface, the BIOS is
36 * currently unsupported (and probably never will be).
37 *
38 * @section sec_qemufwcfg_config Configuration
39 *
40 * To use this interface for a particular VM the following extra data needs to be
41 * set besides enabling the EFI firmware:
42 *
43 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/KernelImage" /path/to/kernel
44 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/InitrdImage" /path/to/initrd
45 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/CmdLine" "<cmd line string>"
46 *
47 * The only mandatory item is the KernelImage one, the others are optional if the
48 * kernel is configured to not require it. The InitrdImage item accepts a path to a directory as well.
49 * If a directory is encountered, the CPIO initrd image is created on the fly and passed to the guest.
50 * If the kernel is not an EFI compatible executable (CONFIG_EFI_STUB=y for Linux) a dedicated setup image might be required
51 * which can be set with:
52 *
53 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/SetupImage" /path/to/setup_image
54 *
55 * @section sec_qemufwcfg_dma DMA
56 *
57 * The QEMU firmware configuration device supports an optional DMA interface to speed up transferring the data into the guest.
58 * It currently is not enabled by default but needs to be enabled with:
59 *
60 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/DmaEnabled" 1
61 */
62
63
64/*********************************************************************************************************************************
65* Header Files *
66*********************************************************************************************************************************/
67#define LOG_GROUP LOG_GROUP_DEV_QEMUFWCFG
68#include <VBox/vmm/pdmdev.h>
69#include <VBox/vmm/mm.h>
70#include <VBox/vmm/pgm.h>
71#include <VBox/log.h>
72#include <iprt/errcore.h>
73#include <iprt/assert.h>
74#include <iprt/dir.h>
75#include <iprt/file.h>
76#include <iprt/mem.h>
77#include <iprt/path.h>
78#include <iprt/string.h>
79#include <iprt/vfs.h>
80#include <iprt/zero.h>
81#include <iprt/zip.h>
82#include <iprt/uuid.h>
83
84#include "VBoxDD.h"
85
86
87/*********************************************************************************************************************************
88* Defined Constants And Macros *
89*********************************************************************************************************************************/
90
91/** Start of the I/O port region. */
92#define QEMU_FW_CFG_IO_PORT_START 0x510
93/** Number of I/O ports reserved for this device. */
94#define QEMU_FW_CFG_IO_PORT_SIZE 12
95/** Offset of the config item selector register from the start. */
96#define QEMU_FW_CFG_OFF_SELECTOR 0
97/** Offset of the data port from the start. */
98#define QEMU_FW_CFG_OFF_DATA 1
99/** Offset of the high 32bit of the DMA address. */
100#define QEMU_FW_CFG_OFF_DMA_HIGH 4
101/** Offset of the low 32bit of the DMA address. */
102#define QEMU_FW_CFG_OFF_DMA_LOW 8
103
104
105/** @name MMIO register offsets.
106 * @{ */
107/** Data register offset. */
108#define QEU_FW_CFG_MMIO_OFF_DATA 0
109/** Selector register offset. */
110#define QEU_FW_CFG_MMIO_OFF_SELECTOR 8
111/** DMA base address register offset. */
112#define QEU_FW_CFG_MMIO_OFF_DMA 16
113/** @} */
114
115
116/** Set if legacy interface is supported (always set).*/
117#define QEMU_FW_CFG_VERSION_LEGACY RT_BIT_32(0)
118/** Set if DMA is supported.*/
119#define QEMU_FW_CFG_VERSION_DMA RT_BIT_32(1)
120
121
122/** Error happened during the DMA access. */
123#define QEMU_FW_CFG_DMA_ERROR RT_BIT_32(0)
124/** Read requested. */
125#define QEMU_FW_CFG_DMA_READ RT_BIT_32(1)
126/** Skipping bytes requested. */
127#define QEMU_FW_CFG_DMA_SKIP RT_BIT_32(2)
128/** The config item is selected. */
129#define QEMU_FW_CFG_DMA_SELECT RT_BIT_32(3)
130/** Write requested. */
131#define QEMU_FW_CFG_DMA_WRITE RT_BIT_32(4)
132/** Extracts the selected config item. */
133#define QEMU_FW_CFG_DMA_GET_CFG_ITEM(a_Control) ((uint16_t)((a_Control) >> 16))
134
135/** The signature when reading the DMA address register and the DMA interace is enabled. */
136#define QEMU_FW_CFG_DMA_ADDR_SIGNATURE UINT64_C(0x51454d5520434647) /* "QEMU CFG" */
137
138/** @name Known config items.
139 * @{ */
140#define QEMU_FW_CFG_ITEM_SIGNATURE UINT16_C(0x0000)
141#define QEMU_FW_CFG_ITEM_VERSION UINT16_C(0x0001)
142#define QEMU_FW_CFG_ITEM_SYSTEM_UUID UINT16_C(0x0002)
143#define QEMU_FW_CFG_ITEM_RAM_SIZE UINT16_C(0x0003)
144#define QEMU_FW_CFG_ITEM_GRAPHICS_ENABLED UINT16_C(0x0004)
145#define QEMU_FW_CFG_ITEM_SMP_CPU_COUNT UINT16_C(0x0005)
146#define QEMU_FW_CFG_ITEM_MACHINE_ID UINT16_C(0x0006)
147#define QEMU_FW_CFG_ITEM_KERNEL_ADDRESS UINT16_C(0x0007)
148#define QEMU_FW_CFG_ITEM_KERNEL_SIZE UINT16_C(0x0008)
149#define QEMU_FW_CFG_ITEM_KERNEL_CMD_LINE UINT16_C(0x0009)
150#define QEMU_FW_CFG_ITEM_INITRD_ADDRESS UINT16_C(0x000a)
151#define QEMU_FW_CFG_ITEM_INITRD_SIZE UINT16_C(0x000b)
152#define QEMU_FW_CFG_ITEM_BOOT_DEVICE UINT16_C(0x000c)
153#define QEMU_FW_CFG_ITEM_NUMA_DATA UINT16_C(0x000d)
154#define QEMU_FW_CFG_ITEM_BOOT_MENU UINT16_C(0x000e)
155#define QEMU_FW_CFG_ITEM_MAX_CPU_COUNT UINT16_C(0x000f)
156#define QEMU_FW_CFG_ITEM_KERNEL_ENTRY UINT16_C(0x0010)
157#define QEMU_FW_CFG_ITEM_KERNEL_DATA UINT16_C(0x0011)
158#define QEMU_FW_CFG_ITEM_INITRD_DATA UINT16_C(0x0012)
159#define QEMU_FW_CFG_ITEM_CMD_LINE_ADDRESS UINT16_C(0x0013)
160#define QEMU_FW_CFG_ITEM_CMD_LINE_SIZE UINT16_C(0x0014)
161#define QEMU_FW_CFG_ITEM_CMD_LINE_DATA UINT16_C(0x0015)
162#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_ADDRESS UINT16_C(0x0016)
163#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE UINT16_C(0x0017)
164#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA UINT16_C(0x0018)
165#define QEMU_FW_CFG_ITEM_FILE_DIR UINT16_C(0x0019)
166
167/** The first index for custom file based data. */
168#define QEMU_FW_CFG_ITEM_FILE_USER_FIRST UINT16_C(0x0020)
169/** @} */
170
171/** Maximum number of characters for a config item filename (without the zero terminator. */
172#define QEMU_FW_CFG_ITEM_FILE_NAME_MAX 55
173
174/** The size of the directory entry buffer we're using. */
175#define QEMUFWCFG_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
176
177
178/*********************************************************************************************************************************
179* Structures and Typedefs *
180*********************************************************************************************************************************/
181
182/**
183 * RAM based framebuffer config.
184 */
185#pragma pack(1)
186typedef struct QEMURAMFBCONFIG
187{
188 /** Base physical address of the framebuffer. */
189 uint64_t GCPhysRamfbBase;
190 /** The FourCC code for the image format. */
191 uint32_t u32FourCC;
192 /** Flags for the framebuffer. */
193 uint32_t u32Flags;
194 /** Width of the framebuffer in pixels. */
195 uint32_t cWidth;
196 /** Height of the framebuffer in pixels. */
197 uint32_t cHeight;
198 /** Stride of the framebuffer in bytes. */
199 uint32_t cbStride;
200} QEMURAMFBCONFIG;
201#pragma pack()
202AssertCompileSize(QEMURAMFBCONFIG, 28);
203/** Pointer to a RAM based framebuffer config. */
204typedef QEMURAMFBCONFIG *PQEMURAMFBCONFIG;
205/** Pointer to a const RAM based framebuffer config. */
206typedef const QEMURAMFBCONFIG *PCQEMURAMFBCONFIG;
207
208/** The FourCC format code for RGB (+ ignored byte). */
209#define QEMU_RAMFB_CFG_FORMAT 0x34325258 /* XRGB8888 */
210/** Number of bytes per pixel. */
211#define QEMU_RAMFB_CFG_BPP 4
212
213
214/**
215 * QEMU firmware config DMA descriptor.
216 */
217typedef struct QEMUFWDMADESC
218{
219 /** Control field. */
220 uint32_t u32Ctrl;
221 /** Length of the transfer in bytes. */
222 uint32_t u32Length;
223 /** Address of the buffer to transfer from/to. */
224 uint64_t u64GCPhysBuf;
225} QEMUFWDMADESC;
226AssertCompileSize(QEMUFWDMADESC, 2 * 4 + 8);
227/** Pointer to a QEMU firmware config DMA descriptor. */
228typedef QEMUFWDMADESC *PQEMUFWDMADESC;
229/** Pointer to a const QEMU firmware config DMA descriptor. */
230typedef const QEMUFWDMADESC *PCQEMUFWDMADESC;
231
232
233/**
234 * QEMU firmware config file.
235 */
236typedef struct QEMUFWCFGFILE
237{
238 /** Size of the file in bytes. */
239 uint32_t cbFile;
240 /** The config selector item. */
241 uint16_t uCfgItem;
242 /** Reserved. */
243 uint16_t u16Rsvd;
244 /** The filename as an zero terminated ASCII string. */
245 char szFilename[QEMU_FW_CFG_ITEM_FILE_NAME_MAX + 1];
246} QEMUFWCFGFILE;
247AssertCompileSize(QEMUFWCFGFILE, 64);
248/** Pointer to a QEMU firmware config file. */
249typedef QEMUFWCFGFILE *PQEMUFWCFGFILE;
250/** Pointer to a const QEMU firmware config file. */
251typedef const QEMUFWCFGFILE *PCQEMUFWCFGFILE;
252
253
254/** Pointer to the QEMU firmware config device instance. */
255typedef struct DEVQEMUFWCFG *PDEVQEMUFWCFG;
256/** Pointer to a const configuration item descriptor. */
257typedef const struct QEMUFWCFGITEM *PCQEMUFWCFGITEM;
258
259
260/**
261 * Setup callback for when the guest writes the selector.
262 *
263 * @returns VBox status code.
264 * @param pThis The QEMU fw config device instance.
265 * @param pItem Pointer to the selected item.
266 * @param pcbItem Where to store the size of the item on success.
267 */
268typedef DECLCALLBACKTYPE(int, FNQEMUFWCFGITEMSETUP,(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem));
269/** Pointer to a FNQEMUFWCFGITEMSETUP() function. */
270typedef FNQEMUFWCFGITEMSETUP *PFNQEMUFWCFGITEMSETUP;
271
272
273/**
274 * Read callback to return the data.
275 *
276 * @returns VBox status code.
277 * @param pThis The QEMU fw config device instance.
278 * @param pItem Pointer to the selected item.
279 * @param off Where to start reading from.
280 * @param pvBuf Where to store the read data.
281 * @param cbToRead How much to read.
282 * @param pcbRead Where to store the amount of bytes read.
283 */
284typedef DECLCALLBACKTYPE(int, FNQEMUFWCFGITEMREAD,(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
285 uint32_t cbToRead, uint32_t *pcbRead));
286/** Pointer to a FNQEMUFWCFGITEMREAD() function. */
287typedef FNQEMUFWCFGITEMREAD *PFNQEMUFWCFGITEMREAD;
288
289
290/**
291 * Write callback to receive data.
292 *
293 * @returns VBox status code.
294 * @param pThis The QEMU fw config device instance.
295 * @param pItem Pointer to the selected item.
296 * @param off Where to start writing to.
297 * @param pvBuf The data to write.
298 * @param cbToWrite How much to write.
299 * @param pcbWritten Where to store the amount of bytes written.
300 */
301typedef DECLCALLBACKTYPE(int, FNQEMUFWCFGITEMWRITE,(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, const void *pvBuf,
302 uint32_t cbToWrite, uint32_t *pcbWritten));
303/** Pointer to a FNQEMUFWCFGITEMWRITE() function. */
304typedef FNQEMUFWCFGITEMWRITE *PFNQEMUFWCFGITEMWRITE;
305
306
307/**
308 * Cleans up any allocated resources when the item is de-selected.
309 *
310 * @returns nothing.
311 * @param pThis The QEMU fw config device instance.
312 * @param pItem Pointer to the selected item.
313 */
314typedef DECLCALLBACKTYPE(void, FNQEMUFWCFGITEMCLEANUP,(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem));
315/** Pointer to a FNQEMUFWCFGITEMCLEANUP() function. */
316typedef FNQEMUFWCFGITEMCLEANUP *PFNQEMUFWCFGITEMCLEANUP;
317
318
319/**
320 * A supported configuration item descriptor.
321 */
322typedef struct QEMUFWCFGITEM
323{
324 /** The config item value. */
325 uint16_t uCfgItem;
326 /** Name of the item. */
327 const char *pszItem;
328 /** Optional CFGM key to lookup the content. */
329 const char *pszCfgmKey;
330
331 /** Setup callback. */
332 PFNQEMUFWCFGITEMSETUP pfnSetup;
333 /** Read callback. */
334 PFNQEMUFWCFGITEMREAD pfnRead;
335 /** Write callback. */
336 PFNQEMUFWCFGITEMWRITE pfnWrite;
337 /** Cleanup callback. */
338 PFNQEMUFWCFGITEMCLEANUP pfnCleanup;
339} QEMUFWCFGITEM;
340/** Pointer to a configuration item descriptor. */
341typedef QEMUFWCFGITEM *PQEMUFWCFGITEM;
342
343
344/**
345 * A config file entry.
346 */
347typedef struct QEMUFWCFGFILEENTRY
348{
349 /** The config item structure. */
350 QEMUFWCFGITEM Cfg;
351 /** Size of the file in bytes. */
352 uint32_t cbFile;
353 /** The stored filename as an zero terminated ASCII string. */
354 char szFilename[QEMU_FW_CFG_ITEM_FILE_NAME_MAX + 1];
355} QEMUFWCFGFILEENTRY;
356/** Pointer to a config file entry. */
357typedef QEMUFWCFGFILEENTRY *PQEMUFWCFGFILEENTRY;
358/** Pointer to a const config file entry. */
359typedef const QEMUFWCFGFILEENTRY *PCQEMUFWCFGFILEENTRY;
360
361
362/**
363 * QEMU firmware config instance data structure.
364 */
365typedef struct DEVQEMUFWCFG
366{
367 /** Pointer back to the device instance. */
368 PPDMDEVINS pDevIns;
369 /** The configuration handle. */
370 PCFGMNODE pCfg;
371
372 /** LUN\#0: The display port base interface. */
373 PDMIBASE IBase;
374 /** LUN\#0: The display port interface for the RAM based framebuffer if enabled. */
375 PDMIDISPLAYPORT IPortRamfb;
376
377 /** Pointer to base interface of the driver - LUN#0. */
378 R3PTRTYPE(PPDMIBASE) pDrvBaseL0;
379 /** Pointer to display connector interface of the driver - LUN#0. */
380 R3PTRTYPE(PPDMIDISPLAYCONNECTOR) pDrvL0;
381
382 /** Pointer to the currently selected item. */
383 PCQEMUFWCFGITEM pCfgItem;
384 /** Offset of the next byte to read from the start of the data item. */
385 uint32_t offCfgItemNext;
386 /** How many bytes are left for transfer. */
387 uint32_t cbCfgItemLeft;
388 /** Version register. */
389 uint32_t u32Version;
390 /** Guest physical address of the DMA descriptor. */
391 RTGCPHYS GCPhysDma;
392 /** VFS file of the on-the-fly created initramfs. */
393 RTVFSFILE hVfsFileInitrd;
394
395 /** Pointer to the array of config file items. */
396 PQEMUFWCFGFILEENTRY paCfgFiles;
397 /** Number of entries in the config file item array. */
398 uint32_t cCfgFiles;
399 /** Number if entries allocated in the config file items array. */
400 uint32_t cCfgFilesMax;
401
402 /** Critical section for synchronizing the RAM framebuffer access. */
403 PDMCRITSECT CritSectRamfb;
404 /** The refresh interval for the Ramfb support. */
405 uint32_t cMilliesRefreshInterval;
406 /** Refresh timer handle for the Ramfb support. */
407 TMTIMERHANDLE hRamfbRefreshTimer;
408 /** The current rambuffer config if enabled. */
409 QEMURAMFBCONFIG RamfbCfg;
410 /** Flag whether rendering the VRAM is enabled currently. */
411 bool fRenderVRam;
412 /** Flag whether the RAM based framebuffer device is enabled. */
413 bool fRamfbSupported;
414 /** Flag whether the DMA interface is available. */
415 bool fDmaEnabled;
416
417 /** Scratch buffer for config item specific data. */
418 union
419 {
420 uint8_t u8;
421 uint16_t u16;
422 uint32_t u32;
423 uint64_t u64;
424 /** VFS file handle. */
425 RTVFSFILE hVfsFile;
426 /** Firmware config file entry. */
427 QEMUFWCFGFILE CfgFile;
428 /** Byte view. */
429 uint8_t ab[8];
430 } u;
431} DEVQEMUFWCFG;
432
433
434/**
435 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the signature configuration item.}
436 */
437static DECLCALLBACK(int) qemuFwCfgR3SetupSignature(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
438{
439 RT_NOREF(pThis, pItem);
440 uint8_t abSig[] = { 'Q', 'E', 'M', 'U' };
441 memcpy(&pThis->u.ab[0], &abSig[0], sizeof(abSig));
442 *pcbItem = sizeof(abSig);
443 return VINF_SUCCESS;
444}
445
446
447/**
448 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the version configuration item.}
449 */
450static DECLCALLBACK(int) qemuFwCfgR3SetupVersion(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
451{
452 RT_NOREF(pThis, pItem);
453 memcpy(&pThis->u.ab[0], &pThis->u32Version, sizeof(pThis->u32Version));
454 *pcbItem = sizeof(pThis->u32Version);
455 return VINF_SUCCESS;
456}
457
458
459/**
460 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the file directory configuration item.}
461 */
462static DECLCALLBACK(int) qemuFwCfgR3SetupFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
463{
464 RT_NOREF(pItem);
465 uint32_t cCfgFiles = RT_H2BE_U32(pThis->cCfgFiles);
466 memcpy(&pThis->u.ab[0], &cCfgFiles, sizeof(cCfgFiles));
467 *pcbItem = sizeof(uint32_t) + pThis->cCfgFiles * sizeof(QEMUFWCFGFILE);
468 return VINF_SUCCESS;
469}
470
471
472/**
473 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a VFS file type configuration item.}
474 */
475static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFileSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
476{
477 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
478
479 int rc = VINF_SUCCESS;
480 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
481 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_SIZE
482 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
483 {
484 RTVfsFileRetain(pThis->hVfsFileInitrd);
485 hVfsFile = pThis->hVfsFileInitrd;
486 }
487 else
488 {
489 /* Query the path from the CFGM key. */
490 char *pszFilePath = NULL;
491 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
492 if (RT_SUCCESS(rc))
493 {
494 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
495 if (RT_FAILURE(rc))
496 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
497 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
498 }
499 else
500 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
501 }
502
503 if (RT_SUCCESS(rc))
504 {
505 uint64_t cbFile = 0;
506 rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
507 if (RT_SUCCESS(rc))
508 {
509 if (cbFile < _4G)
510 {
511 pThis->u.u32 = (uint32_t)cbFile;
512 *pcbItem = sizeof(uint32_t);
513 }
514 else
515 {
516 rc = VERR_BUFFER_OVERFLOW;
517 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
518 }
519 }
520 else
521 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
522 RTVfsFileRelease(hVfsFile);
523 }
524
525 return rc;
526}
527
528
529/**
530 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a string type configuration item.}
531 */
532static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStrSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
533{
534 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
535
536 /* Query the string from the CFGM key. */
537 char sz[_4K];
538 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
539 if (RT_SUCCESS(rc))
540 {
541 pThis->u.u32 = (uint32_t)strlen(&sz[0]) + 1;
542 *pcbItem = sizeof(uint32_t);
543 }
544 else
545 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
546
547 return rc;
548}
549
550
551/**
552 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a string type configuration item gathered from CFGM.}
553 */
554static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
555{
556 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
557
558 /* Query the string from the CFGM key. */
559 char sz[_4K];
560 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
561 if (RT_SUCCESS(rc))
562 *pcbItem = (uint32_t)strlen(&sz[0]) + 1;
563 else
564 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
565
566 return rc;
567}
568
569
570/**
571 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a VFS file type configuration item.}
572 */
573static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
574{
575 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
576
577 int rc = VINF_SUCCESS;
578 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_DATA
579 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
580 {
581 RTVfsFileRetain(pThis->hVfsFileInitrd);
582 pThis->u.hVfsFile = pThis->hVfsFileInitrd;
583 }
584 else
585 {
586 /* Query the path from the CFGM key. */
587 char *pszFilePath = NULL;
588 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
589 if (RT_SUCCESS(rc))
590 {
591 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &pThis->u.hVfsFile);
592 if (RT_FAILURE(rc))
593 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
594 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
595 }
596 else
597 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
598 }
599
600 if (RT_SUCCESS(rc))
601 {
602 uint64_t cbFile = 0;
603 rc = RTVfsFileQuerySize(pThis->u.hVfsFile, &cbFile);
604 if (RT_SUCCESS(rc))
605 {
606 if (cbFile < _4G)
607 *pcbItem = (uint32_t)cbFile;
608 else
609 {
610 rc = VERR_BUFFER_OVERFLOW;
611 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
612 }
613 }
614 else
615 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
616 }
617
618 return rc;
619}
620
621
622/**
623 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a configuration item having its data stored in the scratch buffer.}
624 */
625static DECLCALLBACK(int) qemuFwCfgR3ReadSimple(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
626 uint32_t cbToRead, uint32_t *pcbRead)
627{
628 RT_NOREF(pThis, pItem);
629 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
630 *pcbRead = cbToRead;
631 return VINF_SUCCESS;
632}
633
634
635/**
636 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a VFS file type configuration item.}
637 */
638static DECLCALLBACK(int) qemuFwCfgR3ReadVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
639 uint32_t cbToRead, uint32_t *pcbRead)
640{
641 RT_NOREF(pItem);
642 size_t cbRead = 0;
643 int rc = RTVfsFileReadAt(pThis->u.hVfsFile, off, pvBuf, cbToRead, &cbRead);
644 if (RT_SUCCESS(rc))
645 *pcbRead = (uint32_t)cbRead;
646
647 return rc;
648}
649
650
651/**
652 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads a string item gathered from CFGM.}
653 */
654static DECLCALLBACK(int) qemuFwCfgR3ReadStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
655 uint32_t cbToRead, uint32_t *pcbRead)
656{
657 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
658
659 /* Query the string from the CFGM key. */
660 char sz[_4K];
661 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
662 if (RT_SUCCESS(rc))
663 {
664 uint32_t cch = (uint32_t)strlen(sz) + 1;
665 if (off < cch)
666 {
667 uint32_t cbRead = RT_MIN(cbToRead, off - cch);
668 memcpy(pvBuf, &sz[off], cbRead);
669 *pcbRead = cbRead;
670 }
671 else
672 rc = VERR_BUFFER_OVERFLOW;
673 }
674 else
675 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
676
677 return rc;
678}
679
680
681/**
682 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from the file directory.}
683 */
684static DECLCALLBACK(int) qemuFwCfgR3ReadFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
685 uint32_t cbToRead, uint32_t *pcbRead)
686{
687 RT_NOREF(pItem);
688
689 /* The first 4 bytes are the number of entries following. */
690 if (off < sizeof(uint32_t))
691 {
692 cbToRead = RT_MIN(cbToRead, sizeof(uint32_t) - off);
693 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
694 *pcbRead = cbToRead;
695 }
696 else
697 {
698 off -= sizeof(uint32_t);
699
700 /* The entries are static, so we can deduce the entry number from the offset. */
701 uint32_t idxEntry = off / sizeof(*pThis->paCfgFiles);
702 AssertReturn(idxEntry < pThis->cCfgFiles, VERR_INTERNAL_ERROR);
703
704 off %= sizeof(*pThis->paCfgFiles);
705 cbToRead = RT_MIN(cbToRead, sizeof(pThis->u.CfgFile));
706
707 /* Setup the config file item. */
708 PCQEMUFWCFGFILEENTRY pEntry = &pThis->paCfgFiles[idxEntry];
709 pThis->u.CfgFile.cbFile = RT_H2BE_U32(pEntry->cbFile);
710 pThis->u.CfgFile.uCfgItem = RT_H2BE_U16(pEntry->Cfg.uCfgItem);
711 pThis->u.CfgFile.u16Rsvd = 0;
712 strncpy(&pThis->u.CfgFile.szFilename[0], pEntry->Cfg.pszItem, sizeof(pThis->u.CfgFile.szFilename));
713 pThis->u.CfgFile.szFilename[QEMU_FW_CFG_ITEM_FILE_NAME_MAX] = '\0';
714
715 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
716 *pcbRead = cbToRead;
717 }
718 return VINF_SUCCESS;
719}
720
721
722/**
723 * @interface_method_impl{QEMUFWCFGITEM,pfnCleanup, Cleans up a VFS file type configuration item.}
724 */
725static DECLCALLBACK(void) qemuFwCfgR3CleanupVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem)
726{
727 RT_NOREF(pItem);
728 RTVfsFileRelease(pThis->u.hVfsFile);
729 pThis->u.hVfsFile = NIL_RTVFSFILE;
730}
731
732
733/**
734 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Generic setup routine for file entries which don't have a dedicated setup routine.}
735 */
736static DECLCALLBACK(int) qemuFwCfgR3SetupFileGeneric(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
737{
738 RT_NOREF(pItem);
739 *pcbItem = pThis->paCfgFiles[pItem->uCfgItem - QEMU_FW_CFG_ITEM_FILE_USER_FIRST].cbFile;
740 return VINF_SUCCESS;
741}
742
743
744/**
745 * Supported config items.
746 */
747static const QEMUFWCFGITEM g_aQemuFwCfgItems[] =
748{
749 /** u16Selector pszItem pszCfgmKey pfnSetup pfnRead pfnWrite pfnCleanup */
750 { QEMU_FW_CFG_ITEM_SIGNATURE, "Signature", NULL, qemuFwCfgR3SetupSignature, qemuFwCfgR3ReadSimple, NULL, NULL },
751 { QEMU_FW_CFG_ITEM_VERSION, "Version", NULL, qemuFwCfgR3SetupVersion, qemuFwCfgR3ReadSimple, NULL, NULL },
752 { QEMU_FW_CFG_ITEM_KERNEL_SIZE, "KrnlSz", "KernelImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
753 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "KrnlDat", "KernelImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
754 { QEMU_FW_CFG_ITEM_INITRD_SIZE, "InitrdSz", "InitrdImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
755 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "InitrdDat", "InitrdImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
756 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE, "SetupSz", "SetupImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
757 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA, "SetupDat", "SetupImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
758 { QEMU_FW_CFG_ITEM_CMD_LINE_SIZE, "CmdLineSz", "CmdLine", qemuFwCfgR3SetupCfgmStrSz, qemuFwCfgR3ReadSimple, NULL, NULL },
759 { QEMU_FW_CFG_ITEM_CMD_LINE_DATA, "CmdLineDat", "CmdLine", qemuFwCfgR3SetupCfgmStr, qemuFwCfgR3ReadStr, NULL, NULL },
760 { QEMU_FW_CFG_ITEM_FILE_DIR, "FileDir", NULL, qemuFwCfgR3SetupFileDir, qemuFwCfgR3ReadFileDir, NULL, NULL }
761};
762
763
764/**
765 * Resets the currently selected item.
766 *
767 * @returns nothing.
768 * @param pThis The QEMU fw config device instance.
769 */
770static void qemuFwCfgR3ItemReset(PDEVQEMUFWCFG pThis)
771{
772 if ( pThis->pCfgItem
773 && pThis->pCfgItem->pfnCleanup)
774 pThis->pCfgItem->pfnCleanup(pThis, pThis->pCfgItem);
775
776 pThis->pCfgItem = NULL;
777 pThis->offCfgItemNext = 0;
778 pThis->cbCfgItemLeft = 0;
779}
780
781
782/**
783 * Selects the given config item.
784 *
785 * @returns VBox status code.
786 * @param pThis The QEMU fw config device instance.
787 * @param uCfgItem The configuration item to select.
788 */
789static int qemuFwCfgItemSelect(PDEVQEMUFWCFG pThis, uint16_t uCfgItem)
790{
791 LogFlowFunc(("uCfgItem=%#x\n", uCfgItem));
792
793 qemuFwCfgR3ItemReset(pThis);
794
795 PCQEMUFWCFGITEM pCfgItem = NULL;;
796
797 /* Check whether this is a file item. */
798 if (uCfgItem >= QEMU_FW_CFG_ITEM_FILE_USER_FIRST)
799 {
800 uCfgItem -= QEMU_FW_CFG_ITEM_FILE_USER_FIRST;
801 if (uCfgItem < pThis->cCfgFiles)
802 pCfgItem = &pThis->paCfgFiles[uCfgItem].Cfg;
803 }
804 else
805 {
806 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQemuFwCfgItems); i++)
807 {
808 pCfgItem = &g_aQemuFwCfgItems[i];
809 if (pCfgItem->uCfgItem == uCfgItem)
810 break;
811 }
812 }
813
814 if (pCfgItem)
815 {
816 uint32_t cbItem = 0;
817 AssertPtrReturn(pCfgItem->pfnSetup, VERR_INVALID_STATE);
818
819 int rc = pCfgItem->pfnSetup(pThis, pCfgItem, &cbItem);
820 if (RT_SUCCESS(rc))
821 {
822 pThis->pCfgItem = pCfgItem;
823 pThis->cbCfgItemLeft = cbItem;
824 return VINF_SUCCESS;
825 }
826
827 return rc;
828 }
829
830 return VERR_NOT_FOUND;
831}
832
833
834/**
835 * Processes a DMA transfer.
836 *
837 * @returns nothing.
838 * @param pThis The QEMU fw config device instance.
839 * @param GCPhysDma The guest physical address of the DMA descriptor.
840 */
841static void qemuFwCfgDmaXfer(PDEVQEMUFWCFG pThis, RTGCPHYS GCPhysDma)
842{
843 QEMUFWDMADESC DmaDesc; RT_ZERO(DmaDesc);
844
845 LogFlowFunc(("pThis=%p GCPhysDma=%RGp\n", pThis, GCPhysDma));
846
847 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysDma, &DmaDesc, sizeof(DmaDesc));
848
849 /* Convert from big endianess to host endianess. */
850 DmaDesc.u32Ctrl = RT_BE2H_U32(DmaDesc.u32Ctrl);
851 DmaDesc.u32Length = RT_BE2H_U32(DmaDesc.u32Length);
852 DmaDesc.u64GCPhysBuf = RT_BE2H_U64(DmaDesc.u64GCPhysBuf);
853
854 LogFlowFunc(("u32Ctrl=%#x u32Length=%u u64GCPhysBuf=%llx\n",
855 DmaDesc.u32Ctrl, DmaDesc.u32Length, DmaDesc.u64GCPhysBuf));
856
857 /* If the select bit is set a select is performed. */
858 int rc = VINF_SUCCESS;
859 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_SELECT)
860 rc = qemuFwCfgItemSelect(pThis, QEMU_FW_CFG_DMA_GET_CFG_ITEM(DmaDesc.u32Ctrl));
861
862 if (RT_SUCCESS(rc))
863 {
864 if ( ( DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE
865 && !pThis->pCfgItem->pfnWrite)
866 || ( DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ
867 && !pThis->pCfgItem->pfnRead))
868 rc = VERR_NOT_SUPPORTED;
869 else if ( !pThis->pCfgItem
870 || !pThis->cbCfgItemLeft)
871 {
872 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
873 {
874 /* Item is not supported, just zero out the indicated area. */
875 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
876 uint32_t cbLeft = DmaDesc.u32Length;
877
878 while ( RT_SUCCESS(rc)
879 && cbLeft)
880 {
881 uint32_t cbZero = RT_MIN(_64K, cbLeft);
882
883 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &g_abRTZero64K[0], cbZero);
884
885 cbLeft -= cbZero;
886 GCPhysCur += cbZero;
887 }
888 }
889 /* else: Assume Skip or Write and ignore. */
890 }
891 else
892 {
893 /* Normal path. */
894 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
895 uint32_t cbLeft = RT_MIN(DmaDesc.u32Length, pThis->cbCfgItemLeft);
896
897 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE)
898 {
899 while ( RT_SUCCESS(rc)
900 && cbLeft)
901 {
902 uint8_t abTmp[_1K];
903 uint32_t cbThisWrite = RT_MIN(sizeof(abTmp), cbLeft);
904 uint32_t cbWritten;
905
906 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbThisWrite);
907 rc = pThis->pCfgItem->pfnWrite(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
908 cbThisWrite, &cbWritten);
909 if (RT_SUCCESS(rc))
910 {
911 cbLeft -= cbWritten;
912 GCPhysCur += cbWritten;
913
914 pThis->offCfgItemNext += cbWritten;
915 pThis->cbCfgItemLeft -= cbWritten;
916 }
917 }
918 }
919 else
920 {
921 while ( RT_SUCCESS(rc)
922 && cbLeft)
923 {
924 uint8_t abTmp[_1K];
925 uint32_t cbThisRead = RT_MIN(sizeof(abTmp), cbLeft);
926 uint32_t cbRead;
927
928 rc = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
929 cbThisRead, &cbRead);
930 if (RT_SUCCESS(rc))
931 {
932 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
933 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbRead);
934 /* else: Assume Skip */
935
936 cbLeft -= cbRead;
937 GCPhysCur += cbRead;
938
939 pThis->offCfgItemNext += cbRead;
940 pThis->cbCfgItemLeft -= cbRead;
941 }
942 }
943 }
944 }
945 }
946
947 LogFlowFunc(("pThis=%p GCPhysDma=%RGp -> %Rrc\n", pThis, GCPhysDma, rc));
948
949 /* Write back the control field. */
950 uint32_t u32Resp = RT_SUCCESS(rc) ? 0 : RT_H2BE_U32(QEMU_FW_CFG_DMA_ERROR);
951 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysDma, &u32Resp, sizeof(u32Resp));
952}
953
954
955/**
956 * @callback_method_impl{FNIOMIOPORTNEWOUT, QEMU firmware configuration write.}
957 */
958static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
959{
960 int rc = VINF_SUCCESS;
961 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
962 NOREF(pvUser);
963
964 LogFlowFunc(("offPort=%RTiop u32=%#x cb=%u\n", offPort, u32, cb));
965
966 switch (offPort)
967 {
968 case QEMU_FW_CFG_OFF_SELECTOR:
969 {
970 if (cb == 2)
971 qemuFwCfgItemSelect(pThis, (uint16_t)u32);
972 break;
973 }
974 case QEMU_FW_CFG_OFF_DATA: /* Readonly, ignore */
975 break;
976 case QEMU_FW_CFG_OFF_DMA_HIGH:
977 {
978 if (cb == 4)
979 pThis->GCPhysDma = ((RTGCPHYS)RT_BE2H_U32(u32)) << 32;
980 break;
981 }
982 case QEMU_FW_CFG_OFF_DMA_LOW:
983 {
984 if (cb == 4)
985 {
986 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U32(u32));
987 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
988 pThis->GCPhysDma = 0;
989 }
990 break;
991 }
992 default:
993 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", offPort, cb, u32);
994 break;
995 }
996
997 LogFlowFunc((" -> rc=%Rrc\n", rc));
998 return rc;
999}
1000
1001
1002/**
1003 * @callback_method_impl{FNIOMIOPORTNEWIN, QEMU firmware configuration read.}
1004 */
1005static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1006{
1007 int rc = VINF_SUCCESS;
1008 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1009 NOREF(pvUser);
1010
1011 *pu32 = 0;
1012
1013 LogFlowFunc(("offPort=%RTiop cb=%u\n", offPort, cb));
1014
1015 switch (offPort)
1016 {
1017 /* Selector (Writeonly, ignore). */
1018 case QEMU_FW_CFG_OFF_SELECTOR:
1019 break;
1020 case QEMU_FW_CFG_OFF_DATA:
1021 {
1022 if (cb == 1)
1023 {
1024 if ( pThis->cbCfgItemLeft
1025 && pThis->pCfgItem)
1026 {
1027 uint8_t bRead = 0;
1028 uint32_t cbRead = 0;
1029 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &bRead,
1030 sizeof(bRead), &cbRead);
1031 if ( RT_SUCCESS(rc2)
1032 && cbRead == sizeof(bRead))
1033 {
1034 pThis->offCfgItemNext += cbRead;
1035 pThis->cbCfgItemLeft -= cbRead;
1036 *pu32 = bRead;
1037 }
1038 }
1039 }
1040 else
1041 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
1042 break;
1043 }
1044
1045 default:
1046 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
1047 break;
1048 }
1049
1050 LogFlowFunc(("offPort=%RTiop cb=%u -> rc=%Rrc u32=%#x\n", offPort, cb, rc, *pu32));
1051
1052 return rc;
1053}
1054
1055
1056/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
1057
1058
1059/**
1060 * @callback_method_impl{FNIOMMMIONEWREAD}
1061 */
1062static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1063{
1064 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1065 RT_NOREF(pvUser);
1066
1067 LogFlowFunc(("%RGp cb=%u\n", off, cb));
1068
1069 AssertReturn(cb <= sizeof(uint64_t), VERR_INVALID_PARAMETER);
1070
1071 VBOXSTRICTRC rc = VINF_SUCCESS;
1072 switch (off)
1073 {
1074 case QEU_FW_CFG_MMIO_OFF_DATA:
1075 {
1076 if ( pThis->cbCfgItemLeft
1077 && pThis->pCfgItem)
1078 {
1079 uint32_t cbRead = 0;
1080 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, pv,
1081 cb, &cbRead);
1082 if (RT_SUCCESS(rc2))
1083 {
1084 pThis->offCfgItemNext += cbRead;
1085 pThis->cbCfgItemLeft -= cbRead;
1086 }
1087 }
1088 else
1089 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1090 break;
1091 }
1092 case QEU_FW_CFG_MMIO_OFF_DMA:
1093 if ( cb == sizeof(uint64_t)
1094 && pThis->fDmaEnabled)
1095 *(uint64_t *)pv = RT_H2BE_U64(QEMU_FW_CFG_DMA_ADDR_SIGNATURE);
1096 else
1097 rc = VINF_IOM_MMIO_UNUSED_00;
1098 break;
1099 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
1100 /* Writeonly, ignore. */
1101 rc = VINF_IOM_MMIO_UNUSED_00;
1102 break;
1103 default:
1104 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1105 break;
1106 }
1107
1108 return rc;
1109}
1110
1111
1112/**
1113 * @callback_method_impl{FNIOMMMIONEWWRITE}
1114 */
1115static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1116{
1117 int rc = VINF_SUCCESS;
1118 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1119 RT_NOREF(pvUser);
1120
1121 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
1122
1123 switch (off)
1124 {
1125 case QEU_FW_CFG_MMIO_OFF_DATA: /* Readonly, ignore */
1126 break;
1127 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
1128 {
1129 if (cb == sizeof(uint16_t))
1130 qemuFwCfgItemSelect(pThis, RT_BE2H_U16(*(uint16_t *)pv));
1131 else
1132 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1133 break;
1134 }
1135 case QEU_FW_CFG_MMIO_OFF_DMA:
1136 {
1137 if (cb == sizeof(uint64_t))
1138 {
1139 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U64(*(uint64_t *)pv));
1140 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
1141 pThis->GCPhysDma = 0;
1142 }
1143 else
1144 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1145 break;
1146 }
1147 default:
1148 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1149 break;
1150 }
1151
1152 LogFlowFunc((" -> rc=%Rrc\n", rc));
1153 return rc;
1154}
1155
1156
1157/**
1158 * Sets up the initrd memory file and CPIO filesystem stream for writing.
1159 *
1160 * @returns VBox status code.
1161 * @param pThis The QEMU fw config device instance.
1162 * @param phVfsFss Where to return the filesystem stream handle.
1163 */
1164static int qemuFwCfgCreateOutputArchive(PDEVQEMUFWCFG pThis, PRTVFSFSSTREAM phVfsFss)
1165{
1166 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &pThis->hVfsFileInitrd);
1167 if (RT_SUCCESS(rc))
1168 {
1169 RTVFSFSSTREAM hVfsFss;
1170 RTVFSIOSTREAM hVfsIosOut = RTVfsFileToIoStream(pThis->hVfsFileInitrd);
1171 rc = RTZipTarFsStreamToIoStream(hVfsIosOut, RTZIPTARFORMAT_CPIO_ASCII_NEW, 0 /*fFlags*/, &hVfsFss);
1172 if (RT_SUCCESS(rc))
1173 {
1174 rc = RTZipTarFsStreamSetOwner(hVfsFss, 0, "root");
1175 if (RT_SUCCESS(rc))
1176 rc = RTZipTarFsStreamSetGroup(hVfsFss, 0, "root");
1177
1178 if (RT_SUCCESS(rc))
1179 *phVfsFss = hVfsFss;
1180 else
1181 {
1182 RTVfsFsStrmRelease(hVfsFss);
1183 *phVfsFss = NIL_RTVFSFSSTREAM;
1184 }
1185 }
1186 RTVfsIoStrmRelease(hVfsIosOut);
1187 }
1188
1189 return rc;
1190}
1191
1192
1193/**
1194 * Archives a file.
1195 *
1196 * @returns VBox status code.
1197 * @param hVfsFss The TAR filesystem stream handle.
1198 * @param pszSrc The file path or VFS spec.
1199 * @param pszDst The name to archive the file under.
1200 * @param pErrInfo Error info buffer (saves stack space).
1201 */
1202static int qemuFwCfgInitrdArchiveFile(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
1203 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
1204{
1205 /* Open the file. */
1206 uint32_t offError;
1207 RTVFSIOSTREAM hVfsIosSrc;
1208 int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1209 &hVfsIosSrc, &offError, RTErrInfoInitStatic(pErrInfo));
1210 if (RT_FAILURE(rc))
1211 return rc;
1212
1213 /* I/O stream to base object. */
1214 RTVFSOBJ hVfsObjSrc = RTVfsObjFromIoStream(hVfsIosSrc);
1215 if (hVfsObjSrc != NIL_RTVFSOBJ)
1216 {
1217 /*
1218 * Add it to the stream.
1219 */
1220 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1221 RTVfsIoStrmRelease(hVfsIosSrc);
1222 RTVfsObjRelease(hVfsObjSrc);
1223 return rc;
1224 }
1225 RTVfsIoStrmRelease(hVfsIosSrc);
1226 return VERR_INVALID_POINTER;
1227}
1228
1229
1230/**
1231 * Archives a symlink.
1232 *
1233 * @returns VBox status code.
1234 * @param hVfsFss The TAR filesystem stream handle.
1235 * @param pszSrc The file path or VFS spec.
1236 * @param pszDst The name to archive the file under.
1237 * @param pErrInfo Error info buffer (saves stack space).
1238 */
1239static int qemuFwCfgInitrdArchiveSymlink(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
1240 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
1241{
1242 /* Open the file. */
1243 uint32_t offError;
1244 RTVFSOBJ hVfsObjSrc;
1245 int rc = RTVfsChainOpenObj(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1246 RTVFSOBJ_F_OPEN_SYMLINK | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
1247 &hVfsObjSrc, &offError, RTErrInfoInitStatic(pErrInfo));
1248 if (RT_FAILURE(rc))
1249 return rc;
1250
1251 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1252 RTVfsObjRelease(hVfsObjSrc);
1253 return rc;
1254}
1255
1256
1257/**
1258 * Sub-directory helper for creating archives.
1259 *
1260 * @returns VBox status code.
1261 * @param hVfsFss The TAR filesystem stream handle.
1262 * @param pszSrc The directory path or VFS spec. We append to the
1263 * buffer as we decend.
1264 * @param cchSrc The length of the input.
1265 * @param pszDst The name to archive it the under. We append to the
1266 * buffer as we decend.
1267 * @param cchDst The length of the input.
1268 * @param pDirEntry Directory entry to use for the directory to handle.
1269 * @param pErrInfo Error info buffer (saves stack space).
1270 */
1271static int qemuFwCfgInitrdArchiveDirSub(RTVFSFSSTREAM hVfsFss,
1272 char *pszSrc, size_t cchSrc,
1273 char pszDst[RTPATH_MAX], size_t cchDst, PRTDIRENTRYEX pDirEntry,
1274 PRTERRINFOSTATIC pErrInfo)
1275{
1276 uint32_t offError;
1277 RTVFSDIR hVfsIoDir;
1278 int rc = RTVfsChainOpenDir(pszSrc, 0 /*fFlags*/,
1279 &hVfsIoDir, &offError, RTErrInfoInitStatic(pErrInfo));
1280 if (RT_FAILURE(rc))
1281 return rc;
1282
1283 /* Make sure we've got some room in the path, to save us extra work further down. */
1284 if (cchSrc + 3 >= RTPATH_MAX)
1285 return VERR_FILENAME_TOO_LONG;
1286
1287 /* Ensure we've got a trailing slash (there is space for it see above). */
1288 if (!RTPATH_IS_SEP(pszSrc[cchSrc - 1]))
1289 {
1290 pszSrc[cchSrc++] = RTPATH_SLASH;
1291 pszSrc[cchSrc] = '\0';
1292 }
1293
1294 /* Ditto for destination. */
1295 if (cchDst + 3 >= RTPATH_MAX)
1296 return VERR_FILENAME_TOO_LONG;
1297
1298 /* Don't try adding the root directory. */
1299 if (*pszDst != '\0')
1300 {
1301 RTVFSOBJ hVfsObjSrc = RTVfsObjFromDir(hVfsIoDir);
1302 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1303 RTVfsObjRelease(hVfsObjSrc);
1304 if (RT_FAILURE(rc))
1305 return rc;
1306
1307 if (!RTPATH_IS_SEP(pszDst[cchDst - 1]))
1308 {
1309 pszDst[cchDst++] = RTPATH_SLASH;
1310 pszDst[cchDst] = '\0';
1311 }
1312 }
1313
1314 /*
1315 * Process the files and subdirs.
1316 */
1317 for (;;)
1318 {
1319 size_t cbDirEntry = QEMUFWCFG_DIRENTRY_BUF_SIZE;
1320 rc = RTVfsDirReadEx(hVfsIoDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1321 if (RT_FAILURE(rc))
1322 break;
1323
1324 /* Check length. */
1325 if (pDirEntry->cbName + cchSrc + 3 >= RTPATH_MAX)
1326 {
1327 rc = VERR_BUFFER_OVERFLOW;
1328 break;
1329 }
1330
1331 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1332 {
1333 case RTFS_TYPE_DIRECTORY:
1334 {
1335 if (RTDirEntryExIsStdDotLink(pDirEntry))
1336 continue;
1337
1338 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1339 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1340 rc = qemuFwCfgInitrdArchiveDirSub(hVfsFss, pszSrc, cchSrc + pDirEntry->cbName,
1341 pszDst, cchDst + pDirEntry->cbName, pDirEntry, pErrInfo);
1342 break;
1343 }
1344
1345 case RTFS_TYPE_FILE:
1346 {
1347 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1348 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1349 rc = qemuFwCfgInitrdArchiveFile(hVfsFss, pszSrc, pszDst, pErrInfo);
1350 break;
1351 }
1352
1353 case RTFS_TYPE_SYMLINK:
1354 {
1355 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1356 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1357 rc = qemuFwCfgInitrdArchiveSymlink(hVfsFss, pszSrc, pszDst, pErrInfo);
1358 break;
1359 }
1360
1361 default:
1362 {
1363 LogRel(("Warning: File system type %#x for '%s' not implemented yet, sorry! Skipping ...\n",
1364 pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK, pDirEntry->szName));
1365 break;
1366 }
1367 }
1368 }
1369
1370 if (rc == VERR_NO_MORE_FILES)
1371 rc = VINF_SUCCESS;
1372
1373 RTVfsDirRelease(hVfsIoDir);
1374 return rc;
1375}
1376
1377
1378/**
1379 * Archives a directory recursively.
1380 *
1381 * @returns VBox status code.
1382 * @param hVfsFss The CPIO filesystem stream handle.
1383 * @param pszSrc The directory path or VFS spec. We append to the
1384 * buffer as we decend.
1385 * @param cchSrc The length of the input.
1386 * @param pszDst The name to archive it the under. We append to the
1387 * buffer as we decend.
1388 * @param cchDst The length of the input.
1389 * @param pErrInfo Error info buffer (saves stack space).
1390 */
1391static int qemuFwCfgInitrdArchiveDir(RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
1392 char pszDst[RTPATH_MAX], size_t cchDst,
1393 PRTERRINFOSTATIC pErrInfo)
1394{
1395 RT_NOREF(cchSrc);
1396
1397 char szSrcAbs[RTPATH_MAX];
1398 int rc = RTPathAbs(pszSrc, szSrcAbs, sizeof(szSrcAbs));
1399 if (RT_FAILURE(rc))
1400 return rc;
1401
1402 union
1403 {
1404 uint8_t abPadding[QEMUFWCFG_DIRENTRY_BUF_SIZE];
1405 RTDIRENTRYEX DirEntry;
1406 } uBuf;
1407
1408 return qemuFwCfgInitrdArchiveDirSub(hVfsFss, szSrcAbs, strlen(szSrcAbs), pszDst, cchDst, &uBuf.DirEntry, pErrInfo);
1409}
1410
1411
1412/**
1413 * Creates an on the fly initramfs for a given root directory.
1414 *
1415 * @returns VBox status code.
1416 * @param pThis The QEMU fw config device instance.
1417 * @param pszPath The path to work on.
1418 */
1419static int qemuFwCfgInitrdCreate(PDEVQEMUFWCFG pThis, const char *pszPath)
1420{
1421 /*
1422 * First open the output file.
1423 */
1424 RTVFSFSSTREAM hVfsFss;
1425 int rc = qemuFwCfgCreateOutputArchive(pThis, &hVfsFss);
1426 if (RT_FAILURE(rc))
1427 return rc;
1428
1429 /*
1430 * Construct/copy the source name.
1431 */
1432 char szSrc[RTPATH_MAX];
1433 rc = RTStrCopy(szSrc, sizeof(szSrc), pszPath);
1434 if (RT_SUCCESS(rc))
1435 {
1436 RTERRINFOSTATIC ErrInfo;
1437 char szDst[RTPATH_MAX]; RT_ZERO(szDst);
1438 rc = qemuFwCfgInitrdArchiveDir(hVfsFss, szSrc, strlen(szSrc),
1439 szDst, strlen(szDst), &ErrInfo);
1440 }
1441
1442 /*
1443 * Finalize the archive.
1444 */
1445 int rc2 = RTVfsFsStrmEnd(hVfsFss); AssertRC(rc2);
1446 RTVfsFsStrmRelease(hVfsFss);
1447 return rc;
1448}
1449
1450
1451/**
1452 * Checks whether creation of the initrd should be done on the fly.
1453 *
1454 * @returns VBox status code.
1455 * @param pThis The QEMU fw config device instance.
1456 */
1457static int qemuFwCfgInitrdMaybeCreate(PDEVQEMUFWCFG pThis)
1458{
1459 PPDMDEVINS pDevIns = pThis->pDevIns;
1460 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1461
1462 /* Query the path from the CFGM key. */
1463 char *pszFilePath = NULL;
1464 int rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, "InitrdImage", &pszFilePath);
1465 if (RT_SUCCESS(rc))
1466 {
1467 if (RTDirExists(pszFilePath))
1468 {
1469 rc = qemuFwCfgInitrdCreate(pThis, pszFilePath);
1470 if (RT_FAILURE(rc))
1471 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1472 N_("QemuFwCfg: failed to create the in memory initram filesystem"));
1473 }
1474 /*else: Probably a normal file. */
1475 PDMDevHlpMMHeapFree(pDevIns, pszFilePath);
1476 }
1477 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
1478 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1479 N_("Configuration error: Querying \"InitrdImage\" as a string failed"));
1480 else
1481 rc = VINF_SUCCESS;
1482
1483 return rc;
1484}
1485
1486
1487/**
1488 * Registers a file item with the given name for consumption by the firmware.
1489 *
1490 * @returns VBox status code.
1491 * @param pThis The QEMU fw config device instance.
1492 * @param pszFilename The filename to use.
1493 * @param cbData Size of the file in bytes.
1494 * @param pfnSetup Setup callback - optional.
1495 * @param pfnRead Read callback - optional.
1496 * @param pfnWrite Write callback - optional.
1497 * @param pfnCleanup Cleanup callback when the item gets de-selected - optional.
1498 */
1499static int qemuFwCfgR3FileRegister(PDEVQEMUFWCFG pThis, const char *pszFilename, uint32_t cbData,
1500 PFNQEMUFWCFGITEMSETUP pfnSetup, PFNQEMUFWCFGITEMREAD pfnRead,
1501 PFNQEMUFWCFGITEMWRITE pfnWrite, PFNQEMUFWCFGITEMCLEANUP pfnCleanup)
1502{
1503 AssertReturn(strlen(pszFilename) <= QEMU_FW_CFG_ITEM_FILE_NAME_MAX, VERR_FILENAME_TOO_LONG);
1504
1505 PQEMUFWCFGFILEENTRY pEntry = NULL;
1506 if (pThis->cCfgFiles == pThis->cCfgFilesMax)
1507 {
1508 /* Grow the array. */
1509 PQEMUFWCFGFILEENTRY paCfgFilesNew = (PQEMUFWCFGFILEENTRY)RTMemRealloc(pThis->paCfgFiles,
1510 (pThis->cCfgFilesMax + 10) * sizeof(*pThis->paCfgFiles));
1511 if (!paCfgFilesNew)
1512 return VERR_NO_MEMORY;
1513
1514 pThis->paCfgFiles = paCfgFilesNew;
1515 pThis->cCfgFilesMax += 10;
1516 }
1517
1518 pEntry = &pThis->paCfgFiles[pThis->cCfgFiles];
1519 pThis->cCfgFiles++;
1520
1521 pEntry->cbFile = cbData;
1522 strncpy(&pEntry->szFilename[0], pszFilename, sizeof(pEntry->szFilename));
1523 pEntry->Cfg.uCfgItem = QEMU_FW_CFG_ITEM_FILE_USER_FIRST + pThis->cCfgFiles - 1;
1524 pEntry->Cfg.pszItem = &pEntry->szFilename[0];
1525 pEntry->Cfg.pszCfgmKey = NULL;
1526 pEntry->Cfg.pfnSetup = pfnSetup ? pfnSetup : qemuFwCfgR3SetupFileGeneric;
1527 pEntry->Cfg.pfnRead = pfnRead;
1528 pEntry->Cfg.pfnWrite = pfnWrite;
1529 pEntry->Cfg.pfnCleanup = pfnCleanup;
1530 return VINF_SUCCESS;
1531}
1532
1533
1534/**
1535 * RAM framebuffer config write callback.
1536 *
1537 * @param pThis The QEMU fw config device instance.
1538 * @param pItem Pointer to the selected item.
1539 * @param off Where to start writing to.
1540 * @param pvBuf The data to write.
1541 * @param cbToWrite How much to write.
1542 * @param pcbWritten Where to store the amount of bytes written.
1543 */
1544static DECLCALLBACK(int) qemuFwCfgR3RamfbCfgWrite(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, const void *pvBuf,
1545 uint32_t cbToWrite, uint32_t *pcbWritten)
1546{
1547 RT_NOREF(pItem);
1548
1549 AssertReturn(!off && cbToWrite == sizeof(QEMURAMFBCONFIG), VERR_NOT_SUPPORTED);
1550 *pcbWritten = cbToWrite;
1551
1552 PCQEMURAMFBCONFIG pRamfbCfg = (PCQEMURAMFBCONFIG)pvBuf;
1553 if ( RT_BE2H_U32(pRamfbCfg->u32FourCC) != QEMU_RAMFB_CFG_FORMAT
1554 || RT_BE2H_U32(pRamfbCfg->u32Flags) != 0)
1555 return VERR_NOT_SUPPORTED;
1556
1557 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1558 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1559
1560 pThis->RamfbCfg.GCPhysRamfbBase = RT_BE2H_U64(pRamfbCfg->GCPhysRamfbBase);
1561 pThis->RamfbCfg.cbStride = RT_BE2H_U32(pRamfbCfg->cbStride);
1562 pThis->RamfbCfg.cWidth = RT_BE2H_U32(pRamfbCfg->cWidth);
1563 pThis->RamfbCfg.cHeight = RT_BE2H_U32(pRamfbCfg->cHeight);
1564 pThis->RamfbCfg.u32FourCC = RT_BE2H_U32(pRamfbCfg->u32FourCC);
1565 pThis->RamfbCfg.u32Flags = RT_BE2H_U32(pRamfbCfg->u32Flags);
1566
1567 if (pThis->pDrvL0)
1568 {
1569 int rc = pThis->pDrvL0->pfnResize(pThis->pDrvL0, QEMU_RAMFB_CFG_BPP * 8, NULL /*pvVRAM*/,
1570 pThis->RamfbCfg.cbStride,
1571 pThis->RamfbCfg.cWidth,
1572 pThis->RamfbCfg.cHeight);
1573 AssertRC(rc);
1574 }
1575
1576 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1577
1578 return VINF_SUCCESS;
1579}
1580
1581
1582/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
1583
1584/**
1585 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
1586 */
1587static DECLCALLBACK(int) qemuFwCfgR3RamfbPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
1588{
1589 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1590
1591 LogFlowFunc(("\n"));
1592
1593 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1594 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1595
1596 if ( pThis->fRenderVRam
1597 && pThis->RamfbCfg.GCPhysRamfbBase)
1598 {
1599 if ( pThis->RamfbCfg.cWidth == pThis->pDrvL0->cx
1600 && pThis->RamfbCfg.cHeight == pThis->pDrvL0->cy
1601 && pThis->RamfbCfg.cbStride == pThis->pDrvL0->cbScanline
1602 && pThis->pDrvL0->pbData)
1603 {
1604 PDMDevHlpPhysReadUser(pThis->pDevIns, pThis->RamfbCfg.GCPhysRamfbBase, pThis->pDrvL0->pbData, pThis->RamfbCfg.cbStride * pThis->RamfbCfg.cHeight);
1605 AssertPtr(pThis->pDrvL0);
1606 pThis->pDrvL0->pfnUpdateRect(pThis->pDrvL0, 0, 0, pThis->RamfbCfg.cWidth, pThis->RamfbCfg.cHeight);
1607 }
1608 else
1609 LogFlowFunc(("Framebuffer dimension mismatch ({%u, %u, %u} vs {%u, %u, %u})\n",
1610 pThis->RamfbCfg.cWidth, pThis->RamfbCfg.cHeight, pThis->RamfbCfg.cbStride,
1611 pThis->pDrvL0->cx, pThis->pDrvL0->cy, pThis->pDrvL0->cbScanline));
1612 }
1613 else
1614 LogFlowFunc(("Rendering disabled or no RAM framebuffer set up\n"));
1615
1616 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1617 return VINF_SUCCESS;
1618}
1619
1620
1621/**
1622 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
1623 */
1624static DECLCALLBACK(int) qemuFwCfgR3RamfbPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
1625{
1626 RT_NOREF(pInterface, fFailOnResize);
1627 AssertReleaseFailed();
1628 return VERR_NOT_IMPLEMENTED;
1629}
1630
1631
1632/**
1633 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
1634 */
1635static DECLCALLBACK(int) qemuFwCfgR3RamfbPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
1636{
1637 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1638
1639 /*
1640 * Update the interval, then restart or stop the timer.
1641 */
1642 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
1643
1644 if (cMilliesInterval)
1645 return PDMDevHlpTimerSetMillies(pThis->pDevIns, pThis->hRamfbRefreshTimer, cMilliesInterval);
1646 return PDMDevHlpTimerStop(pThis->pDevIns, pThis->hRamfbRefreshTimer);
1647}
1648
1649
1650/**
1651 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
1652 */
1653static DECLCALLBACK(int) qemuFwCfgR3RamfbPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
1654{
1655 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
1656
1657 RT_NOREF(pInterface, pcx, pcy);
1658 AssertReleaseFailed();
1659 return VERR_NOT_IMPLEMENTED;
1660}
1661
1662
1663/**
1664 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
1665 */
1666static DECLCALLBACK(int) qemuFwCfgR3RamfbPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
1667 uint32_t *pcx, uint32_t *pcy)
1668{
1669 RT_NOREF(pInterface, ppbData, pcbData, pcx, pcy);
1670 return VERR_NOT_SUPPORTED;
1671}
1672
1673
1674/**
1675 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
1676 */
1677static DECLCALLBACK(void) qemuFwCfgR3RamfbPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
1678{
1679 NOREF(pInterface);
1680 LogFlowFunc(("pbData=%p\n", pbData));
1681 RTMemFree(pbData);
1682}
1683
1684
1685/**
1686 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
1687 */
1688static DECLCALLBACK(int) qemuFwCfgR3RamfbPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
1689 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1690{
1691 RT_NOREF(pInterface, pvData, x, y, cx, cy);
1692 AssertReleaseFailed();
1693 return VERR_NOT_IMPLEMENTED;
1694}
1695
1696
1697/**
1698 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
1699 */
1700static DECLCALLBACK(void) qemuFwCfgR3RamfbPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y,
1701 uint32_t cx, uint32_t cy)
1702{
1703 RT_NOREF(pInterface, x, y, cx, cy);
1704 AssertReleaseFailed();
1705}
1706
1707
1708/**
1709 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
1710 */
1711static DECLCALLBACK(int)
1712qemuFwCfgR3RamfbPortCopyRect(PPDMIDISPLAYPORT pInterface,
1713 uint32_t cx, uint32_t cy,
1714 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
1715 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
1716 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
1717 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
1718{
1719 RT_NOREF(pInterface, cx, cy, pbSrc, xSrc, ySrc, cxSrc, cySrc, cbSrcLine, cSrcBitsPerPixel, pbDst, xDst, yDst, cxDst, cyDst,
1720 cbDstLine, cDstBitsPerPixel);
1721 AssertReleaseFailed();
1722 return VINF_SUCCESS;
1723}
1724
1725
1726/**
1727 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
1728 */
1729static DECLCALLBACK(void) qemuFwCfgR3RamfbPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
1730{
1731 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1732
1733 LogFlowFunc(("fRender = %d\n", fRender));
1734
1735 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1736 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1737
1738 pThis->fRenderVRam = fRender;
1739
1740 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1741}
1742
1743
1744/**
1745 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
1746 */
1747static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
1748 bool fSupportsMoveCursor)
1749{
1750 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
1751}
1752
1753
1754/**
1755 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
1756 */
1757static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
1758{
1759 RT_NOREF(pInterface, x, y, fOutOfRange);
1760}
1761
1762
1763/**
1764 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
1765 */
1766static DECLCALLBACK(void) qemuFwCfgR3RamfbTimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1767{
1768 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1769 RT_NOREF(pvUser);
1770
1771 if (pThis->pDrvL0)
1772 pThis->pDrvL0->pfnRefresh(pThis->pDrvL0);
1773
1774 if (pThis->cMilliesRefreshInterval)
1775 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
1776}
1777
1778
1779/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
1780
1781/**
1782 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1783 */
1784static DECLCALLBACK(void *) qemuFwCfgR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1785{
1786 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IBase);
1787 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1788 if (pThis->fRamfbSupported)
1789 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPortRamfb);
1790 return NULL;
1791}
1792
1793
1794/**
1795 * @interface_method_impl{PDMDEVREG,pfnReset}
1796 */
1797static DECLCALLBACK(void) qemuFwCfgReset(PPDMDEVINS pDevIns)
1798{
1799 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1800
1801 qemuFwCfgR3ItemReset(pThis);
1802 pThis->GCPhysDma = 0;
1803
1804 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1805 RTVfsFileRelease(pThis->hVfsFileInitrd);
1806 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1807
1808 qemuFwCfgInitrdMaybeCreate(pThis); /* Ignores status code. */
1809}
1810
1811
1812/**
1813 * @interface_method_impl{PDMDEVREG,pfnAttach}
1814 *
1815 * This is like plugging in the monitor after turning on the PC.
1816 */
1817static DECLCALLBACK(int) qemuFwCfgR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1818{
1819 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1820
1821 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1822 ("QEMU RAM framebuffer device does not support hotplugging\n"),
1823 VERR_INVALID_PARAMETER);
1824
1825 switch (iLUN)
1826 {
1827 /* LUN #0: Display port. */
1828 case 0:
1829 {
1830 AssertLogRelMsgReturn(pThis->fRamfbSupported,
1831 ("QemuFwCfg: Trying to attach a display without the RAM framebuffer support being enabled!\n"),
1832 VERR_NOT_SUPPORTED);
1833
1834 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBaseL0, "Display Port");
1835 if (RT_SUCCESS(rc))
1836 {
1837 pThis->pDrvL0 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBaseL0, PDMIDISPLAYCONNECTOR);
1838 if (pThis->pDrvL0)
1839 {
1840 /* pThis->pDrvL0->pbData can be NULL when there is no framebuffer. */
1841 if ( pThis->pDrvL0->pfnRefresh
1842 && pThis->pDrvL0->pfnResize
1843 && pThis->pDrvL0->pfnUpdateRect)
1844 rc = VINF_SUCCESS;
1845 else
1846 {
1847 Assert(pThis->pDrvL0->pfnRefresh);
1848 Assert(pThis->pDrvL0->pfnResize);
1849 Assert(pThis->pDrvL0->pfnUpdateRect);
1850 pThis->pDrvL0 = NULL;
1851 pThis->pDrvBaseL0 = NULL;
1852 rc = VERR_INTERNAL_ERROR;
1853 }
1854 }
1855 else
1856 {
1857 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
1858 pThis->pDrvBaseL0 = NULL;
1859 rc = VERR_PDM_MISSING_INTERFACE;
1860 }
1861 }
1862 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1863 {
1864 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1865 rc = VINF_SUCCESS;
1866 }
1867 else
1868 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
1869 return rc;
1870 }
1871
1872 default:
1873 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1874 return VERR_PDM_NO_SUCH_LUN;
1875 }
1876}
1877
1878
1879/**
1880 * @interface_method_impl{PDMDEVREG,pfnDetach}
1881 *
1882 * This is like unplugging the monitor while the PC is still running.
1883 */
1884static DECLCALLBACK(void) qemuFwCfgR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1885{
1886 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1887 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("QEMU RAM framebuffer device does not support hotplugging\n"));
1888 RT_NOREF(fFlags);
1889
1890 /*
1891 * Reset the interfaces and update the controller state.
1892 */
1893 switch (iLUN)
1894 {
1895 /* LUN #0: Display port. */
1896 case 0:
1897 AssertLogRelMsg(pThis->fRamfbSupported,
1898 ("QemuFwCfg: Trying to detach a display without the RAM framebuffer support being enabled!\n"));
1899
1900 pThis->pDrvL0 = NULL;
1901 pThis->pDrvBaseL0 = NULL;
1902 break;
1903
1904 default:
1905 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1906 break;
1907 }
1908}
1909
1910
1911/**
1912 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1913 */
1914static DECLCALLBACK(int) qemuFwCfgDestruct(PPDMDEVINS pDevIns)
1915{
1916 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1917 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1918
1919 qemuFwCfgR3ItemReset(pThis);
1920 pThis->GCPhysDma = 0;
1921
1922 if (pThis->paCfgFiles)
1923 {
1924 Assert(pThis->cCfgFiles && pThis->cCfgFilesMax);
1925 RTMemFree(pThis->paCfgFiles);
1926 pThis->paCfgFiles = NULL;
1927 pThis->cCfgFiles = 0;
1928 pThis->cCfgFilesMax = 0;
1929 }
1930 else
1931 Assert(!pThis->cCfgFiles && !pThis->cCfgFilesMax);
1932
1933 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1934 RTVfsFileRelease(pThis->hVfsFileInitrd);
1935 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1936
1937 return VINF_SUCCESS;
1938}
1939
1940
1941/**
1942 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1943 */
1944static DECLCALLBACK(int) qemuFwCfgConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1945{
1946 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1947 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1948 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1949 Assert(iInstance == 0); RT_NOREF(iInstance);
1950
1951 /*
1952 * Validate configuration.
1953 */
1954 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DmaEnabled"
1955 "|MmioBase"
1956 "|MmioSize"
1957 "|KernelImage"
1958 "|InitrdImage"
1959 "|SetupImage"
1960 "|CmdLine"
1961 "|QemuRamfbSupport",
1962 "");
1963
1964 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DmaEnabled", &pThis->fDmaEnabled, false);
1965 if (RT_FAILURE(rc))
1966 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmaEnabled\""));
1967
1968 /*
1969 * Init the data.
1970 */
1971 pThis->pDevIns = pDevIns;
1972 pThis->pCfg = pCfg;
1973 pThis->u32Version = QEMU_FW_CFG_VERSION_LEGACY | (pThis->fDmaEnabled ? QEMU_FW_CFG_VERSION_DMA : 0);
1974 pThis->GCPhysDma = 0;
1975 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1976 pThis->paCfgFiles = NULL;
1977 pThis->cCfgFiles = 0;
1978 pThis->cCfgFilesMax = 0;
1979
1980 pThis->IBase.pfnQueryInterface = qemuFwCfgR3PortQueryInterface;
1981
1982 pThis->IPortRamfb.pfnUpdateDisplay = qemuFwCfgR3RamfbPortUpdateDisplay;
1983 pThis->IPortRamfb.pfnUpdateDisplayAll = qemuFwCfgR3RamfbPortUpdateDisplayAll;
1984 pThis->IPortRamfb.pfnQueryVideoMode = qemuFwCfgR3RamfbPortQueryVideoMode;
1985 pThis->IPortRamfb.pfnSetRefreshRate = qemuFwCfgR3RamfbPortSetRefreshRate;
1986 pThis->IPortRamfb.pfnTakeScreenshot = qemuFwCfgR3RamfbPortTakeScreenshot;
1987 pThis->IPortRamfb.pfnFreeScreenshot = qemuFwCfgR3RamfbPortFreeScreenshot;
1988 pThis->IPortRamfb.pfnDisplayBlt = qemuFwCfgR3RamfbPortDisplayBlt;
1989 pThis->IPortRamfb.pfnUpdateDisplayRect = qemuFwCfgR3RamfbPortUpdateDisplayRect;
1990 pThis->IPortRamfb.pfnCopyRect = qemuFwCfgR3RamfbPortCopyRect;
1991 pThis->IPortRamfb.pfnSetRenderVRAM = qemuFwCfgR3RamfbPortSetRenderVRAM;
1992 pThis->IPortRamfb.pfnSetViewport = NULL;
1993 pThis->IPortRamfb.pfnReportMonitorPositions = NULL;
1994 pThis->IPortRamfb.pfnSendModeHint = NULL;
1995 pThis->IPortRamfb.pfnReportHostCursorCapabilities = qemuFwCfgR3RamfbPortReportHostCursorCapabilities;
1996 pThis->IPortRamfb.pfnReportHostCursorPosition = qemuFwCfgR3RamfbPortReportHostCursorPosition;
1997
1998 RTGCPHYS GCPhysMmioBase = 0;
1999 rc = pHlp->pfnCFGMQueryU64(pCfg, "MmioBase", &GCPhysMmioBase);
2000 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2001 return PDMDEV_SET_ERROR(pDevIns, rc,
2002 N_("Configuration error: Failed to get the \"MmioBase\" value"));
2003 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2004 {
2005 /*
2006 * Register standard I/O Ports
2007 */
2008 IOMIOPORTHANDLE hIoPorts;
2009 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, QEMU_FW_CFG_IO_PORT_START, QEMU_FW_CFG_IO_PORT_SIZE, 0 /*fFlags*/,
2010 qemuFwCfgIoPortWrite, qemuFwCfgIoPortRead,
2011 "QEMU firmware configuration", NULL /*paExtDescs*/, &hIoPorts);
2012 AssertRCReturn(rc, rc);
2013 }
2014 else
2015 {
2016 uint32_t cbMmio = 0;
2017 rc = pHlp->pfnCFGMQueryU32(pCfg, "MmioSize", &cbMmio);
2018 if (RT_FAILURE(rc))
2019 return PDMDEV_SET_ERROR(pDevIns, rc,
2020 N_("Configuration error: Failed to get the \"MmioSize\" value"));
2021
2022 /*
2023 * Register and map the MMIO region.
2024 */
2025 IOMMMIOHANDLE hMmio;
2026 rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysMmioBase, cbMmio, qemuFwCfgMmioWrite, qemuFwCfgMmioRead,
2027 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, "QemuFwCfg", &hMmio);
2028 AssertRCReturn(rc, rc);
2029 }
2030
2031 qemuFwCfgR3ItemReset(pThis);
2032
2033 /* Setup the RAM based framebuffer support if configured. */
2034 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "QemuRamfbSupport", &pThis->fRamfbSupported, false);
2035 if (RT_FAILURE(rc))
2036 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"QemuRamfbSupport\""));
2037
2038 if (pThis->fRamfbSupported)
2039 {
2040 LogRel(("QemuFwCfg: RAM based framebuffer support enabled\n"));
2041 if (!pThis->fDmaEnabled)
2042 return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER, N_("Configuration error: Enabling \"QemuRamfbSupport\" requires \"DmaEnabled\""));
2043
2044 /* Critical section for synchronizing access. */
2045 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectRamfb, RT_SRC_POS, "Ramfb#%u", iInstance);
2046 AssertRCReturn(rc, rc);
2047
2048 /*
2049 * Create the refresh timer.
2050 */
2051 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, qemuFwCfgR3RamfbTimerRefresh, NULL,
2052 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Ramfb Refresh", &pThis->hRamfbRefreshTimer);
2053 AssertRCReturn(rc, rc);
2054
2055 /* Register a config file item and attach the driver below us. */
2056 rc = qemuFwCfgR3Attach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
2057 AssertRCReturn(rc, rc);
2058
2059 rc = qemuFwCfgR3FileRegister(pThis, "etc/ramfb", sizeof(pThis->RamfbCfg),
2060 NULL /* pfnSetup */, NULL /*pfnRead*/,
2061 qemuFwCfgR3RamfbCfgWrite, NULL /*pfnCleanup*/);
2062 AssertRCReturn(rc, rc);
2063 }
2064
2065 rc = qemuFwCfgInitrdMaybeCreate(pThis);
2066 if (RT_FAILURE(rc))
2067 return rc; /* Error set. */
2068
2069 return VINF_SUCCESS;
2070}
2071
2072
2073/**
2074 * The device registration structure.
2075 */
2076const PDMDEVREG g_DeviceQemuFwCfg =
2077{
2078 /* .u32Version = */ PDM_DEVREG_VERSION,
2079 /* .uReserved0 = */ 0,
2080 /* .szName = */ "qemu-fw-cfg",
2081 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2082 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
2083 /* .cMaxInstances = */ 1,
2084 /* .uSharedVersion = */ 42,
2085 /* .cbInstanceShared = */ sizeof(DEVQEMUFWCFG),
2086 /* .cbInstanceCC = */ 0,
2087 /* .cbInstanceRC = */ 0,
2088 /* .cMaxPciDevices = */ 0,
2089 /* .cMaxMsixVectors = */ 0,
2090 /* .pszDescription = */ "QEMU Firmware Config compatible device",
2091#if defined(IN_RING3)
2092 /* .pszRCMod = */ "",
2093 /* .pszR0Mod = */ "",
2094 /* .pfnConstruct = */ qemuFwCfgConstruct,
2095 /* .pfnDestruct = */ qemuFwCfgDestruct,
2096 /* .pfnRelocate = */ NULL,
2097 /* .pfnMemSetup = */ NULL,
2098 /* .pfnPowerOn = */ NULL,
2099 /* .pfnReset = */ qemuFwCfgReset,
2100 /* .pfnSuspend = */ NULL,
2101 /* .pfnResume = */ NULL,
2102 /* .pfnAttach = */ qemuFwCfgR3Attach,
2103 /* .pfnDetach = */ qemuFwCfgR3Detach,
2104 /* .pfnQueryInterface = */ NULL,
2105 /* .pfnInitComplete = */ NULL,
2106 /* .pfnPowerOff = */ NULL,
2107 /* .pfnSoftReset = */ NULL,
2108 /* .pfnReserved0 = */ NULL,
2109 /* .pfnReserved1 = */ NULL,
2110 /* .pfnReserved2 = */ NULL,
2111 /* .pfnReserved3 = */ NULL,
2112 /* .pfnReserved4 = */ NULL,
2113 /* .pfnReserved5 = */ NULL,
2114 /* .pfnReserved6 = */ NULL,
2115 /* .pfnReserved7 = */ NULL,
2116#elif defined(IN_RING0)
2117 /* .pfnEarlyConstruct = */ NULL,
2118 /* .pfnConstruct = */ NULL,
2119 /* .pfnDestruct = */ NULL,
2120 /* .pfnFinalDestruct = */ NULL,
2121 /* .pfnRequest = */ NULL,
2122 /* .pfnReserved0 = */ NULL,
2123 /* .pfnReserved1 = */ NULL,
2124 /* .pfnReserved2 = */ NULL,
2125 /* .pfnReserved3 = */ NULL,
2126 /* .pfnReserved4 = */ NULL,
2127 /* .pfnReserved5 = */ NULL,
2128 /* .pfnReserved6 = */ NULL,
2129 /* .pfnReserved7 = */ NULL,
2130#elif defined(IN_RC)
2131 /* .pfnConstruct = */ NULL,
2132 /* .pfnReserved0 = */ NULL,
2133 /* .pfnReserved1 = */ NULL,
2134 /* .pfnReserved2 = */ NULL,
2135 /* .pfnReserved3 = */ NULL,
2136 /* .pfnReserved4 = */ NULL,
2137 /* .pfnReserved5 = */ NULL,
2138 /* .pfnReserved6 = */ NULL,
2139 /* .pfnReserved7 = */ NULL,
2140#else
2141# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2142#endif
2143 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2144};
2145
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette