VirtualBox

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

Last change on this file since 99545 was 99545, checked in by vboxsync, 20 months ago

Devices/DevQemuFwCfg: Fixes, bugref:10431

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 78.5 KB
Line 
1/* $Id: DevQemuFwCfg.cpp 99545 2023-04-27 12:31:56Z 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.virtualbox.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 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
500 {
501 pThis->u.u32 = 0;
502 *pcbItem = sizeof(uint32_t);
503 rc = VINF_SUCCESS;
504 }
505 else
506 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
507 }
508
509 if ( RT_SUCCESS(rc)
510 && hVfsFile != NIL_RTVFSFILE)
511 {
512 uint64_t cbFile = 0;
513 rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
514 if (RT_SUCCESS(rc))
515 {
516 if (cbFile < _4G)
517 {
518 pThis->u.u32 = (uint32_t)cbFile;
519 *pcbItem = sizeof(uint32_t);
520 }
521 else
522 {
523 rc = VERR_BUFFER_OVERFLOW;
524 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
525 }
526 }
527 else
528 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
529 RTVfsFileRelease(hVfsFile);
530 }
531
532 return rc;
533}
534
535
536/**
537 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a string type configuration item.}
538 */
539static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStrSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
540{
541 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
542
543 /* Query the string from the CFGM key. */
544 char sz[_4K];
545 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
546 if (RT_SUCCESS(rc))
547 {
548 pThis->u.u32 = (uint32_t)strlen(&sz[0]) + 1;
549 *pcbItem = sizeof(uint32_t);
550 }
551 else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
552 {
553 pThis->u.u32 = 0;
554 *pcbItem = sizeof(uint32_t);
555 rc = VINF_SUCCESS;
556 }
557 else
558 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
559
560 return rc;
561}
562
563
564/**
565 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a string type configuration item gathered from CFGM.}
566 */
567static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
568{
569 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
570
571 /* Query the string from the CFGM key. */
572 char sz[_4K];
573 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
574 if (RT_SUCCESS(rc))
575 *pcbItem = (uint32_t)strlen(&sz[0]) + 1;
576 else
577 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
578
579 return rc;
580}
581
582
583/**
584 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a VFS file type configuration item.}
585 */
586static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
587{
588 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
589
590 int rc = VINF_SUCCESS;
591 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_DATA
592 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
593 {
594 RTVfsFileRetain(pThis->hVfsFileInitrd);
595 pThis->u.hVfsFile = pThis->hVfsFileInitrd;
596 }
597 else
598 {
599 /* Query the path from the CFGM key. */
600 char *pszFilePath = NULL;
601 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
602 if (RT_SUCCESS(rc))
603 {
604 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &pThis->u.hVfsFile);
605 if (RT_FAILURE(rc))
606 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
607 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
608 }
609 else
610 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
611 }
612
613 if (RT_SUCCESS(rc))
614 {
615 uint64_t cbFile = 0;
616 rc = RTVfsFileQuerySize(pThis->u.hVfsFile, &cbFile);
617 if (RT_SUCCESS(rc))
618 {
619 if (cbFile < _4G)
620 *pcbItem = (uint32_t)cbFile;
621 else
622 {
623 rc = VERR_BUFFER_OVERFLOW;
624 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
625 }
626 }
627 else
628 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
629 }
630
631 return rc;
632}
633
634
635/**
636 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a configuration item having its data stored in the scratch buffer.}
637 */
638static DECLCALLBACK(int) qemuFwCfgR3ReadSimple(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
639 uint32_t cbToRead, uint32_t *pcbRead)
640{
641 RT_NOREF(pThis, pItem);
642 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
643 *pcbRead = cbToRead;
644 return VINF_SUCCESS;
645}
646
647
648/**
649 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a VFS file type configuration item.}
650 */
651static DECLCALLBACK(int) qemuFwCfgR3ReadVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
652 uint32_t cbToRead, uint32_t *pcbRead)
653{
654 RT_NOREF(pItem);
655 size_t cbRead = 0;
656 int rc = RTVfsFileReadAt(pThis->u.hVfsFile, off, pvBuf, cbToRead, &cbRead);
657 if (RT_SUCCESS(rc))
658 *pcbRead = (uint32_t)cbRead;
659
660 return rc;
661}
662
663
664/**
665 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads a string item gathered from CFGM.}
666 */
667static DECLCALLBACK(int) qemuFwCfgR3ReadStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
668 uint32_t cbToRead, uint32_t *pcbRead)
669{
670 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
671
672 /* Query the string from the CFGM key. */
673 char sz[_4K];
674 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
675 if (RT_SUCCESS(rc))
676 {
677 uint32_t cch = (uint32_t)strlen(sz) + 1;
678 if (off < cch)
679 {
680 uint32_t cbRead = RT_MIN(cbToRead, off - cch);
681 memcpy(pvBuf, &sz[off], cbRead);
682 *pcbRead = cbRead;
683 }
684 else
685 rc = VERR_BUFFER_OVERFLOW;
686 }
687 else
688 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
689
690 return rc;
691}
692
693
694/**
695 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from the file directory.}
696 */
697static DECLCALLBACK(int) qemuFwCfgR3ReadFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
698 uint32_t cbToRead, uint32_t *pcbRead)
699{
700 RT_NOREF(pItem);
701
702 /* The first 4 bytes are the number of entries following. */
703 if (off < sizeof(uint32_t))
704 {
705 cbToRead = RT_MIN(cbToRead, sizeof(uint32_t) - off);
706 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
707 *pcbRead = cbToRead;
708 }
709 else
710 {
711 off -= sizeof(uint32_t);
712
713 /* The entries are static, so we can deduce the entry number from the offset. */
714 uint32_t idxEntry = off / sizeof(*pThis->paCfgFiles);
715 AssertReturn(idxEntry < pThis->cCfgFiles, VERR_INTERNAL_ERROR);
716
717 off %= sizeof(*pThis->paCfgFiles);
718 cbToRead = RT_MIN(cbToRead, sizeof(pThis->u.CfgFile));
719
720 /* Setup the config file item. */
721 PCQEMUFWCFGFILEENTRY pEntry = &pThis->paCfgFiles[idxEntry];
722 pThis->u.CfgFile.cbFile = RT_H2BE_U32(pEntry->cbFile);
723 pThis->u.CfgFile.uCfgItem = RT_H2BE_U16(pEntry->Cfg.uCfgItem);
724 pThis->u.CfgFile.u16Rsvd = 0;
725 strncpy(&pThis->u.CfgFile.szFilename[0], pEntry->Cfg.pszItem, sizeof(pThis->u.CfgFile.szFilename));
726 pThis->u.CfgFile.szFilename[QEMU_FW_CFG_ITEM_FILE_NAME_MAX] = '\0';
727
728 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
729 *pcbRead = cbToRead;
730 }
731 return VINF_SUCCESS;
732}
733
734
735/**
736 * @interface_method_impl{QEMUFWCFGITEM,pfnCleanup, Cleans up a VFS file type configuration item.}
737 */
738static DECLCALLBACK(void) qemuFwCfgR3CleanupVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem)
739{
740 RT_NOREF(pItem);
741 RTVfsFileRelease(pThis->u.hVfsFile);
742 pThis->u.hVfsFile = NIL_RTVFSFILE;
743}
744
745
746/**
747 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Generic setup routine for file entries which don't have a dedicated setup routine.}
748 */
749static DECLCALLBACK(int) qemuFwCfgR3SetupFileGeneric(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
750{
751 RT_NOREF(pItem);
752 *pcbItem = pThis->paCfgFiles[pItem->uCfgItem - QEMU_FW_CFG_ITEM_FILE_USER_FIRST].cbFile;
753 return VINF_SUCCESS;
754}
755
756
757/**
758 * Supported config items.
759 */
760static const QEMUFWCFGITEM g_aQemuFwCfgItems[] =
761{
762 /** u16Selector pszItem pszCfgmKey pfnSetup pfnRead pfnWrite pfnCleanup */
763 { QEMU_FW_CFG_ITEM_SIGNATURE, "Signature", NULL, qemuFwCfgR3SetupSignature, qemuFwCfgR3ReadSimple, NULL, NULL },
764 { QEMU_FW_CFG_ITEM_VERSION, "Version", NULL, qemuFwCfgR3SetupVersion, qemuFwCfgR3ReadSimple, NULL, NULL },
765 { QEMU_FW_CFG_ITEM_KERNEL_SIZE, "KrnlSz", "KernelImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
766 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "KrnlDat", "KernelImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
767 { QEMU_FW_CFG_ITEM_INITRD_SIZE, "InitrdSz", "InitrdImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
768 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "InitrdDat", "InitrdImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
769 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE, "SetupSz", "SetupImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
770 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA, "SetupDat", "SetupImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
771 { QEMU_FW_CFG_ITEM_CMD_LINE_SIZE, "CmdLineSz", "CmdLine", qemuFwCfgR3SetupCfgmStrSz, qemuFwCfgR3ReadSimple, NULL, NULL },
772 { QEMU_FW_CFG_ITEM_CMD_LINE_DATA, "CmdLineDat", "CmdLine", qemuFwCfgR3SetupCfgmStr, qemuFwCfgR3ReadStr, NULL, NULL },
773 { QEMU_FW_CFG_ITEM_FILE_DIR, "FileDir", NULL, qemuFwCfgR3SetupFileDir, qemuFwCfgR3ReadFileDir, NULL, NULL }
774};
775
776
777/**
778 * Resets the currently selected item.
779 *
780 * @returns nothing.
781 * @param pThis The QEMU fw config device instance.
782 */
783static void qemuFwCfgR3ItemReset(PDEVQEMUFWCFG pThis)
784{
785 if ( pThis->pCfgItem
786 && pThis->pCfgItem->pfnCleanup)
787 pThis->pCfgItem->pfnCleanup(pThis, pThis->pCfgItem);
788
789 pThis->pCfgItem = NULL;
790 pThis->offCfgItemNext = 0;
791 pThis->cbCfgItemLeft = 0;
792}
793
794
795/**
796 * Selects the given config item.
797 *
798 * @returns VBox status code.
799 * @param pThis The QEMU fw config device instance.
800 * @param uCfgItem The configuration item to select.
801 */
802static int qemuFwCfgItemSelect(PDEVQEMUFWCFG pThis, uint16_t uCfgItem)
803{
804 LogFlowFunc(("uCfgItem=%#x\n", uCfgItem));
805
806 qemuFwCfgR3ItemReset(pThis);
807
808 PCQEMUFWCFGITEM pCfgItem = NULL;;
809
810 /* Check whether this is a file item. */
811 if (uCfgItem >= QEMU_FW_CFG_ITEM_FILE_USER_FIRST)
812 {
813 uCfgItem -= QEMU_FW_CFG_ITEM_FILE_USER_FIRST;
814 if (uCfgItem < pThis->cCfgFiles)
815 pCfgItem = &pThis->paCfgFiles[uCfgItem].Cfg;
816 }
817 else
818 {
819 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQemuFwCfgItems); i++)
820 {
821 pCfgItem = &g_aQemuFwCfgItems[i];
822 if (pCfgItem->uCfgItem == uCfgItem)
823 break;
824 }
825 }
826
827 if (pCfgItem)
828 {
829 uint32_t cbItem = 0;
830 AssertPtrReturn(pCfgItem->pfnSetup, VERR_INVALID_STATE);
831
832 int rc = pCfgItem->pfnSetup(pThis, pCfgItem, &cbItem);
833 if (RT_SUCCESS(rc))
834 {
835 pThis->pCfgItem = pCfgItem;
836 pThis->cbCfgItemLeft = cbItem;
837 return VINF_SUCCESS;
838 }
839
840 return rc;
841 }
842
843 return VERR_NOT_FOUND;
844}
845
846
847/**
848 * Processes a DMA transfer.
849 *
850 * @returns nothing.
851 * @param pThis The QEMU fw config device instance.
852 * @param GCPhysDma The guest physical address of the DMA descriptor.
853 */
854static void qemuFwCfgDmaXfer(PDEVQEMUFWCFG pThis, RTGCPHYS GCPhysDma)
855{
856 QEMUFWDMADESC DmaDesc; RT_ZERO(DmaDesc);
857
858 LogFlowFunc(("pThis=%p GCPhysDma=%RGp\n", pThis, GCPhysDma));
859
860 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysDma, &DmaDesc, sizeof(DmaDesc));
861
862 /* Convert from big endianess to host endianess. */
863 DmaDesc.u32Ctrl = RT_BE2H_U32(DmaDesc.u32Ctrl);
864 DmaDesc.u32Length = RT_BE2H_U32(DmaDesc.u32Length);
865 DmaDesc.u64GCPhysBuf = RT_BE2H_U64(DmaDesc.u64GCPhysBuf);
866
867 LogFlowFunc(("u32Ctrl=%#x u32Length=%u u64GCPhysBuf=%llx\n",
868 DmaDesc.u32Ctrl, DmaDesc.u32Length, DmaDesc.u64GCPhysBuf));
869
870 /* If the select bit is set a select is performed. */
871 int rc = VINF_SUCCESS;
872 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_SELECT)
873 rc = qemuFwCfgItemSelect(pThis, QEMU_FW_CFG_DMA_GET_CFG_ITEM(DmaDesc.u32Ctrl));
874 else if (!pThis->pCfgItem)
875 rc = VERR_NOT_FOUND;
876
877 if (RT_SUCCESS(rc))
878 {
879 if ( ( DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE
880 && !pThis->pCfgItem->pfnWrite)
881 || ( DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ
882 && !pThis->pCfgItem->pfnRead))
883 rc = VERR_NOT_SUPPORTED;
884 else if ( !pThis->pCfgItem
885 || !pThis->cbCfgItemLeft)
886 {
887 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
888 {
889 /* Item is not supported, just zero out the indicated area. */
890 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
891 uint32_t cbLeft = DmaDesc.u32Length;
892
893 while ( RT_SUCCESS(rc)
894 && cbLeft)
895 {
896 uint32_t cbZero = RT_MIN(_64K, cbLeft);
897
898 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &g_abRTZero64K[0], cbZero);
899
900 cbLeft -= cbZero;
901 GCPhysCur += cbZero;
902 }
903 }
904 /* else: Assume Skip or Write and ignore. */
905 }
906 else
907 {
908 /* Normal path. */
909 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
910 uint32_t cbLeft = RT_MIN(DmaDesc.u32Length, pThis->cbCfgItemLeft);
911
912 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE)
913 {
914 while ( RT_SUCCESS(rc)
915 && cbLeft)
916 {
917 uint8_t abTmp[_1K];
918 uint32_t cbThisWrite = RT_MIN(sizeof(abTmp), cbLeft);
919 uint32_t cbWritten;
920
921 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbThisWrite);
922 rc = pThis->pCfgItem->pfnWrite(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
923 cbThisWrite, &cbWritten);
924 if (RT_SUCCESS(rc))
925 {
926 cbLeft -= cbWritten;
927 GCPhysCur += cbWritten;
928
929 pThis->offCfgItemNext += cbWritten;
930 pThis->cbCfgItemLeft -= cbWritten;
931 }
932 }
933 }
934 else
935 {
936 while ( RT_SUCCESS(rc)
937 && cbLeft)
938 {
939 uint8_t abTmp[_1K];
940 uint32_t cbThisRead = RT_MIN(sizeof(abTmp), cbLeft);
941 uint32_t cbRead;
942
943 rc = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
944 cbThisRead, &cbRead);
945 if (RT_SUCCESS(rc))
946 {
947 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
948 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbRead);
949 /* else: Assume Skip */
950
951 cbLeft -= cbRead;
952 GCPhysCur += cbRead;
953
954 pThis->offCfgItemNext += cbRead;
955 pThis->cbCfgItemLeft -= cbRead;
956 }
957 }
958 }
959 }
960 }
961
962 LogFlowFunc(("pThis=%p GCPhysDma=%RGp -> %Rrc\n", pThis, GCPhysDma, rc));
963
964 /* Write back the control field. */
965 uint32_t u32Resp = RT_SUCCESS(rc) ? 0 : RT_H2BE_U32(QEMU_FW_CFG_DMA_ERROR);
966 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysDma, &u32Resp, sizeof(u32Resp));
967}
968
969
970/**
971 * @callback_method_impl{FNIOMIOPORTNEWOUT, QEMU firmware configuration write.}
972 */
973static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
974{
975 int rc = VINF_SUCCESS;
976 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
977 NOREF(pvUser);
978
979 LogFlowFunc(("offPort=%RTiop u32=%#x cb=%u\n", offPort, u32, cb));
980
981 switch (offPort)
982 {
983 case QEMU_FW_CFG_OFF_SELECTOR:
984 {
985 if (cb == 2)
986 qemuFwCfgItemSelect(pThis, (uint16_t)u32);
987 break;
988 }
989 case QEMU_FW_CFG_OFF_DATA: /* Readonly, ignore */
990 break;
991 case QEMU_FW_CFG_OFF_DMA_HIGH:
992 {
993 if (cb == 4)
994 pThis->GCPhysDma = ((RTGCPHYS)RT_BE2H_U32(u32)) << 32;
995 break;
996 }
997 case QEMU_FW_CFG_OFF_DMA_LOW:
998 {
999 if (cb == 4)
1000 {
1001 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U32(u32));
1002 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
1003 pThis->GCPhysDma = 0;
1004 }
1005 break;
1006 }
1007 default:
1008 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", offPort, cb, u32);
1009 break;
1010 }
1011
1012 LogFlowFunc((" -> rc=%Rrc\n", rc));
1013 return rc;
1014}
1015
1016
1017/**
1018 * @callback_method_impl{FNIOMIOPORTNEWIN, QEMU firmware configuration read.}
1019 */
1020static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1021{
1022 int rc = VINF_SUCCESS;
1023 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1024 NOREF(pvUser);
1025
1026 *pu32 = 0;
1027
1028 LogFlowFunc(("offPort=%RTiop cb=%u\n", offPort, cb));
1029
1030 switch (offPort)
1031 {
1032 /* Selector (Writeonly, ignore). */
1033 case QEMU_FW_CFG_OFF_SELECTOR:
1034 break;
1035 case QEMU_FW_CFG_OFF_DATA:
1036 {
1037 if (cb == 1)
1038 {
1039 if ( pThis->cbCfgItemLeft
1040 && pThis->pCfgItem)
1041 {
1042 uint8_t bRead = 0;
1043 uint32_t cbRead = 0;
1044 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &bRead,
1045 sizeof(bRead), &cbRead);
1046 if ( RT_SUCCESS(rc2)
1047 && cbRead == sizeof(bRead))
1048 {
1049 pThis->offCfgItemNext += cbRead;
1050 pThis->cbCfgItemLeft -= cbRead;
1051 *pu32 = bRead;
1052 }
1053 }
1054 }
1055 else
1056 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
1057 break;
1058 }
1059
1060 default:
1061 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
1062 break;
1063 }
1064
1065 LogFlowFunc(("offPort=%RTiop cb=%u -> rc=%Rrc u32=%#x\n", offPort, cb, rc, *pu32));
1066
1067 return rc;
1068}
1069
1070
1071/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
1072
1073
1074/**
1075 * @callback_method_impl{FNIOMMMIONEWREAD}
1076 */
1077static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1078{
1079 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1080 RT_NOREF(pvUser);
1081
1082 LogFlowFunc(("%RGp cb=%u\n", off, cb));
1083
1084 AssertReturn(cb <= sizeof(uint64_t), VERR_INVALID_PARAMETER);
1085
1086 VBOXSTRICTRC rc = VINF_SUCCESS;
1087 switch (off)
1088 {
1089 case QEU_FW_CFG_MMIO_OFF_DATA:
1090 {
1091 if ( pThis->cbCfgItemLeft
1092 && pThis->pCfgItem)
1093 {
1094 uint32_t cbRead = 0;
1095 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, pv,
1096 cb, &cbRead);
1097 if (RT_SUCCESS(rc2))
1098 {
1099 pThis->offCfgItemNext += cbRead;
1100 pThis->cbCfgItemLeft -= cbRead;
1101 }
1102 }
1103 else
1104 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1105 break;
1106 }
1107 case QEU_FW_CFG_MMIO_OFF_DMA:
1108 if ( cb == sizeof(uint64_t)
1109 && pThis->fDmaEnabled)
1110 *(uint64_t *)pv = RT_H2BE_U64(QEMU_FW_CFG_DMA_ADDR_SIGNATURE);
1111 else
1112 rc = VINF_IOM_MMIO_UNUSED_00;
1113 break;
1114 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
1115 /* Writeonly, ignore. */
1116 rc = VINF_IOM_MMIO_UNUSED_00;
1117 break;
1118 default:
1119 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1120 break;
1121 }
1122
1123 return rc;
1124}
1125
1126
1127/**
1128 * @callback_method_impl{FNIOMMMIONEWWRITE}
1129 */
1130static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1131{
1132 int rc = VINF_SUCCESS;
1133 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1134 RT_NOREF(pvUser);
1135
1136 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
1137
1138 switch (off)
1139 {
1140 case QEU_FW_CFG_MMIO_OFF_DATA: /* Readonly, ignore */
1141 break;
1142 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
1143 {
1144 if (cb == sizeof(uint16_t))
1145 qemuFwCfgItemSelect(pThis, RT_BE2H_U16(*(uint16_t *)pv));
1146 else
1147 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1148 break;
1149 }
1150 case QEU_FW_CFG_MMIO_OFF_DMA:
1151 {
1152 if (cb == sizeof(uint64_t))
1153 {
1154 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U64(*(uint64_t *)pv));
1155 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
1156 pThis->GCPhysDma = 0;
1157 }
1158 else
1159 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1160 break;
1161 }
1162 default:
1163 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1164 break;
1165 }
1166
1167 LogFlowFunc((" -> rc=%Rrc\n", rc));
1168 return rc;
1169}
1170
1171
1172/**
1173 * Sets up the initrd memory file and CPIO filesystem stream for writing.
1174 *
1175 * @returns VBox status code.
1176 * @param pThis The QEMU fw config device instance.
1177 * @param phVfsFss Where to return the filesystem stream handle.
1178 */
1179static int qemuFwCfgCreateOutputArchive(PDEVQEMUFWCFG pThis, PRTVFSFSSTREAM phVfsFss)
1180{
1181 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &pThis->hVfsFileInitrd);
1182 if (RT_SUCCESS(rc))
1183 {
1184 RTVFSFSSTREAM hVfsFss;
1185 RTVFSIOSTREAM hVfsIosOut = RTVfsFileToIoStream(pThis->hVfsFileInitrd);
1186 rc = RTZipTarFsStreamToIoStream(hVfsIosOut, RTZIPTARFORMAT_CPIO_ASCII_NEW, 0 /*fFlags*/, &hVfsFss);
1187 if (RT_SUCCESS(rc))
1188 {
1189 rc = RTZipTarFsStreamSetOwner(hVfsFss, 0, "root");
1190 if (RT_SUCCESS(rc))
1191 rc = RTZipTarFsStreamSetGroup(hVfsFss, 0, "root");
1192
1193 if (RT_SUCCESS(rc))
1194 *phVfsFss = hVfsFss;
1195 else
1196 {
1197 RTVfsFsStrmRelease(hVfsFss);
1198 *phVfsFss = NIL_RTVFSFSSTREAM;
1199 }
1200 }
1201 RTVfsIoStrmRelease(hVfsIosOut);
1202 }
1203
1204 return rc;
1205}
1206
1207
1208/**
1209 * Archives a file.
1210 *
1211 * @returns VBox status code.
1212 * @param hVfsFss The TAR filesystem stream handle.
1213 * @param pszSrc The file path or VFS spec.
1214 * @param pszDst The name to archive the file under.
1215 * @param pErrInfo Error info buffer (saves stack space).
1216 */
1217static int qemuFwCfgInitrdArchiveFile(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
1218 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
1219{
1220 /* Open the file. */
1221 uint32_t offError;
1222 RTVFSIOSTREAM hVfsIosSrc;
1223 int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1224 &hVfsIosSrc, &offError, RTErrInfoInitStatic(pErrInfo));
1225 if (RT_FAILURE(rc))
1226 return rc;
1227
1228 /* I/O stream to base object. */
1229 RTVFSOBJ hVfsObjSrc = RTVfsObjFromIoStream(hVfsIosSrc);
1230 if (hVfsObjSrc != NIL_RTVFSOBJ)
1231 {
1232 /*
1233 * Add it to the stream.
1234 */
1235 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1236 RTVfsIoStrmRelease(hVfsIosSrc);
1237 RTVfsObjRelease(hVfsObjSrc);
1238 return rc;
1239 }
1240 RTVfsIoStrmRelease(hVfsIosSrc);
1241 return VERR_INVALID_POINTER;
1242}
1243
1244
1245/**
1246 * Archives a symlink.
1247 *
1248 * @returns VBox status code.
1249 * @param hVfsFss The TAR filesystem stream handle.
1250 * @param pszSrc The file path or VFS spec.
1251 * @param pszDst The name to archive the file under.
1252 * @param pErrInfo Error info buffer (saves stack space).
1253 */
1254static int qemuFwCfgInitrdArchiveSymlink(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
1255 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
1256{
1257 /* Open the file. */
1258 uint32_t offError;
1259 RTVFSOBJ hVfsObjSrc;
1260 int rc = RTVfsChainOpenObj(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1261 RTVFSOBJ_F_OPEN_SYMLINK | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
1262 &hVfsObjSrc, &offError, RTErrInfoInitStatic(pErrInfo));
1263 if (RT_FAILURE(rc))
1264 return rc;
1265
1266 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1267 RTVfsObjRelease(hVfsObjSrc);
1268 return rc;
1269}
1270
1271
1272/**
1273 * Sub-directory helper for creating archives.
1274 *
1275 * @returns VBox status code.
1276 * @param hVfsFss The TAR filesystem stream handle.
1277 * @param pszSrc The directory path or VFS spec. We append to the
1278 * buffer as we decend.
1279 * @param cchSrc The length of the input.
1280 * @param pszDst The name to archive it the under. We append to the
1281 * buffer as we decend.
1282 * @param cchDst The length of the input.
1283 * @param pDirEntry Directory entry to use for the directory to handle.
1284 * @param pErrInfo Error info buffer (saves stack space).
1285 */
1286static int qemuFwCfgInitrdArchiveDirSub(RTVFSFSSTREAM hVfsFss,
1287 char *pszSrc, size_t cchSrc,
1288 char pszDst[RTPATH_MAX], size_t cchDst, PRTDIRENTRYEX pDirEntry,
1289 PRTERRINFOSTATIC pErrInfo)
1290{
1291 uint32_t offError;
1292 RTVFSDIR hVfsIoDir;
1293 int rc = RTVfsChainOpenDir(pszSrc, 0 /*fFlags*/,
1294 &hVfsIoDir, &offError, RTErrInfoInitStatic(pErrInfo));
1295 if (RT_FAILURE(rc))
1296 return rc;
1297
1298 /* Make sure we've got some room in the path, to save us extra work further down. */
1299 if (cchSrc + 3 >= RTPATH_MAX)
1300 return VERR_FILENAME_TOO_LONG;
1301
1302 /* Ensure we've got a trailing slash (there is space for it see above). */
1303 if (!RTPATH_IS_SEP(pszSrc[cchSrc - 1]))
1304 {
1305 pszSrc[cchSrc++] = RTPATH_SLASH;
1306 pszSrc[cchSrc] = '\0';
1307 }
1308
1309 /* Ditto for destination. */
1310 if (cchDst + 3 >= RTPATH_MAX)
1311 return VERR_FILENAME_TOO_LONG;
1312
1313 /* Don't try adding the root directory. */
1314 if (*pszDst != '\0')
1315 {
1316 RTVFSOBJ hVfsObjSrc = RTVfsObjFromDir(hVfsIoDir);
1317 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1318 RTVfsObjRelease(hVfsObjSrc);
1319 if (RT_FAILURE(rc))
1320 return rc;
1321
1322 if (!RTPATH_IS_SEP(pszDst[cchDst - 1]))
1323 {
1324 pszDst[cchDst++] = RTPATH_SLASH;
1325 pszDst[cchDst] = '\0';
1326 }
1327 }
1328
1329 /*
1330 * Process the files and subdirs.
1331 */
1332 for (;;)
1333 {
1334 size_t cbDirEntry = QEMUFWCFG_DIRENTRY_BUF_SIZE;
1335 rc = RTVfsDirReadEx(hVfsIoDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1336 if (RT_FAILURE(rc))
1337 break;
1338
1339 /* Check length. */
1340 if (pDirEntry->cbName + cchSrc + 3 >= RTPATH_MAX)
1341 {
1342 rc = VERR_BUFFER_OVERFLOW;
1343 break;
1344 }
1345
1346 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1347 {
1348 case RTFS_TYPE_DIRECTORY:
1349 {
1350 if (RTDirEntryExIsStdDotLink(pDirEntry))
1351 continue;
1352
1353 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1354 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1355 rc = qemuFwCfgInitrdArchiveDirSub(hVfsFss, pszSrc, cchSrc + pDirEntry->cbName,
1356 pszDst, cchDst + pDirEntry->cbName, pDirEntry, pErrInfo);
1357 break;
1358 }
1359
1360 case RTFS_TYPE_FILE:
1361 {
1362 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1363 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1364 rc = qemuFwCfgInitrdArchiveFile(hVfsFss, pszSrc, pszDst, pErrInfo);
1365 break;
1366 }
1367
1368 case RTFS_TYPE_SYMLINK:
1369 {
1370 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1371 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1372 rc = qemuFwCfgInitrdArchiveSymlink(hVfsFss, pszSrc, pszDst, pErrInfo);
1373 break;
1374 }
1375
1376 default:
1377 {
1378 LogRel(("Warning: File system type %#x for '%s' not implemented yet, sorry! Skipping ...\n",
1379 pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK, pDirEntry->szName));
1380 break;
1381 }
1382 }
1383 }
1384
1385 if (rc == VERR_NO_MORE_FILES)
1386 rc = VINF_SUCCESS;
1387
1388 RTVfsDirRelease(hVfsIoDir);
1389 return rc;
1390}
1391
1392
1393/**
1394 * Archives a directory recursively.
1395 *
1396 * @returns VBox status code.
1397 * @param hVfsFss The CPIO filesystem stream handle.
1398 * @param pszSrc The directory path or VFS spec. We append to the
1399 * buffer as we decend.
1400 * @param cchSrc The length of the input.
1401 * @param pszDst The name to archive it the under. We append to the
1402 * buffer as we decend.
1403 * @param cchDst The length of the input.
1404 * @param pErrInfo Error info buffer (saves stack space).
1405 */
1406static int qemuFwCfgInitrdArchiveDir(RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
1407 char pszDst[RTPATH_MAX], size_t cchDst,
1408 PRTERRINFOSTATIC pErrInfo)
1409{
1410 RT_NOREF(cchSrc);
1411
1412 char szSrcAbs[RTPATH_MAX];
1413 int rc = RTPathAbs(pszSrc, szSrcAbs, sizeof(szSrcAbs));
1414 if (RT_FAILURE(rc))
1415 return rc;
1416
1417 union
1418 {
1419 uint8_t abPadding[QEMUFWCFG_DIRENTRY_BUF_SIZE];
1420 RTDIRENTRYEX DirEntry;
1421 } uBuf;
1422
1423 return qemuFwCfgInitrdArchiveDirSub(hVfsFss, szSrcAbs, strlen(szSrcAbs), pszDst, cchDst, &uBuf.DirEntry, pErrInfo);
1424}
1425
1426
1427/**
1428 * Creates an on the fly initramfs for a given root directory.
1429 *
1430 * @returns VBox status code.
1431 * @param pThis The QEMU fw config device instance.
1432 * @param pszPath The path to work on.
1433 */
1434static int qemuFwCfgInitrdCreate(PDEVQEMUFWCFG pThis, const char *pszPath)
1435{
1436 /*
1437 * First open the output file.
1438 */
1439 RTVFSFSSTREAM hVfsFss;
1440 int rc = qemuFwCfgCreateOutputArchive(pThis, &hVfsFss);
1441 if (RT_FAILURE(rc))
1442 return rc;
1443
1444 /*
1445 * Construct/copy the source name.
1446 */
1447 char szSrc[RTPATH_MAX];
1448 rc = RTStrCopy(szSrc, sizeof(szSrc), pszPath);
1449 if (RT_SUCCESS(rc))
1450 {
1451 RTERRINFOSTATIC ErrInfo;
1452 char szDst[RTPATH_MAX]; RT_ZERO(szDst);
1453 rc = qemuFwCfgInitrdArchiveDir(hVfsFss, szSrc, strlen(szSrc),
1454 szDst, strlen(szDst), &ErrInfo);
1455 }
1456
1457 /*
1458 * Finalize the archive.
1459 */
1460 int rc2 = RTVfsFsStrmEnd(hVfsFss); AssertRC(rc2);
1461 RTVfsFsStrmRelease(hVfsFss);
1462 return rc;
1463}
1464
1465
1466/**
1467 * Checks whether creation of the initrd should be done on the fly.
1468 *
1469 * @returns VBox status code.
1470 * @param pThis The QEMU fw config device instance.
1471 */
1472static int qemuFwCfgInitrdMaybeCreate(PDEVQEMUFWCFG pThis)
1473{
1474 PPDMDEVINS pDevIns = pThis->pDevIns;
1475 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1476
1477 /* Query the path from the CFGM key. */
1478 char *pszFilePath = NULL;
1479 int rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, "InitrdImage", &pszFilePath);
1480 if (RT_SUCCESS(rc))
1481 {
1482 if (RTDirExists(pszFilePath))
1483 {
1484 rc = qemuFwCfgInitrdCreate(pThis, pszFilePath);
1485 if (RT_FAILURE(rc))
1486 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1487 N_("QemuFwCfg: failed to create the in memory initram filesystem"));
1488 }
1489 /*else: Probably a normal file. */
1490 PDMDevHlpMMHeapFree(pDevIns, pszFilePath);
1491 }
1492 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
1493 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1494 N_("Configuration error: Querying \"InitrdImage\" as a string failed"));
1495 else
1496 rc = VINF_SUCCESS;
1497
1498 return rc;
1499}
1500
1501
1502/**
1503 * Registers a file item with the given name for consumption by the firmware.
1504 *
1505 * @returns VBox status code.
1506 * @param pThis The QEMU fw config device instance.
1507 * @param pszFilename The filename to use.
1508 * @param cbData Size of the file in bytes.
1509 * @param pfnSetup Setup callback - optional.
1510 * @param pfnRead Read callback - optional.
1511 * @param pfnWrite Write callback - optional.
1512 * @param pfnCleanup Cleanup callback when the item gets de-selected - optional.
1513 */
1514static int qemuFwCfgR3FileRegister(PDEVQEMUFWCFG pThis, const char *pszFilename, uint32_t cbData,
1515 PFNQEMUFWCFGITEMSETUP pfnSetup, PFNQEMUFWCFGITEMREAD pfnRead,
1516 PFNQEMUFWCFGITEMWRITE pfnWrite, PFNQEMUFWCFGITEMCLEANUP pfnCleanup)
1517{
1518 AssertReturn(strlen(pszFilename) <= QEMU_FW_CFG_ITEM_FILE_NAME_MAX, VERR_FILENAME_TOO_LONG);
1519
1520 PQEMUFWCFGFILEENTRY pEntry = NULL;
1521 if (pThis->cCfgFiles == pThis->cCfgFilesMax)
1522 {
1523 /* Grow the array. */
1524 PQEMUFWCFGFILEENTRY paCfgFilesNew = (PQEMUFWCFGFILEENTRY)RTMemRealloc(pThis->paCfgFiles,
1525 (pThis->cCfgFilesMax + 10) * sizeof(*pThis->paCfgFiles));
1526 if (!paCfgFilesNew)
1527 return VERR_NO_MEMORY;
1528
1529 pThis->paCfgFiles = paCfgFilesNew;
1530 pThis->cCfgFilesMax += 10;
1531 }
1532
1533 pEntry = &pThis->paCfgFiles[pThis->cCfgFiles];
1534 pThis->cCfgFiles++;
1535
1536 pEntry->cbFile = cbData;
1537 strncpy(&pEntry->szFilename[0], pszFilename, sizeof(pEntry->szFilename));
1538 pEntry->Cfg.uCfgItem = QEMU_FW_CFG_ITEM_FILE_USER_FIRST + pThis->cCfgFiles - 1;
1539 pEntry->Cfg.pszItem = &pEntry->szFilename[0];
1540 pEntry->Cfg.pszCfgmKey = NULL;
1541 pEntry->Cfg.pfnSetup = pfnSetup ? pfnSetup : qemuFwCfgR3SetupFileGeneric;
1542 pEntry->Cfg.pfnRead = pfnRead;
1543 pEntry->Cfg.pfnWrite = pfnWrite;
1544 pEntry->Cfg.pfnCleanup = pfnCleanup;
1545 return VINF_SUCCESS;
1546}
1547
1548
1549/**
1550 * RAM framebuffer config write callback.
1551 *
1552 * @param pThis The QEMU fw config device instance.
1553 * @param pItem Pointer to the selected item.
1554 * @param off Where to start writing to.
1555 * @param pvBuf The data to write.
1556 * @param cbToWrite How much to write.
1557 * @param pcbWritten Where to store the amount of bytes written.
1558 */
1559static DECLCALLBACK(int) qemuFwCfgR3RamfbCfgWrite(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, const void *pvBuf,
1560 uint32_t cbToWrite, uint32_t *pcbWritten)
1561{
1562 RT_NOREF(pItem);
1563
1564 AssertReturn(!off && cbToWrite == sizeof(QEMURAMFBCONFIG), VERR_NOT_SUPPORTED);
1565 *pcbWritten = cbToWrite;
1566
1567 PCQEMURAMFBCONFIG pRamfbCfg = (PCQEMURAMFBCONFIG)pvBuf;
1568 if ( RT_BE2H_U32(pRamfbCfg->u32FourCC) != QEMU_RAMFB_CFG_FORMAT
1569 || RT_BE2H_U32(pRamfbCfg->u32Flags) != 0)
1570 return VERR_NOT_SUPPORTED;
1571
1572 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1573 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1574
1575 pThis->RamfbCfg.GCPhysRamfbBase = RT_BE2H_U64(pRamfbCfg->GCPhysRamfbBase);
1576 pThis->RamfbCfg.cbStride = RT_BE2H_U32(pRamfbCfg->cbStride);
1577 pThis->RamfbCfg.cWidth = RT_BE2H_U32(pRamfbCfg->cWidth);
1578 pThis->RamfbCfg.cHeight = RT_BE2H_U32(pRamfbCfg->cHeight);
1579 pThis->RamfbCfg.u32FourCC = RT_BE2H_U32(pRamfbCfg->u32FourCC);
1580 pThis->RamfbCfg.u32Flags = RT_BE2H_U32(pRamfbCfg->u32Flags);
1581
1582 if (pThis->pDrvL0)
1583 {
1584 int rc = pThis->pDrvL0->pfnResize(pThis->pDrvL0, QEMU_RAMFB_CFG_BPP * 8, NULL /*pvVRAM*/,
1585 pThis->RamfbCfg.cbStride,
1586 pThis->RamfbCfg.cWidth,
1587 pThis->RamfbCfg.cHeight);
1588 AssertRC(rc);
1589 }
1590
1591 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1592
1593 return VINF_SUCCESS;
1594}
1595
1596
1597/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
1598
1599/**
1600 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
1601 */
1602static DECLCALLBACK(int) qemuFwCfgR3RamfbPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
1603{
1604 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1605
1606 LogFlowFunc(("\n"));
1607
1608 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1609 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1610
1611 if ( pThis->fRenderVRam
1612 && pThis->RamfbCfg.GCPhysRamfbBase)
1613 {
1614 if ( pThis->RamfbCfg.cWidth == pThis->pDrvL0->cx
1615 && pThis->RamfbCfg.cHeight == pThis->pDrvL0->cy
1616 && pThis->RamfbCfg.cbStride == pThis->pDrvL0->cbScanline
1617 && pThis->pDrvL0->pbData)
1618 {
1619 PDMDevHlpPhysReadUser(pThis->pDevIns, pThis->RamfbCfg.GCPhysRamfbBase, pThis->pDrvL0->pbData, pThis->RamfbCfg.cbStride * pThis->RamfbCfg.cHeight);
1620 AssertPtr(pThis->pDrvL0);
1621 pThis->pDrvL0->pfnUpdateRect(pThis->pDrvL0, 0, 0, pThis->RamfbCfg.cWidth, pThis->RamfbCfg.cHeight);
1622 }
1623 else
1624 LogFlowFunc(("Framebuffer dimension mismatch ({%u, %u, %u} vs {%u, %u, %u})\n",
1625 pThis->RamfbCfg.cWidth, pThis->RamfbCfg.cHeight, pThis->RamfbCfg.cbStride,
1626 pThis->pDrvL0->cx, pThis->pDrvL0->cy, pThis->pDrvL0->cbScanline));
1627 }
1628 else
1629 LogFlowFunc(("Rendering disabled or no RAM framebuffer set up\n"));
1630
1631 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1632 return VINF_SUCCESS;
1633}
1634
1635
1636/**
1637 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
1638 */
1639static DECLCALLBACK(int) qemuFwCfgR3RamfbPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
1640{
1641 RT_NOREF(pInterface, fFailOnResize);
1642 AssertReleaseFailed();
1643 return VERR_NOT_IMPLEMENTED;
1644}
1645
1646
1647/**
1648 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
1649 */
1650static DECLCALLBACK(int) qemuFwCfgR3RamfbPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
1651{
1652 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1653
1654 /*
1655 * Update the interval, then restart or stop the timer.
1656 */
1657 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
1658
1659 if (cMilliesInterval)
1660 return PDMDevHlpTimerSetMillies(pThis->pDevIns, pThis->hRamfbRefreshTimer, cMilliesInterval);
1661 return PDMDevHlpTimerStop(pThis->pDevIns, pThis->hRamfbRefreshTimer);
1662}
1663
1664
1665/**
1666 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
1667 */
1668static DECLCALLBACK(int) qemuFwCfgR3RamfbPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
1669{
1670 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
1671
1672 RT_NOREF(pInterface, pcx, pcy);
1673 AssertReleaseFailed();
1674 return VERR_NOT_IMPLEMENTED;
1675}
1676
1677
1678/**
1679 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
1680 */
1681static DECLCALLBACK(int) qemuFwCfgR3RamfbPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
1682 uint32_t *pcx, uint32_t *pcy)
1683{
1684 RT_NOREF(pInterface, ppbData, pcbData, pcx, pcy);
1685 return VERR_NOT_SUPPORTED;
1686}
1687
1688
1689/**
1690 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
1691 */
1692static DECLCALLBACK(void) qemuFwCfgR3RamfbPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
1693{
1694 NOREF(pInterface);
1695 LogFlowFunc(("pbData=%p\n", pbData));
1696 RTMemFree(pbData);
1697}
1698
1699
1700/**
1701 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
1702 */
1703static DECLCALLBACK(int) qemuFwCfgR3RamfbPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
1704 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1705{
1706 RT_NOREF(pInterface, pvData, x, y, cx, cy);
1707 AssertReleaseFailed();
1708 return VERR_NOT_IMPLEMENTED;
1709}
1710
1711
1712/**
1713 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
1714 */
1715static DECLCALLBACK(void) qemuFwCfgR3RamfbPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y,
1716 uint32_t cx, uint32_t cy)
1717{
1718 RT_NOREF(pInterface, x, y, cx, cy);
1719 AssertReleaseFailed();
1720}
1721
1722
1723/**
1724 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
1725 */
1726static DECLCALLBACK(int)
1727qemuFwCfgR3RamfbPortCopyRect(PPDMIDISPLAYPORT pInterface,
1728 uint32_t cx, uint32_t cy,
1729 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
1730 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
1731 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
1732 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
1733{
1734 RT_NOREF(pInterface, cx, cy, pbSrc, xSrc, ySrc, cxSrc, cySrc, cbSrcLine, cSrcBitsPerPixel, pbDst, xDst, yDst, cxDst, cyDst,
1735 cbDstLine, cDstBitsPerPixel);
1736 AssertReleaseFailed();
1737 return VINF_SUCCESS;
1738}
1739
1740
1741/**
1742 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
1743 */
1744static DECLCALLBACK(void) qemuFwCfgR3RamfbPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
1745{
1746 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1747
1748 LogFlowFunc(("fRender = %d\n", fRender));
1749
1750 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1751 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1752
1753 pThis->fRenderVRam = fRender;
1754
1755 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1756}
1757
1758
1759/**
1760 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
1761 */
1762static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
1763 bool fSupportsMoveCursor)
1764{
1765 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
1766}
1767
1768
1769/**
1770 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
1771 */
1772static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
1773{
1774 RT_NOREF(pInterface, x, y, fOutOfRange);
1775}
1776
1777
1778/**
1779 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
1780 */
1781static DECLCALLBACK(void) qemuFwCfgR3RamfbTimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1782{
1783 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1784 RT_NOREF(pvUser);
1785
1786 if (pThis->pDrvL0)
1787 pThis->pDrvL0->pfnRefresh(pThis->pDrvL0);
1788
1789 if (pThis->cMilliesRefreshInterval)
1790 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
1791}
1792
1793
1794/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
1795
1796/**
1797 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1798 */
1799static DECLCALLBACK(void *) qemuFwCfgR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1800{
1801 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IBase);
1802 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1803 if (pThis->fRamfbSupported)
1804 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPortRamfb);
1805 return NULL;
1806}
1807
1808
1809/**
1810 * @interface_method_impl{PDMDEVREG,pfnReset}
1811 */
1812static DECLCALLBACK(void) qemuFwCfgReset(PPDMDEVINS pDevIns)
1813{
1814 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1815
1816 qemuFwCfgR3ItemReset(pThis);
1817 pThis->GCPhysDma = 0;
1818
1819 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1820 RTVfsFileRelease(pThis->hVfsFileInitrd);
1821 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1822
1823 qemuFwCfgInitrdMaybeCreate(pThis); /* Ignores status code. */
1824}
1825
1826
1827/**
1828 * @interface_method_impl{PDMDEVREG,pfnAttach}
1829 *
1830 * This is like plugging in the monitor after turning on the PC.
1831 */
1832static DECLCALLBACK(int) qemuFwCfgR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1833{
1834 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1835
1836 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1837 ("QEMU RAM framebuffer device does not support hotplugging\n"),
1838 VERR_INVALID_PARAMETER);
1839
1840 switch (iLUN)
1841 {
1842 /* LUN #0: Display port. */
1843 case 0:
1844 {
1845 AssertLogRelMsgReturn(pThis->fRamfbSupported,
1846 ("QemuFwCfg: Trying to attach a display without the RAM framebuffer support being enabled!\n"),
1847 VERR_NOT_SUPPORTED);
1848
1849 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBaseL0, "Display Port");
1850 if (RT_SUCCESS(rc))
1851 {
1852 pThis->pDrvL0 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBaseL0, PDMIDISPLAYCONNECTOR);
1853 if (pThis->pDrvL0)
1854 {
1855 /* pThis->pDrvL0->pbData can be NULL when there is no framebuffer. */
1856 if ( pThis->pDrvL0->pfnRefresh
1857 && pThis->pDrvL0->pfnResize
1858 && pThis->pDrvL0->pfnUpdateRect)
1859 rc = VINF_SUCCESS;
1860 else
1861 {
1862 Assert(pThis->pDrvL0->pfnRefresh);
1863 Assert(pThis->pDrvL0->pfnResize);
1864 Assert(pThis->pDrvL0->pfnUpdateRect);
1865 pThis->pDrvL0 = NULL;
1866 pThis->pDrvBaseL0 = NULL;
1867 rc = VERR_INTERNAL_ERROR;
1868 }
1869 }
1870 else
1871 {
1872 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
1873 pThis->pDrvBaseL0 = NULL;
1874 rc = VERR_PDM_MISSING_INTERFACE;
1875 }
1876 }
1877 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1878 {
1879 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1880 rc = VINF_SUCCESS;
1881 }
1882 else
1883 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
1884 return rc;
1885 }
1886
1887 default:
1888 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1889 return VERR_PDM_NO_SUCH_LUN;
1890 }
1891}
1892
1893
1894/**
1895 * @interface_method_impl{PDMDEVREG,pfnDetach}
1896 *
1897 * This is like unplugging the monitor while the PC is still running.
1898 */
1899static DECLCALLBACK(void) qemuFwCfgR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1900{
1901 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1902 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("QEMU RAM framebuffer device does not support hotplugging\n"));
1903 RT_NOREF(fFlags);
1904
1905 /*
1906 * Reset the interfaces and update the controller state.
1907 */
1908 switch (iLUN)
1909 {
1910 /* LUN #0: Display port. */
1911 case 0:
1912 AssertLogRelMsg(pThis->fRamfbSupported,
1913 ("QemuFwCfg: Trying to detach a display without the RAM framebuffer support being enabled!\n"));
1914
1915 pThis->pDrvL0 = NULL;
1916 pThis->pDrvBaseL0 = NULL;
1917 break;
1918
1919 default:
1920 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1921 break;
1922 }
1923}
1924
1925
1926/**
1927 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1928 */
1929static DECLCALLBACK(int) qemuFwCfgDestruct(PPDMDEVINS pDevIns)
1930{
1931 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1932 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1933
1934 qemuFwCfgR3ItemReset(pThis);
1935 pThis->GCPhysDma = 0;
1936
1937 if (pThis->paCfgFiles)
1938 {
1939 Assert(pThis->cCfgFiles && pThis->cCfgFilesMax);
1940 RTMemFree(pThis->paCfgFiles);
1941 pThis->paCfgFiles = NULL;
1942 pThis->cCfgFiles = 0;
1943 pThis->cCfgFilesMax = 0;
1944 }
1945 else
1946 Assert(!pThis->cCfgFiles && !pThis->cCfgFilesMax);
1947
1948 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1949 RTVfsFileRelease(pThis->hVfsFileInitrd);
1950 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1951
1952 return VINF_SUCCESS;
1953}
1954
1955
1956/**
1957 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1958 */
1959static DECLCALLBACK(int) qemuFwCfgConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1960{
1961 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1962 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1963 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1964 Assert(iInstance == 0); RT_NOREF(iInstance);
1965
1966 /*
1967 * Validate configuration.
1968 */
1969 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DmaEnabled"
1970 "|MmioBase"
1971 "|MmioSize"
1972 "|KernelImage"
1973 "|InitrdImage"
1974 "|SetupImage"
1975 "|CmdLine"
1976 "|QemuRamfbSupport",
1977 "");
1978
1979 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DmaEnabled", &pThis->fDmaEnabled, false);
1980 if (RT_FAILURE(rc))
1981 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmaEnabled\""));
1982
1983 /*
1984 * Init the data.
1985 */
1986 pThis->pDevIns = pDevIns;
1987 pThis->pCfg = pCfg;
1988 pThis->u32Version = QEMU_FW_CFG_VERSION_LEGACY | (pThis->fDmaEnabled ? QEMU_FW_CFG_VERSION_DMA : 0);
1989 pThis->GCPhysDma = 0;
1990 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1991 pThis->paCfgFiles = NULL;
1992 pThis->cCfgFiles = 0;
1993 pThis->cCfgFilesMax = 0;
1994
1995 pThis->IBase.pfnQueryInterface = qemuFwCfgR3PortQueryInterface;
1996
1997 pThis->IPortRamfb.pfnUpdateDisplay = qemuFwCfgR3RamfbPortUpdateDisplay;
1998 pThis->IPortRamfb.pfnUpdateDisplayAll = qemuFwCfgR3RamfbPortUpdateDisplayAll;
1999 pThis->IPortRamfb.pfnQueryVideoMode = qemuFwCfgR3RamfbPortQueryVideoMode;
2000 pThis->IPortRamfb.pfnSetRefreshRate = qemuFwCfgR3RamfbPortSetRefreshRate;
2001 pThis->IPortRamfb.pfnTakeScreenshot = qemuFwCfgR3RamfbPortTakeScreenshot;
2002 pThis->IPortRamfb.pfnFreeScreenshot = qemuFwCfgR3RamfbPortFreeScreenshot;
2003 pThis->IPortRamfb.pfnDisplayBlt = qemuFwCfgR3RamfbPortDisplayBlt;
2004 pThis->IPortRamfb.pfnUpdateDisplayRect = qemuFwCfgR3RamfbPortUpdateDisplayRect;
2005 pThis->IPortRamfb.pfnCopyRect = qemuFwCfgR3RamfbPortCopyRect;
2006 pThis->IPortRamfb.pfnSetRenderVRAM = qemuFwCfgR3RamfbPortSetRenderVRAM;
2007 pThis->IPortRamfb.pfnSetViewport = NULL;
2008 pThis->IPortRamfb.pfnReportMonitorPositions = NULL;
2009 pThis->IPortRamfb.pfnSendModeHint = NULL;
2010 pThis->IPortRamfb.pfnReportHostCursorCapabilities = qemuFwCfgR3RamfbPortReportHostCursorCapabilities;
2011 pThis->IPortRamfb.pfnReportHostCursorPosition = qemuFwCfgR3RamfbPortReportHostCursorPosition;
2012
2013 RTGCPHYS GCPhysMmioBase = 0;
2014 rc = pHlp->pfnCFGMQueryU64(pCfg, "MmioBase", &GCPhysMmioBase);
2015 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2016 return PDMDEV_SET_ERROR(pDevIns, rc,
2017 N_("Configuration error: Failed to get the \"MmioBase\" value"));
2018 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2019 {
2020 /*
2021 * Register standard I/O Ports
2022 */
2023 IOMIOPORTHANDLE hIoPorts;
2024 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, QEMU_FW_CFG_IO_PORT_START, QEMU_FW_CFG_IO_PORT_SIZE, 0 /*fFlags*/,
2025 qemuFwCfgIoPortWrite, qemuFwCfgIoPortRead,
2026 "QEMU firmware configuration", NULL /*paExtDescs*/, &hIoPorts);
2027 AssertRCReturn(rc, rc);
2028 }
2029 else
2030 {
2031 uint32_t cbMmio = 0;
2032 rc = pHlp->pfnCFGMQueryU32(pCfg, "MmioSize", &cbMmio);
2033 if (RT_FAILURE(rc))
2034 return PDMDEV_SET_ERROR(pDevIns, rc,
2035 N_("Configuration error: Failed to get the \"MmioSize\" value"));
2036
2037 /*
2038 * Register and map the MMIO region.
2039 */
2040 IOMMMIOHANDLE hMmio;
2041 rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysMmioBase, cbMmio, qemuFwCfgMmioWrite, qemuFwCfgMmioRead,
2042 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, "QemuFwCfg", &hMmio);
2043 AssertRCReturn(rc, rc);
2044 }
2045
2046 qemuFwCfgR3ItemReset(pThis);
2047
2048 /* Setup the RAM based framebuffer support if configured. */
2049 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "QemuRamfbSupport", &pThis->fRamfbSupported, false);
2050 if (RT_FAILURE(rc))
2051 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"QemuRamfbSupport\""));
2052
2053 if (pThis->fRamfbSupported)
2054 {
2055 LogRel(("QemuFwCfg: RAM based framebuffer support enabled\n"));
2056 if (!pThis->fDmaEnabled)
2057 return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER, N_("Configuration error: Enabling \"QemuRamfbSupport\" requires \"DmaEnabled\""));
2058
2059 /* Critical section for synchronizing access. */
2060 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectRamfb, RT_SRC_POS, "Ramfb#%u", iInstance);
2061 AssertRCReturn(rc, rc);
2062
2063 /*
2064 * Create the refresh timer.
2065 */
2066 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, qemuFwCfgR3RamfbTimerRefresh, NULL,
2067 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Ramfb Refresh", &pThis->hRamfbRefreshTimer);
2068 AssertRCReturn(rc, rc);
2069
2070 /* Register a config file item and attach the driver below us. */
2071 rc = qemuFwCfgR3Attach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
2072 AssertRCReturn(rc, rc);
2073
2074 rc = qemuFwCfgR3FileRegister(pThis, "etc/ramfb", sizeof(pThis->RamfbCfg),
2075 NULL /* pfnSetup */, NULL /*pfnRead*/,
2076 qemuFwCfgR3RamfbCfgWrite, NULL /*pfnCleanup*/);
2077 AssertRCReturn(rc, rc);
2078 }
2079
2080 rc = qemuFwCfgInitrdMaybeCreate(pThis);
2081 if (RT_FAILURE(rc))
2082 return rc; /* Error set. */
2083
2084 return VINF_SUCCESS;
2085}
2086
2087
2088/**
2089 * The device registration structure.
2090 */
2091const PDMDEVREG g_DeviceQemuFwCfg =
2092{
2093 /* .u32Version = */ PDM_DEVREG_VERSION,
2094 /* .uReserved0 = */ 0,
2095 /* .szName = */ "qemu-fw-cfg",
2096 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2097 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
2098 /* .cMaxInstances = */ 1,
2099 /* .uSharedVersion = */ 42,
2100 /* .cbInstanceShared = */ sizeof(DEVQEMUFWCFG),
2101 /* .cbInstanceCC = */ 0,
2102 /* .cbInstanceRC = */ 0,
2103 /* .cMaxPciDevices = */ 0,
2104 /* .cMaxMsixVectors = */ 0,
2105 /* .pszDescription = */ "QEMU Firmware Config compatible device",
2106#if defined(IN_RING3)
2107 /* .pszRCMod = */ "",
2108 /* .pszR0Mod = */ "",
2109 /* .pfnConstruct = */ qemuFwCfgConstruct,
2110 /* .pfnDestruct = */ qemuFwCfgDestruct,
2111 /* .pfnRelocate = */ NULL,
2112 /* .pfnMemSetup = */ NULL,
2113 /* .pfnPowerOn = */ NULL,
2114 /* .pfnReset = */ qemuFwCfgReset,
2115 /* .pfnSuspend = */ NULL,
2116 /* .pfnResume = */ NULL,
2117 /* .pfnAttach = */ qemuFwCfgR3Attach,
2118 /* .pfnDetach = */ qemuFwCfgR3Detach,
2119 /* .pfnQueryInterface = */ NULL,
2120 /* .pfnInitComplete = */ NULL,
2121 /* .pfnPowerOff = */ NULL,
2122 /* .pfnSoftReset = */ NULL,
2123 /* .pfnReserved0 = */ NULL,
2124 /* .pfnReserved1 = */ NULL,
2125 /* .pfnReserved2 = */ NULL,
2126 /* .pfnReserved3 = */ NULL,
2127 /* .pfnReserved4 = */ NULL,
2128 /* .pfnReserved5 = */ NULL,
2129 /* .pfnReserved6 = */ NULL,
2130 /* .pfnReserved7 = */ NULL,
2131#elif defined(IN_RING0)
2132 /* .pfnEarlyConstruct = */ NULL,
2133 /* .pfnConstruct = */ NULL,
2134 /* .pfnDestruct = */ NULL,
2135 /* .pfnFinalDestruct = */ NULL,
2136 /* .pfnRequest = */ NULL,
2137 /* .pfnReserved0 = */ NULL,
2138 /* .pfnReserved1 = */ NULL,
2139 /* .pfnReserved2 = */ NULL,
2140 /* .pfnReserved3 = */ NULL,
2141 /* .pfnReserved4 = */ NULL,
2142 /* .pfnReserved5 = */ NULL,
2143 /* .pfnReserved6 = */ NULL,
2144 /* .pfnReserved7 = */ NULL,
2145#elif defined(IN_RC)
2146 /* .pfnConstruct = */ NULL,
2147 /* .pfnReserved0 = */ NULL,
2148 /* .pfnReserved1 = */ NULL,
2149 /* .pfnReserved2 = */ NULL,
2150 /* .pfnReserved3 = */ NULL,
2151 /* .pfnReserved4 = */ NULL,
2152 /* .pfnReserved5 = */ NULL,
2153 /* .pfnReserved6 = */ NULL,
2154 /* .pfnReserved7 = */ NULL,
2155#else
2156# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2157#endif
2158 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2159};
2160
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