VirtualBox

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

最後變更 在這個檔案是 108401,由 vboxsync 提交於 3 週 前

Devices/PC/DevQemuFwCfg.cpp: Add saved state handling for the RAM based framebuffer, bugref:10392

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

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