VirtualBox

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

Last change on this file since 107459 was 107459, checked in by vboxsync, 12 days ago

VBox/Devices/PC/DevQuemuFwCfg.cpp: Break out of the loop if on error, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 79.9 KB
Line 
1/* $Id: DevQemuFwCfg.cpp 107459 2025-01-07 11:01:07Z vboxsync $ */
2/** @file
3 * DevQemuFwCfg - QEMU firmware configuration compatible device.
4 */
5
6/*
7 * Copyright (C) 2020-2024 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 &lt;VM name&gt; "VBoxInternal/Devices/qemu-fw-cfg/0/Config/KernelImage" /path/to/kernel
44 * VBoxManage setextradata &lt;VM name&gt; "VBoxInternal/Devices/qemu-fw-cfg/0/Config/InitrdImage" /path/to/initrd
45 * VBoxManage setextradata &lt;VM name&gt; "VBoxInternal/Devices/qemu-fw-cfg/0/Config/CmdLine" "&lt;cmd line string&gt;"
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 &lt;VM name&gt; "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 &lt;VM name&gt; "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 * @param pThis The QEMU fw config device instance.
311 * @param pItem Pointer to the selected item.
312 */
313typedef DECLCALLBACKTYPE(void, FNQEMUFWCFGITEMCLEANUP,(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem));
314/** Pointer to a FNQEMUFWCFGITEMCLEANUP() function. */
315typedef FNQEMUFWCFGITEMCLEANUP *PFNQEMUFWCFGITEMCLEANUP;
316
317
318/**
319 * A supported configuration item descriptor.
320 */
321typedef struct QEMUFWCFGITEM
322{
323 /** The config item value. */
324 uint16_t uCfgItem;
325 /** Name of the item. */
326 const char *pszItem;
327 /** Optional CFGM key to lookup the content. */
328 const char *pszCfgmKey;
329
330 /** Setup callback. */
331 PFNQEMUFWCFGITEMSETUP pfnSetup;
332 /** Read callback. */
333 PFNQEMUFWCFGITEMREAD pfnRead;
334 /** Write callback. */
335 PFNQEMUFWCFGITEMWRITE pfnWrite;
336 /** Cleanup callback. */
337 PFNQEMUFWCFGITEMCLEANUP pfnCleanup;
338} QEMUFWCFGITEM;
339/** Pointer to a configuration item descriptor. */
340typedef QEMUFWCFGITEM *PQEMUFWCFGITEM;
341
342
343/**
344 * A config file entry.
345 */
346typedef struct QEMUFWCFGFILEENTRY
347{
348 /** The config item structure. */
349 QEMUFWCFGITEM Cfg;
350 /** Size of the file in bytes. */
351 uint32_t cbFile;
352 /** The stored filename as an zero terminated ASCII string. */
353 char szFilename[QEMU_FW_CFG_ITEM_FILE_NAME_MAX + 1];
354} QEMUFWCFGFILEENTRY;
355/** Pointer to a config file entry. */
356typedef QEMUFWCFGFILEENTRY *PQEMUFWCFGFILEENTRY;
357/** Pointer to a const config file entry. */
358typedef const QEMUFWCFGFILEENTRY *PCQEMUFWCFGFILEENTRY;
359
360
361/**
362 * QEMU firmware config instance data structure.
363 */
364typedef struct DEVQEMUFWCFG
365{
366 /** Pointer back to the device instance. */
367 PPDMDEVINS pDevIns;
368 /** The configuration handle. */
369 PCFGMNODE pCfg;
370
371 /** LUN\#0: The display port base interface. */
372 PDMIBASE IBase;
373 /** LUN\#0: The display port interface for the RAM based framebuffer if enabled. */
374 PDMIDISPLAYPORT IPortRamfb;
375
376 /** Pointer to base interface of the driver - LUN#0. */
377 R3PTRTYPE(PPDMIBASE) pDrvBaseL0;
378 /** Pointer to display connector interface of the driver - LUN#0. */
379 R3PTRTYPE(PPDMIDISPLAYCONNECTOR) pDrvL0;
380
381 /** Pointer to the currently selected item. */
382 PCQEMUFWCFGITEM pCfgItem;
383 /** Offset of the next byte to read from the start of the data item. */
384 uint32_t offCfgItemNext;
385 /** How many bytes are left for transfer. */
386 uint32_t cbCfgItemLeft;
387 /** Version register. */
388 uint32_t u32Version;
389 /** Guest physical address of the DMA descriptor. */
390 RTGCPHYS GCPhysDma;
391 /** VFS file of the on-the-fly created initramfs. */
392 RTVFSFILE hVfsFileInitrd;
393
394 /** Pointer to the array of config file items. */
395 PQEMUFWCFGFILEENTRY paCfgFiles;
396 /** Number of entries in the config file item array. */
397 uint32_t cCfgFiles;
398 /** Number if entries allocated in the config file items array. */
399 uint32_t cCfgFilesMax;
400
401 /** Critical section for synchronizing the RAM framebuffer access. */
402 PDMCRITSECT CritSectRamfb;
403 /** The refresh interval for the Ramfb support. */
404 uint32_t cMilliesRefreshInterval;
405 /** Refresh timer handle for the Ramfb support. */
406 TMTIMERHANDLE hRamfbRefreshTimer;
407 /** The current rambuffer config if enabled. */
408 QEMURAMFBCONFIG RamfbCfg;
409 /** Flag whether rendering the VRAM is enabled currently. */
410 bool fRenderVRam;
411 /** Flag whether the RAM based framebuffer device is enabled. */
412 bool fRamfbSupported;
413 /** Flag whether the DMA interface is available. */
414 bool fDmaEnabled;
415
416 /** Scratch buffer for config item specific data. */
417 union
418 {
419 uint8_t u8;
420 uint16_t u16;
421 uint32_t u32;
422 uint64_t u64;
423 /** VFS file handle. */
424 RTVFSFILE hVfsFile;
425 /** Firmware config file entry. */
426 QEMUFWCFGFILE CfgFile;
427 /** Byte view. */
428 uint8_t ab[8];
429 } u;
430} DEVQEMUFWCFG;
431
432
433/**
434 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the signature configuration item.}
435 */
436static DECLCALLBACK(int) qemuFwCfgR3SetupSignature(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
437{
438 RT_NOREF(pThis, pItem);
439 uint8_t abSig[] = { 'Q', 'E', 'M', 'U' };
440 memcpy(&pThis->u.ab[0], &abSig[0], sizeof(abSig));
441 *pcbItem = sizeof(abSig);
442 return VINF_SUCCESS;
443}
444
445
446/**
447 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the version configuration item.}
448 */
449static DECLCALLBACK(int) qemuFwCfgR3SetupVersion(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
450{
451 RT_NOREF(pThis, pItem);
452 memcpy(&pThis->u.ab[0], &pThis->u32Version, sizeof(pThis->u32Version));
453 *pcbItem = sizeof(pThis->u32Version);
454 return VINF_SUCCESS;
455}
456
457
458/**
459 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the file directory configuration item.}
460 */
461static DECLCALLBACK(int) qemuFwCfgR3SetupFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
462{
463 RT_NOREF(pItem);
464 uint32_t cCfgFiles = RT_H2BE_U32(pThis->cCfgFiles);
465 memcpy(&pThis->u.ab[0], &cCfgFiles, sizeof(cCfgFiles));
466 *pcbItem = sizeof(uint32_t) + pThis->cCfgFiles * sizeof(QEMUFWCFGFILE);
467 return VINF_SUCCESS;
468}
469
470
471/**
472 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a VFS file type configuration item.}
473 */
474static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFileSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
475{
476 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
477
478 int rc = VINF_SUCCESS;
479 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
480 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_SIZE
481 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
482 {
483 RTVfsFileRetain(pThis->hVfsFileInitrd);
484 hVfsFile = pThis->hVfsFileInitrd;
485 }
486 else
487 {
488 /* Query the path from the CFGM key. */
489 char *pszFilePath = NULL;
490 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
491 if (RT_SUCCESS(rc))
492 {
493 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
494 if (RT_FAILURE(rc))
495 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
496 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
497 }
498 else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
499 {
500 pThis->u.u32 = 0;
501 *pcbItem = sizeof(uint32_t);
502 rc = VINF_SUCCESS;
503 }
504 else
505 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
506 }
507
508 if ( RT_SUCCESS(rc)
509 && hVfsFile != NIL_RTVFSFILE)
510 {
511 uint64_t cbFile = 0;
512 rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
513 if (RT_SUCCESS(rc))
514 {
515 if (cbFile < _4G)
516 {
517 pThis->u.u32 = (uint32_t)cbFile;
518 *pcbItem = sizeof(uint32_t);
519 }
520 else
521 {
522 rc = VERR_BUFFER_OVERFLOW;
523 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
524 }
525 }
526 else
527 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
528 RTVfsFileRelease(hVfsFile);
529 }
530
531 return rc;
532}
533
534
535/**
536 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a string type configuration item.}
537 */
538static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStrSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
539{
540 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
541
542 /* Query the string from the CFGM key. */
543 char sz[_4K];
544 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
545 if (RT_SUCCESS(rc))
546 {
547 pThis->u.u32 = (uint32_t)strlen(&sz[0]) + 1;
548 *pcbItem = sizeof(uint32_t);
549 }
550 else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
551 {
552 pThis->u.u32 = 0;
553 *pcbItem = sizeof(uint32_t);
554 rc = VINF_SUCCESS;
555 }
556 else
557 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
558
559 return rc;
560}
561
562
563/**
564 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a string type configuration item gathered from CFGM.}
565 */
566static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
567{
568 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
569
570 /* Query the string from the CFGM key. */
571 char sz[_4K];
572 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
573 if (RT_SUCCESS(rc))
574 *pcbItem = (uint32_t)strlen(&sz[0]) + 1;
575 else
576 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
577
578 return rc;
579}
580
581
582/**
583 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a VFS file type configuration item.}
584 */
585static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
586{
587 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
588
589 int rc = VINF_SUCCESS;
590 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_DATA
591 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
592 {
593 RTVfsFileRetain(pThis->hVfsFileInitrd);
594 pThis->u.hVfsFile = pThis->hVfsFileInitrd;
595 }
596 else
597 {
598 /* Query the path from the CFGM key. */
599 char *pszFilePath = NULL;
600 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
601 if (RT_SUCCESS(rc))
602 {
603 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &pThis->u.hVfsFile);
604 if (RT_FAILURE(rc))
605 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
606 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
607 }
608 else
609 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
610 }
611
612 if (RT_SUCCESS(rc))
613 {
614 uint64_t cbFile = 0;
615 rc = RTVfsFileQuerySize(pThis->u.hVfsFile, &cbFile);
616 if (RT_SUCCESS(rc))
617 {
618 if (cbFile < _4G)
619 *pcbItem = (uint32_t)cbFile;
620 else
621 {
622 rc = VERR_BUFFER_OVERFLOW;
623 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
624 }
625 }
626 else
627 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
628 }
629
630 return rc;
631}
632
633
634/**
635 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a configuration item having its data stored in the scratch buffer.}
636 */
637static DECLCALLBACK(int) qemuFwCfgR3ReadSimple(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
638 uint32_t cbToRead, uint32_t *pcbRead)
639{
640 RT_NOREF(pThis, pItem);
641 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
642 *pcbRead = cbToRead;
643 return VINF_SUCCESS;
644}
645
646
647/**
648 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a VFS file type configuration item.}
649 */
650static DECLCALLBACK(int) qemuFwCfgR3ReadVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
651 uint32_t cbToRead, uint32_t *pcbRead)
652{
653 RT_NOREF(pItem);
654 size_t cbRead = 0;
655 int rc = RTVfsFileReadAt(pThis->u.hVfsFile, off, pvBuf, cbToRead, &cbRead);
656 if (RT_SUCCESS(rc))
657 *pcbRead = (uint32_t)cbRead;
658
659 return rc;
660}
661
662
663/**
664 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads a string item gathered from CFGM.}
665 */
666static DECLCALLBACK(int) qemuFwCfgR3ReadStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
667 uint32_t cbToRead, uint32_t *pcbRead)
668{
669 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
670
671 /* Query the string from the CFGM key. */
672 char sz[_4K];
673 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
674 if (RT_SUCCESS(rc))
675 {
676 uint32_t cch = (uint32_t)strlen(sz) + 1;
677 if (off < cch)
678 {
679 uint32_t cbRead = RT_MIN(cbToRead, off - cch);
680 memcpy(pvBuf, &sz[off], cbRead);
681 *pcbRead = cbRead;
682 }
683 else
684 rc = VERR_BUFFER_OVERFLOW;
685 }
686 else
687 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
688
689 return rc;
690}
691
692
693/**
694 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from the file directory.}
695 */
696static DECLCALLBACK(int) qemuFwCfgR3ReadFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
697 uint32_t cbToRead, uint32_t *pcbRead)
698{
699 RT_NOREF(pItem);
700
701 /* The first 4 bytes are the number of entries following. */
702 if (off < sizeof(uint32_t))
703 {
704 cbToRead = RT_MIN(cbToRead, sizeof(uint32_t) - off);
705 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
706 *pcbRead = cbToRead;
707 }
708 else
709 {
710 off -= sizeof(uint32_t);
711
712 /* The entries are static, so we can deduce the entry number from the offset. */
713 uint32_t idxEntry = off / sizeof(pThis->u.CfgFile);
714 AssertReturn(idxEntry < pThis->cCfgFiles, VERR_INTERNAL_ERROR);
715
716 off %= sizeof(pThis->u.CfgFile);
717 cbToRead = RT_MIN(cbToRead, sizeof(pThis->u.CfgFile));
718
719 /* Setup the config file item. */
720 PCQEMUFWCFGFILEENTRY pEntry = &pThis->paCfgFiles[idxEntry];
721 pThis->u.CfgFile.cbFile = RT_H2BE_U32(pEntry->cbFile);
722 pThis->u.CfgFile.uCfgItem = RT_H2BE_U16(pEntry->Cfg.uCfgItem);
723 pThis->u.CfgFile.u16Rsvd = 0;
724 strncpy(pThis->u.CfgFile.szFilename, pEntry->Cfg.pszItem, sizeof(pThis->u.CfgFile.szFilename) - 1);
725 pThis->u.CfgFile.szFilename[QEMU_FW_CFG_ITEM_FILE_NAME_MAX] = '\0';
726
727 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
728 *pcbRead = cbToRead;
729 }
730 return VINF_SUCCESS;
731}
732
733
734/**
735 * @interface_method_impl{QEMUFWCFGITEM,pfnCleanup, Cleans up a VFS file type configuration item.}
736 */
737static DECLCALLBACK(void) qemuFwCfgR3CleanupVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem)
738{
739 RT_NOREF(pItem);
740 RTVfsFileRelease(pThis->u.hVfsFile);
741 pThis->u.hVfsFile = NIL_RTVFSFILE;
742}
743
744
745/**
746 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Generic setup routine for file entries which don't have a dedicated setup routine.}
747 */
748static DECLCALLBACK(int) qemuFwCfgR3SetupFileGeneric(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
749{
750 RT_NOREF(pItem);
751 *pcbItem = pThis->paCfgFiles[pItem->uCfgItem - QEMU_FW_CFG_ITEM_FILE_USER_FIRST].cbFile;
752 return VINF_SUCCESS;
753}
754
755
756/**
757 * Supported config items.
758 */
759static const QEMUFWCFGITEM g_aQemuFwCfgItems[] =
760{
761 /** u16Selector pszItem pszCfgmKey pfnSetup pfnRead pfnWrite pfnCleanup */
762 { QEMU_FW_CFG_ITEM_SIGNATURE, "Signature", NULL, qemuFwCfgR3SetupSignature, qemuFwCfgR3ReadSimple, NULL, NULL },
763 { QEMU_FW_CFG_ITEM_VERSION, "Version", NULL, qemuFwCfgR3SetupVersion, qemuFwCfgR3ReadSimple, NULL, NULL },
764 { QEMU_FW_CFG_ITEM_KERNEL_SIZE, "KrnlSz", "KernelImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
765 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "KrnlDat", "KernelImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
766 { QEMU_FW_CFG_ITEM_INITRD_SIZE, "InitrdSz", "InitrdImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
767 { QEMU_FW_CFG_ITEM_INITRD_DATA, "InitrdDat", "InitrdImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
768 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE, "SetupSz", "SetupImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL, NULL },
769 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA, "SetupDat", "SetupImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, NULL, qemuFwCfgR3CleanupVfsFile },
770 { QEMU_FW_CFG_ITEM_CMD_LINE_SIZE, "CmdLineSz", "CmdLine", qemuFwCfgR3SetupCfgmStrSz, qemuFwCfgR3ReadSimple, NULL, NULL },
771 { QEMU_FW_CFG_ITEM_CMD_LINE_DATA, "CmdLineDat", "CmdLine", qemuFwCfgR3SetupCfgmStr, qemuFwCfgR3ReadStr, NULL, NULL },
772 { QEMU_FW_CFG_ITEM_FILE_DIR, "FileDir", NULL, qemuFwCfgR3SetupFileDir, qemuFwCfgR3ReadFileDir, NULL, NULL }
773};
774
775
776/**
777 * Resets the currently selected item.
778 *
779 * @param pThis The QEMU fw config device instance.
780 */
781static void qemuFwCfgR3ItemReset(PDEVQEMUFWCFG pThis)
782{
783 if ( pThis->pCfgItem
784 && pThis->pCfgItem->pfnCleanup)
785 pThis->pCfgItem->pfnCleanup(pThis, pThis->pCfgItem);
786
787 pThis->pCfgItem = NULL;
788 pThis->offCfgItemNext = 0;
789 pThis->cbCfgItemLeft = 0;
790}
791
792
793/**
794 * Selects the given config item.
795 *
796 * @returns VBox status code.
797 * @param pThis The QEMU fw config device instance.
798 * @param uCfgItem The configuration item to select.
799 */
800static int qemuFwCfgItemSelect(PDEVQEMUFWCFG pThis, uint16_t uCfgItem)
801{
802 LogFlowFunc(("uCfgItem=%#x\n", uCfgItem));
803
804 qemuFwCfgR3ItemReset(pThis);
805
806 PCQEMUFWCFGITEM pCfgItem = NULL;;
807
808 /* Check whether this is a file item. */
809 if (uCfgItem >= QEMU_FW_CFG_ITEM_FILE_USER_FIRST)
810 {
811 uCfgItem -= QEMU_FW_CFG_ITEM_FILE_USER_FIRST;
812 if (uCfgItem < pThis->cCfgFiles)
813 pCfgItem = &pThis->paCfgFiles[uCfgItem].Cfg;
814 }
815 else
816 {
817 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQemuFwCfgItems); i++)
818 {
819 pCfgItem = &g_aQemuFwCfgItems[i];
820 if (pCfgItem->uCfgItem == uCfgItem)
821 break;
822 }
823 }
824
825 if (pCfgItem)
826 {
827 uint32_t cbItem = 0;
828 AssertPtrReturn(pCfgItem->pfnSetup, VERR_INVALID_STATE);
829
830 int rc = pCfgItem->pfnSetup(pThis, pCfgItem, &cbItem);
831 if (RT_SUCCESS(rc))
832 {
833 pThis->pCfgItem = pCfgItem;
834 pThis->cbCfgItemLeft = cbItem;
835 return VINF_SUCCESS;
836 }
837
838 return rc;
839 }
840
841 return VERR_NOT_FOUND;
842}
843
844
845/**
846 * Processes a DMA transfer.
847 *
848 * @param pThis The QEMU fw config device instance.
849 * @param GCPhysDma The guest physical address of the DMA descriptor.
850 */
851static void qemuFwCfgDmaXfer(PDEVQEMUFWCFG pThis, RTGCPHYS GCPhysDma)
852{
853 QEMUFWDMADESC DmaDesc; RT_ZERO(DmaDesc);
854
855 LogFlowFunc(("pThis=%p GCPhysDma=%RGp\n", pThis, GCPhysDma));
856
857 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysDma, &DmaDesc, sizeof(DmaDesc));
858
859 /* Convert from big endianess to host endianess. */
860 DmaDesc.u32Ctrl = RT_BE2H_U32(DmaDesc.u32Ctrl);
861 DmaDesc.u32Length = RT_BE2H_U32(DmaDesc.u32Length);
862 DmaDesc.u64GCPhysBuf = RT_BE2H_U64(DmaDesc.u64GCPhysBuf);
863
864 LogFlowFunc(("u32Ctrl=%#x u32Length=%u u64GCPhysBuf=%llx\n",
865 DmaDesc.u32Ctrl, DmaDesc.u32Length, DmaDesc.u64GCPhysBuf));
866
867 /* If the select bit is set a select is performed. */
868 int rc = VINF_SUCCESS;
869 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_SELECT)
870 rc = qemuFwCfgItemSelect(pThis, QEMU_FW_CFG_DMA_GET_CFG_ITEM(DmaDesc.u32Ctrl));
871 else if (!pThis->pCfgItem)
872 rc = VERR_NOT_FOUND;
873
874 if (RT_SUCCESS(rc))
875 {
876 if ( ( DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE
877 && !pThis->pCfgItem->pfnWrite)
878 || ( DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ
879 && !pThis->pCfgItem->pfnRead))
880 rc = VERR_NOT_SUPPORTED;
881 else if ( !pThis->pCfgItem
882 || !pThis->cbCfgItemLeft)
883 {
884 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
885 {
886 /* Item is not supported, just zero out the indicated area. */
887 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
888 uint32_t cbLeft = DmaDesc.u32Length;
889
890 while ( RT_SUCCESS(rc)
891 && cbLeft)
892 {
893 uint32_t cbZero = RT_MIN(_64K, cbLeft);
894
895 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &g_abRTZero64K[0], cbZero);
896
897 cbLeft -= cbZero;
898 GCPhysCur += cbZero;
899 }
900 }
901 /* else: Assume Skip or Write and ignore. */
902 }
903 else
904 {
905 /* Normal path. */
906 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
907 uint32_t cbLeft = RT_MIN(DmaDesc.u32Length, pThis->cbCfgItemLeft);
908
909 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE)
910 {
911 while ( RT_SUCCESS(rc)
912 && cbLeft)
913 {
914 uint8_t abTmp[_1K];
915 uint32_t cbThisWrite = RT_MIN(sizeof(abTmp), cbLeft);
916 uint32_t cbWritten;
917
918 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbThisWrite);
919 rc = pThis->pCfgItem->pfnWrite(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
920 cbThisWrite, &cbWritten);
921 if (RT_SUCCESS(rc))
922 {
923 cbLeft -= cbWritten;
924 GCPhysCur += cbWritten;
925
926 pThis->offCfgItemNext += cbWritten;
927 pThis->cbCfgItemLeft -= cbWritten;
928 }
929 }
930 }
931 else
932 {
933 while ( RT_SUCCESS(rc)
934 && cbLeft)
935 {
936 uint8_t abTmp[_1K];
937 uint32_t cbThisRead = RT_MIN(sizeof(abTmp), cbLeft);
938 uint32_t cbRead;
939
940 rc = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
941 cbThisRead, &cbRead);
942 if (RT_SUCCESS(rc))
943 {
944 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
945 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbRead);
946 /* else: Assume Skip */
947
948 cbLeft -= cbRead;
949 GCPhysCur += cbRead;
950
951 pThis->offCfgItemNext += cbRead;
952 pThis->cbCfgItemLeft -= cbRead;
953 }
954 }
955 }
956 }
957 }
958
959 LogFlowFunc(("pThis=%p GCPhysDma=%RGp -> %Rrc\n", pThis, GCPhysDma, rc));
960
961 /* Write back the control field. */
962 uint32_t u32Resp = RT_SUCCESS(rc) ? 0 : RT_H2BE_U32(QEMU_FW_CFG_DMA_ERROR);
963 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysDma, &u32Resp, sizeof(u32Resp));
964}
965
966
967/**
968 * @callback_method_impl{FNIOMIOPORTNEWOUT, QEMU firmware configuration write.}
969 */
970static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
971{
972 int rc = VINF_SUCCESS;
973 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
974 NOREF(pvUser);
975
976 LogFlowFunc(("offPort=%RTiop u32=%#x cb=%u\n", offPort, u32, cb));
977
978 switch (offPort)
979 {
980 case QEMU_FW_CFG_OFF_SELECTOR:
981 {
982 if (cb == 2)
983 qemuFwCfgItemSelect(pThis, (uint16_t)u32);
984 break;
985 }
986 case QEMU_FW_CFG_OFF_DATA: /* Readonly, ignore */
987 break;
988 case QEMU_FW_CFG_OFF_DMA_HIGH:
989 {
990 if (cb == 4)
991 pThis->GCPhysDma = ((RTGCPHYS)RT_BE2H_U32(u32)) << 32;
992 break;
993 }
994 case QEMU_FW_CFG_OFF_DMA_LOW:
995 {
996 if (cb == 4)
997 {
998 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U32(u32));
999 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
1000 pThis->GCPhysDma = 0;
1001 }
1002 break;
1003 }
1004 default:
1005 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", offPort, cb, u32);
1006 break;
1007 }
1008
1009 LogFlowFunc((" -> rc=%Rrc\n", rc));
1010 return rc;
1011}
1012
1013
1014/**
1015 * @callback_method_impl{FNIOMIOPORTNEWIN, QEMU firmware configuration read.}
1016 */
1017static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1018{
1019 int rc = VINF_SUCCESS;
1020 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1021 NOREF(pvUser);
1022
1023 *pu32 = 0;
1024
1025 LogFlowFunc(("offPort=%RTiop cb=%u\n", offPort, cb));
1026
1027 switch (offPort)
1028 {
1029 /* Selector (Writeonly, ignore). */
1030 case QEMU_FW_CFG_OFF_SELECTOR:
1031 break;
1032 case QEMU_FW_CFG_OFF_DATA:
1033 {
1034 if (cb == 1)
1035 {
1036 if ( pThis->cbCfgItemLeft
1037 && pThis->pCfgItem)
1038 {
1039 uint8_t bRead = 0;
1040 uint32_t cbRead = 0;
1041 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &bRead,
1042 sizeof(bRead), &cbRead);
1043 if ( RT_SUCCESS(rc2)
1044 && cbRead == sizeof(bRead))
1045 {
1046 pThis->offCfgItemNext += cbRead;
1047 pThis->cbCfgItemLeft -= cbRead;
1048 *pu32 = bRead;
1049 }
1050 }
1051 }
1052 else
1053 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
1054 break;
1055 }
1056
1057 default:
1058 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
1059 break;
1060 }
1061
1062 LogFlowFunc(("offPort=%RTiop cb=%u -> rc=%Rrc u32=%#x\n", offPort, cb, rc, *pu32));
1063
1064 return rc;
1065}
1066
1067
1068/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
1069
1070
1071/**
1072 * @callback_method_impl{FNIOMMMIONEWREAD}
1073 */
1074static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1075{
1076 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1077 RT_NOREF(pvUser);
1078
1079 LogFlowFunc(("%RGp cb=%u\n", off, cb));
1080
1081 AssertReturn(cb <= sizeof(uint64_t), VERR_INVALID_PARAMETER);
1082
1083 VBOXSTRICTRC rc = VINF_SUCCESS;
1084 switch (off)
1085 {
1086 case QEU_FW_CFG_MMIO_OFF_DATA:
1087 {
1088 if ( pThis->cbCfgItemLeft
1089 && pThis->pCfgItem)
1090 {
1091 uint32_t cbRead = 0;
1092 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, pv,
1093 cb, &cbRead);
1094 if (RT_SUCCESS(rc2))
1095 {
1096 pThis->offCfgItemNext += cbRead;
1097 pThis->cbCfgItemLeft -= cbRead;
1098 }
1099 }
1100 else
1101 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1102 break;
1103 }
1104 case QEU_FW_CFG_MMIO_OFF_DMA:
1105 if ( cb == sizeof(uint64_t)
1106 && pThis->fDmaEnabled)
1107 *(uint64_t *)pv = RT_H2BE_U64(QEMU_FW_CFG_DMA_ADDR_SIGNATURE);
1108 else
1109 rc = VINF_IOM_MMIO_UNUSED_00;
1110 break;
1111 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
1112 /* Writeonly, ignore. */
1113 rc = VINF_IOM_MMIO_UNUSED_00;
1114 break;
1115 default:
1116 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1117 break;
1118 }
1119
1120 return rc;
1121}
1122
1123
1124/**
1125 * @callback_method_impl{FNIOMMMIONEWWRITE}
1126 */
1127static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1128{
1129 int rc = VINF_SUCCESS;
1130 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1131 RT_NOREF(pvUser);
1132
1133 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
1134
1135 switch (off)
1136 {
1137 case QEU_FW_CFG_MMIO_OFF_DATA: /* Readonly, ignore */
1138 break;
1139 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
1140 {
1141 if (cb == sizeof(uint16_t))
1142 qemuFwCfgItemSelect(pThis, RT_BE2H_U16(*(uint16_t *)pv));
1143 else
1144 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1145 break;
1146 }
1147 case QEU_FW_CFG_MMIO_OFF_DMA:
1148 {
1149 if (cb == sizeof(uint64_t))
1150 {
1151 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U64(*(uint64_t *)pv));
1152 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
1153 pThis->GCPhysDma = 0;
1154 }
1155 else
1156 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1157 break;
1158 }
1159 default:
1160 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
1161 break;
1162 }
1163
1164 LogFlowFunc((" -> rc=%Rrc\n", rc));
1165 return rc;
1166}
1167
1168
1169/**
1170 * Sets up the initrd memory file and CPIO filesystem stream for writing.
1171 *
1172 * @returns VBox status code.
1173 * @param pThis The QEMU fw config device instance.
1174 * @param phVfsFss Where to return the filesystem stream handle.
1175 */
1176static int qemuFwCfgCreateOutputArchive(PDEVQEMUFWCFG pThis, PRTVFSFSSTREAM phVfsFss)
1177{
1178 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &pThis->hVfsFileInitrd);
1179 if (RT_SUCCESS(rc))
1180 {
1181 RTVFSFSSTREAM hVfsFss;
1182 RTVFSIOSTREAM hVfsIosOut = RTVfsFileToIoStream(pThis->hVfsFileInitrd);
1183 rc = RTZipTarFsStreamToIoStream(hVfsIosOut, RTZIPTARFORMAT_CPIO_ASCII_NEW, 0 /*fFlags*/, &hVfsFss);
1184 if (RT_SUCCESS(rc))
1185 {
1186 rc = RTZipTarFsStreamSetOwner(hVfsFss, 0, "root");
1187 if (RT_SUCCESS(rc))
1188 rc = RTZipTarFsStreamSetGroup(hVfsFss, 0, "root");
1189
1190 if (RT_SUCCESS(rc))
1191 *phVfsFss = hVfsFss;
1192 else
1193 {
1194 RTVfsFsStrmRelease(hVfsFss);
1195 *phVfsFss = NIL_RTVFSFSSTREAM;
1196 }
1197 }
1198 RTVfsIoStrmRelease(hVfsIosOut);
1199 }
1200
1201 return rc;
1202}
1203
1204
1205/**
1206 * Archives a file.
1207 *
1208 * @returns VBox status code.
1209 * @param hVfsFss The TAR filesystem stream handle.
1210 * @param pszSrc The file path or VFS spec.
1211 * @param pszDst The name to archive the file under.
1212 * @param pErrInfo Error info buffer (saves stack space).
1213 */
1214static int qemuFwCfgInitrdArchiveFile(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
1215 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
1216{
1217 /* Open the file. */
1218 uint32_t offError;
1219 RTVFSIOSTREAM hVfsIosSrc;
1220 int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1221 &hVfsIosSrc, &offError, RTErrInfoInitStatic(pErrInfo));
1222 if (RT_FAILURE(rc))
1223 return rc;
1224
1225 /* I/O stream to base object. */
1226 RTVFSOBJ hVfsObjSrc = RTVfsObjFromIoStream(hVfsIosSrc);
1227 if (hVfsObjSrc != NIL_RTVFSOBJ)
1228 {
1229 /*
1230 * Add it to the stream.
1231 */
1232 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1233 RTVfsIoStrmRelease(hVfsIosSrc);
1234 RTVfsObjRelease(hVfsObjSrc);
1235 return rc;
1236 }
1237 RTVfsIoStrmRelease(hVfsIosSrc);
1238 return VERR_INVALID_POINTER;
1239}
1240
1241
1242/**
1243 * Archives a symlink.
1244 *
1245 * @returns VBox status code.
1246 * @param hVfsFss The TAR filesystem stream handle.
1247 * @param pszSrc The file path or VFS spec.
1248 * @param pszDst The name to archive the file under.
1249 * @param pErrInfo Error info buffer (saves stack space).
1250 */
1251static int qemuFwCfgInitrdArchiveSymlink(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
1252 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
1253{
1254 /* Open the file. */
1255 uint32_t offError;
1256 RTVFSOBJ hVfsObjSrc;
1257 int rc = RTVfsChainOpenObj(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1258 RTVFSOBJ_F_OPEN_SYMLINK | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
1259 &hVfsObjSrc, &offError, RTErrInfoInitStatic(pErrInfo));
1260 if (RT_FAILURE(rc))
1261 return rc;
1262
1263 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1264 RTVfsObjRelease(hVfsObjSrc);
1265 return rc;
1266}
1267
1268
1269/**
1270 * Sub-directory helper for creating archives.
1271 *
1272 * @returns VBox status code.
1273 * @param hVfsFss The TAR filesystem stream handle.
1274 * @param pszSrc The directory path or VFS spec. We append to the
1275 * buffer as we decend.
1276 * @param cchSrc The length of the input.
1277 * @param pszDst The name to archive it the under. We append to the
1278 * buffer as we decend.
1279 * @param cchDst The length of the input.
1280 * @param pDirEntry Directory entry to use for the directory to handle.
1281 * @param pErrInfo Error info buffer (saves stack space).
1282 */
1283static int qemuFwCfgInitrdArchiveDirSub(RTVFSFSSTREAM hVfsFss,
1284 char *pszSrc, size_t cchSrc,
1285 char pszDst[RTPATH_MAX], size_t cchDst, PRTDIRENTRYEX pDirEntry,
1286 PRTERRINFOSTATIC pErrInfo)
1287{
1288 uint32_t offError;
1289 RTVFSDIR hVfsIoDir;
1290 int rc = RTVfsChainOpenDir(pszSrc, 0 /*fFlags*/,
1291 &hVfsIoDir, &offError, RTErrInfoInitStatic(pErrInfo));
1292 if (RT_FAILURE(rc))
1293 return rc;
1294
1295 /* Make sure we've got some room in the path, to save us extra work further down. */
1296 if (cchSrc + 3 >= RTPATH_MAX)
1297 return VERR_FILENAME_TOO_LONG;
1298
1299 /* Ensure we've got a trailing slash (there is space for it see above). */
1300 if (!RTPATH_IS_SEP(pszSrc[cchSrc - 1]))
1301 {
1302 pszSrc[cchSrc++] = RTPATH_SLASH;
1303 pszSrc[cchSrc] = '\0';
1304 }
1305
1306 /* Ditto for destination. */
1307 if (cchDst + 3 >= RTPATH_MAX)
1308 return VERR_FILENAME_TOO_LONG;
1309
1310 /* Don't try adding the root directory. */
1311 if (*pszDst != '\0')
1312 {
1313 RTVFSOBJ hVfsObjSrc = RTVfsObjFromDir(hVfsIoDir);
1314 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1315 RTVfsObjRelease(hVfsObjSrc);
1316 if (RT_FAILURE(rc))
1317 return rc;
1318
1319 if (!RTPATH_IS_SEP(pszDst[cchDst - 1]))
1320 {
1321 pszDst[cchDst++] = RTPATH_SLASH;
1322 pszDst[cchDst] = '\0';
1323 }
1324 }
1325
1326 /*
1327 * Process the files and subdirs.
1328 */
1329 for (;;)
1330 {
1331 size_t cbDirEntry = QEMUFWCFG_DIRENTRY_BUF_SIZE;
1332 rc = RTVfsDirReadEx(hVfsIoDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1333 if (RT_FAILURE(rc))
1334 break;
1335
1336 /* Check length. */
1337 if (pDirEntry->cbName + cchSrc + 3 >= RTPATH_MAX)
1338 {
1339 rc = VERR_BUFFER_OVERFLOW;
1340 break;
1341 }
1342
1343 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1344 {
1345 case RTFS_TYPE_DIRECTORY:
1346 {
1347 if (RTDirEntryExIsStdDotLink(pDirEntry))
1348 continue;
1349
1350 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1351 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1352 rc = qemuFwCfgInitrdArchiveDirSub(hVfsFss, pszSrc, cchSrc + pDirEntry->cbName,
1353 pszDst, cchDst + pDirEntry->cbName, pDirEntry, pErrInfo);
1354 break;
1355 }
1356
1357 case RTFS_TYPE_FILE:
1358 {
1359 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1360 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1361 rc = qemuFwCfgInitrdArchiveFile(hVfsFss, pszSrc, pszDst, pErrInfo);
1362 break;
1363 }
1364
1365 case RTFS_TYPE_SYMLINK:
1366 {
1367 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1368 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1369 rc = qemuFwCfgInitrdArchiveSymlink(hVfsFss, pszSrc, pszDst, pErrInfo);
1370 break;
1371 }
1372
1373 default:
1374 {
1375 LogRel(("Warning: File system type %#x for '%s' not implemented yet, sorry! Skipping ...\n",
1376 pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK, pDirEntry->szName));
1377 break;
1378 }
1379 }
1380
1381 if (RT_FAILURE(rc))
1382 break;
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(fFailOnResize);
1642 return qemuFwCfgR3RamfbPortUpdateDisplay(pInterface);
1643}
1644
1645
1646/**
1647 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
1648 */
1649static DECLCALLBACK(int) qemuFwCfgR3RamfbPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
1650{
1651 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1652
1653 /*
1654 * Update the interval, then restart or stop the timer.
1655 */
1656 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
1657
1658 if (cMilliesInterval)
1659 return PDMDevHlpTimerSetMillies(pThis->pDevIns, pThis->hRamfbRefreshTimer, cMilliesInterval);
1660 return PDMDevHlpTimerStop(pThis->pDevIns, pThis->hRamfbRefreshTimer);
1661}
1662
1663
1664/**
1665 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
1666 */
1667static DECLCALLBACK(int) qemuFwCfgR3RamfbPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
1668{
1669 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
1670
1671 RT_NOREF(pInterface, pcx, pcy);
1672 AssertReleaseFailed();
1673 return VERR_NOT_IMPLEMENTED;
1674}
1675
1676
1677/**
1678 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
1679 */
1680static DECLCALLBACK(int) qemuFwCfgR3RamfbPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
1681 uint32_t *pcx, uint32_t *pcy)
1682{
1683 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1684
1685 LogFlowFunc(("\n"));
1686
1687 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1688 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1689
1690 int rc;
1691 size_t cbData = pThis->RamfbCfg.cHeight * pThis->RamfbCfg.cbStride;
1692 if ( pThis->RamfbCfg.GCPhysRamfbBase
1693 && cbData)
1694 {
1695 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbData);
1696 if (pbData)
1697 {
1698 rc = PDMDevHlpPhysReadUser(pThis->pDevIns, pThis->RamfbCfg.GCPhysRamfbBase, pbData, cbData);
1699 if (RT_SUCCESS(rc))
1700 {
1701 *ppbData = pbData;
1702 *pcbData = cbData;
1703 *pcx = pThis->RamfbCfg.cWidth;
1704 *pcy = pThis->RamfbCfg.cHeight;
1705 }
1706 }
1707 else
1708 rc = VERR_NO_MEMORY;
1709 }
1710 else
1711 rc = VERR_NOT_SUPPORTED;
1712
1713 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1714 return rc;
1715}
1716
1717
1718/**
1719 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
1720 */
1721static DECLCALLBACK(void) qemuFwCfgR3RamfbPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
1722{
1723 NOREF(pInterface);
1724 LogFlowFunc(("pbData=%p\n", pbData));
1725 RTMemFree(pbData);
1726}
1727
1728
1729/**
1730 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
1731 */
1732static DECLCALLBACK(int) qemuFwCfgR3RamfbPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
1733 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1734{
1735 RT_NOREF(pInterface, pvData, x, y, cx, cy);
1736 AssertReleaseFailed();
1737 return VERR_NOT_IMPLEMENTED;
1738}
1739
1740
1741/**
1742 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
1743 */
1744static DECLCALLBACK(void) qemuFwCfgR3RamfbPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y,
1745 uint32_t cx, uint32_t cy)
1746{
1747 RT_NOREF(pInterface, x, y, cx, cy);
1748 AssertReleaseFailed();
1749}
1750
1751
1752/**
1753 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
1754 */
1755static DECLCALLBACK(int)
1756qemuFwCfgR3RamfbPortCopyRect(PPDMIDISPLAYPORT pInterface,
1757 uint32_t cx, uint32_t cy,
1758 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
1759 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
1760 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
1761 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
1762{
1763 RT_NOREF(pInterface, cx, cy, pbSrc, xSrc, ySrc, cxSrc, cySrc, cbSrcLine, cSrcBitsPerPixel, pbDst, xDst, yDst, cxDst, cyDst,
1764 cbDstLine, cDstBitsPerPixel);
1765 AssertReleaseFailed();
1766 return VINF_SUCCESS;
1767}
1768
1769
1770/**
1771 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
1772 */
1773static DECLCALLBACK(void) qemuFwCfgR3RamfbPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
1774{
1775 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1776
1777 LogFlowFunc(("fRender = %d\n", fRender));
1778
1779 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1780 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1781
1782 pThis->fRenderVRam = fRender;
1783
1784 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1785}
1786
1787
1788/**
1789 * @interface_method_impl{PDMIDISPLAYPORT,pfnSendModeHint}
1790 */
1791static DECLCALLBACK(int) qemuFwCfgR3RamfbPortSendModeHint(PPDMIDISPLAYPORT pInterface, uint32_t cx, uint32_t cy, uint32_t cBPP,
1792 uint32_t iDisplay, uint32_t dx, uint32_t dy, uint32_t fEnabled, uint32_t fNotifyGuest)
1793{
1794 RT_NOREF(pInterface, cx, cy, cBPP, iDisplay, dx, dy, fEnabled, fNotifyGuest);
1795 return VINF_SUCCESS;
1796}
1797
1798
1799/**
1800 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
1801 */
1802static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
1803 bool fSupportsMoveCursor)
1804{
1805 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
1806}
1807
1808
1809/**
1810 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
1811 */
1812static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
1813{
1814 RT_NOREF(pInterface, x, y, fOutOfRange);
1815}
1816
1817
1818/**
1819 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
1820 */
1821static DECLCALLBACK(void) qemuFwCfgR3RamfbTimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1822{
1823 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1824 RT_NOREF(pvUser);
1825
1826 if (pThis->pDrvL0)
1827 pThis->pDrvL0->pfnRefresh(pThis->pDrvL0);
1828
1829 if (pThis->cMilliesRefreshInterval)
1830 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
1831}
1832
1833
1834/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
1835
1836/**
1837 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1838 */
1839static DECLCALLBACK(void *) qemuFwCfgR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1840{
1841 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IBase);
1842 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1843 if (pThis->fRamfbSupported)
1844 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPortRamfb);
1845 return NULL;
1846}
1847
1848
1849/**
1850 * @interface_method_impl{PDMDEVREG,pfnReset}
1851 */
1852static DECLCALLBACK(void) qemuFwCfgReset(PPDMDEVINS pDevIns)
1853{
1854 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1855
1856 qemuFwCfgR3ItemReset(pThis);
1857 pThis->GCPhysDma = 0;
1858
1859 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1860 RTVfsFileRelease(pThis->hVfsFileInitrd);
1861 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1862
1863 qemuFwCfgInitrdMaybeCreate(pThis); /* Ignores status code. */
1864}
1865
1866
1867/**
1868 * @interface_method_impl{PDMDEVREG,pfnAttach}
1869 *
1870 * This is like plugging in the monitor after turning on the PC.
1871 */
1872static DECLCALLBACK(int) qemuFwCfgR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1873{
1874 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1875
1876 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1877 ("QEMU RAM framebuffer device does not support hotplugging\n"),
1878 VERR_INVALID_PARAMETER);
1879
1880 switch (iLUN)
1881 {
1882 /* LUN #0: Display port. */
1883 case 0:
1884 {
1885 AssertLogRelMsgReturn(pThis->fRamfbSupported,
1886 ("QemuFwCfg: Trying to attach a display without the RAM framebuffer support being enabled!\n"),
1887 VERR_NOT_SUPPORTED);
1888
1889 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBaseL0, "Display Port");
1890 if (RT_SUCCESS(rc))
1891 {
1892 pThis->pDrvL0 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBaseL0, PDMIDISPLAYCONNECTOR);
1893 if (pThis->pDrvL0)
1894 {
1895 /* pThis->pDrvL0->pbData can be NULL when there is no framebuffer. */
1896 if ( pThis->pDrvL0->pfnRefresh
1897 && pThis->pDrvL0->pfnResize
1898 && pThis->pDrvL0->pfnUpdateRect)
1899 rc = VINF_SUCCESS;
1900 else
1901 {
1902 Assert(pThis->pDrvL0->pfnRefresh);
1903 Assert(pThis->pDrvL0->pfnResize);
1904 Assert(pThis->pDrvL0->pfnUpdateRect);
1905 pThis->pDrvL0 = NULL;
1906 pThis->pDrvBaseL0 = NULL;
1907 rc = VERR_INTERNAL_ERROR;
1908 }
1909 }
1910 else
1911 {
1912 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
1913 pThis->pDrvBaseL0 = NULL;
1914 rc = VERR_PDM_MISSING_INTERFACE;
1915 }
1916 }
1917 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1918 {
1919 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1920 rc = VINF_SUCCESS;
1921 }
1922 else
1923 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
1924 return rc;
1925 }
1926
1927 default:
1928 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1929 return VERR_PDM_NO_SUCH_LUN;
1930 }
1931}
1932
1933
1934/**
1935 * @interface_method_impl{PDMDEVREG,pfnDetach}
1936 *
1937 * This is like unplugging the monitor while the PC is still running.
1938 */
1939static DECLCALLBACK(void) qemuFwCfgR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1940{
1941 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1942 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("QEMU RAM framebuffer device does not support hotplugging\n"));
1943 RT_NOREF(fFlags);
1944
1945 /*
1946 * Reset the interfaces and update the controller state.
1947 */
1948 switch (iLUN)
1949 {
1950 /* LUN #0: Display port. */
1951 case 0:
1952 AssertLogRelMsg(pThis->fRamfbSupported,
1953 ("QemuFwCfg: Trying to detach a display without the RAM framebuffer support being enabled!\n"));
1954
1955 pThis->pDrvL0 = NULL;
1956 pThis->pDrvBaseL0 = NULL;
1957 break;
1958
1959 default:
1960 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1961 break;
1962 }
1963}
1964
1965
1966/**
1967 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1968 */
1969static DECLCALLBACK(int) qemuFwCfgDestruct(PPDMDEVINS pDevIns)
1970{
1971 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1972 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1973
1974 qemuFwCfgR3ItemReset(pThis);
1975 pThis->GCPhysDma = 0;
1976
1977 if (pThis->paCfgFiles)
1978 {
1979 Assert(pThis->cCfgFiles && pThis->cCfgFilesMax);
1980 RTMemFree(pThis->paCfgFiles);
1981 pThis->paCfgFiles = NULL;
1982 pThis->cCfgFiles = 0;
1983 pThis->cCfgFilesMax = 0;
1984 }
1985 else
1986 Assert(!pThis->cCfgFiles && !pThis->cCfgFilesMax);
1987
1988 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1989 RTVfsFileRelease(pThis->hVfsFileInitrd);
1990 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1991
1992 return VINF_SUCCESS;
1993}
1994
1995
1996/**
1997 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1998 */
1999static DECLCALLBACK(int) qemuFwCfgConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2000{
2001 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2002 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
2003 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2004 Assert(iInstance == 0); RT_NOREF(iInstance);
2005
2006 /*
2007 * Validate configuration.
2008 */
2009 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DmaEnabled"
2010 "|MmioBase"
2011 "|MmioSize"
2012 "|KernelImage"
2013 "|InitrdImage"
2014 "|SetupImage"
2015 "|CmdLine"
2016 "|QemuRamfbSupport",
2017 "");
2018
2019 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DmaEnabled", &pThis->fDmaEnabled, false);
2020 if (RT_FAILURE(rc))
2021 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmaEnabled\""));
2022
2023 /*
2024 * Init the data.
2025 */
2026 pThis->pDevIns = pDevIns;
2027 pThis->pCfg = pCfg;
2028 pThis->u32Version = QEMU_FW_CFG_VERSION_LEGACY | (pThis->fDmaEnabled ? QEMU_FW_CFG_VERSION_DMA : 0);
2029 pThis->GCPhysDma = 0;
2030 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
2031 pThis->paCfgFiles = NULL;
2032 pThis->cCfgFiles = 0;
2033 pThis->cCfgFilesMax = 0;
2034
2035 pThis->IBase.pfnQueryInterface = qemuFwCfgR3PortQueryInterface;
2036
2037 pThis->IPortRamfb.pfnUpdateDisplay = qemuFwCfgR3RamfbPortUpdateDisplay;
2038 pThis->IPortRamfb.pfnUpdateDisplayAll = qemuFwCfgR3RamfbPortUpdateDisplayAll;
2039 pThis->IPortRamfb.pfnQueryVideoMode = qemuFwCfgR3RamfbPortQueryVideoMode;
2040 pThis->IPortRamfb.pfnSetRefreshRate = qemuFwCfgR3RamfbPortSetRefreshRate;
2041 pThis->IPortRamfb.pfnTakeScreenshot = qemuFwCfgR3RamfbPortTakeScreenshot;
2042 pThis->IPortRamfb.pfnFreeScreenshot = qemuFwCfgR3RamfbPortFreeScreenshot;
2043 pThis->IPortRamfb.pfnDisplayBlt = qemuFwCfgR3RamfbPortDisplayBlt;
2044 pThis->IPortRamfb.pfnUpdateDisplayRect = qemuFwCfgR3RamfbPortUpdateDisplayRect;
2045 pThis->IPortRamfb.pfnCopyRect = qemuFwCfgR3RamfbPortCopyRect;
2046 pThis->IPortRamfb.pfnSetRenderVRAM = qemuFwCfgR3RamfbPortSetRenderVRAM;
2047 pThis->IPortRamfb.pfnSetViewport = NULL;
2048 pThis->IPortRamfb.pfnReportMonitorPositions = NULL;
2049 pThis->IPortRamfb.pfnSendModeHint = qemuFwCfgR3RamfbPortSendModeHint;
2050 pThis->IPortRamfb.pfnReportHostCursorCapabilities = qemuFwCfgR3RamfbPortReportHostCursorCapabilities;
2051 pThis->IPortRamfb.pfnReportHostCursorPosition = qemuFwCfgR3RamfbPortReportHostCursorPosition;
2052
2053 RTGCPHYS GCPhysMmioBase = 0;
2054 rc = pHlp->pfnCFGMQueryU64(pCfg, "MmioBase", &GCPhysMmioBase);
2055 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2056 return PDMDEV_SET_ERROR(pDevIns, rc,
2057 N_("Configuration error: Failed to get the \"MmioBase\" value"));
2058 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2059 {
2060 /*
2061 * Register standard I/O Ports
2062 */
2063 IOMIOPORTHANDLE hIoPorts;
2064 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, QEMU_FW_CFG_IO_PORT_START, QEMU_FW_CFG_IO_PORT_SIZE, 0 /*fFlags*/,
2065 qemuFwCfgIoPortWrite, qemuFwCfgIoPortRead,
2066 "QEMU firmware configuration", NULL /*paExtDescs*/, &hIoPorts);
2067 AssertRCReturn(rc, rc);
2068 }
2069 else
2070 {
2071 uint32_t cbMmio = 0;
2072 rc = pHlp->pfnCFGMQueryU32(pCfg, "MmioSize", &cbMmio);
2073 if (RT_FAILURE(rc))
2074 return PDMDEV_SET_ERROR(pDevIns, rc,
2075 N_("Configuration error: Failed to get the \"MmioSize\" value"));
2076
2077 /*
2078 * Register and map the MMIO region.
2079 */
2080 IOMMMIOHANDLE hMmio;
2081 rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysMmioBase, cbMmio, qemuFwCfgMmioWrite, qemuFwCfgMmioRead,
2082 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, "QemuFwCfg", &hMmio);
2083 AssertRCReturn(rc, rc);
2084 }
2085
2086 qemuFwCfgR3ItemReset(pThis);
2087
2088 /* Setup the RAM based framebuffer support if configured. */
2089 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "QemuRamfbSupport", &pThis->fRamfbSupported, false);
2090 if (RT_FAILURE(rc))
2091 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"QemuRamfbSupport\""));
2092
2093 if (pThis->fRamfbSupported)
2094 {
2095 LogRel(("QemuFwCfg: RAM based framebuffer support enabled\n"));
2096 if (!pThis->fDmaEnabled)
2097 return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER, N_("Configuration error: Enabling \"QemuRamfbSupport\" requires \"DmaEnabled\""));
2098
2099 /* Critical section for synchronizing access. */
2100 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectRamfb, RT_SRC_POS, "Ramfb#%u", iInstance);
2101 AssertRCReturn(rc, rc);
2102
2103 /*
2104 * Create the refresh timer.
2105 */
2106 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, qemuFwCfgR3RamfbTimerRefresh, NULL,
2107 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Ramfb Refresh", &pThis->hRamfbRefreshTimer);
2108 AssertRCReturn(rc, rc);
2109
2110 /* Register a config file item and attach the driver below us. */
2111 rc = qemuFwCfgR3Attach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
2112 AssertRCReturn(rc, rc);
2113
2114 rc = qemuFwCfgR3FileRegister(pThis, "etc/ramfb", sizeof(pThis->RamfbCfg),
2115 NULL /* pfnSetup */, NULL /*pfnRead*/,
2116 qemuFwCfgR3RamfbCfgWrite, NULL /*pfnCleanup*/);
2117 AssertRCReturn(rc, rc);
2118 }
2119
2120 rc = qemuFwCfgInitrdMaybeCreate(pThis);
2121 if (RT_FAILURE(rc))
2122 return rc; /* Error set. */
2123
2124 return VINF_SUCCESS;
2125}
2126
2127
2128/**
2129 * The device registration structure.
2130 */
2131const PDMDEVREG g_DeviceQemuFwCfg =
2132{
2133 /* .u32Version = */ PDM_DEVREG_VERSION,
2134 /* .uReserved0 = */ 0,
2135 /* .szName = */ "qemu-fw-cfg",
2136 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2137 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
2138 /* .cMaxInstances = */ 1,
2139 /* .uSharedVersion = */ 42,
2140 /* .cbInstanceShared = */ sizeof(DEVQEMUFWCFG),
2141 /* .cbInstanceCC = */ 0,
2142 /* .cbInstanceRC = */ 0,
2143 /* .cMaxPciDevices = */ 0,
2144 /* .cMaxMsixVectors = */ 0,
2145 /* .pszDescription = */ "QEMU Firmware Config compatible device",
2146#if defined(IN_RING3)
2147 /* .pszRCMod = */ "",
2148 /* .pszR0Mod = */ "",
2149 /* .pfnConstruct = */ qemuFwCfgConstruct,
2150 /* .pfnDestruct = */ qemuFwCfgDestruct,
2151 /* .pfnRelocate = */ NULL,
2152 /* .pfnMemSetup = */ NULL,
2153 /* .pfnPowerOn = */ NULL,
2154 /* .pfnReset = */ qemuFwCfgReset,
2155 /* .pfnSuspend = */ NULL,
2156 /* .pfnResume = */ NULL,
2157 /* .pfnAttach = */ qemuFwCfgR3Attach,
2158 /* .pfnDetach = */ qemuFwCfgR3Detach,
2159 /* .pfnQueryInterface = */ NULL,
2160 /* .pfnInitComplete = */ NULL,
2161 /* .pfnPowerOff = */ NULL,
2162 /* .pfnSoftReset = */ NULL,
2163 /* .pfnReserved0 = */ NULL,
2164 /* .pfnReserved1 = */ NULL,
2165 /* .pfnReserved2 = */ NULL,
2166 /* .pfnReserved3 = */ NULL,
2167 /* .pfnReserved4 = */ NULL,
2168 /* .pfnReserved5 = */ NULL,
2169 /* .pfnReserved6 = */ NULL,
2170 /* .pfnReserved7 = */ NULL,
2171#elif defined(IN_RING0)
2172 /* .pfnEarlyConstruct = */ NULL,
2173 /* .pfnConstruct = */ NULL,
2174 /* .pfnDestruct = */ NULL,
2175 /* .pfnFinalDestruct = */ NULL,
2176 /* .pfnRequest = */ NULL,
2177 /* .pfnReserved0 = */ NULL,
2178 /* .pfnReserved1 = */ NULL,
2179 /* .pfnReserved2 = */ NULL,
2180 /* .pfnReserved3 = */ NULL,
2181 /* .pfnReserved4 = */ NULL,
2182 /* .pfnReserved5 = */ NULL,
2183 /* .pfnReserved6 = */ NULL,
2184 /* .pfnReserved7 = */ NULL,
2185#elif defined(IN_RC)
2186 /* .pfnConstruct = */ NULL,
2187 /* .pfnReserved0 = */ NULL,
2188 /* .pfnReserved1 = */ NULL,
2189 /* .pfnReserved2 = */ NULL,
2190 /* .pfnReserved3 = */ NULL,
2191 /* .pfnReserved4 = */ NULL,
2192 /* .pfnReserved5 = */ NULL,
2193 /* .pfnReserved6 = */ NULL,
2194 /* .pfnReserved7 = */ NULL,
2195#else
2196# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2197#endif
2198 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2199};
2200
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