VirtualBox

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

Last change on this file since 105381 was 103151, checked in by vboxsync, 10 months ago

Devices/DevQemuFwCfg.cpp: Some warning fixes about externally visible functions which should be static, 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 103151 2024-01-31 15:42:35Z vboxsync $ */
2/** @file
3 * DevQemuFwCfg - QEMU firmware configuration compatible device.
4 */
5
6/*
7 * Copyright (C) 2020-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_qemufwcfg The QEMU firmware configuration Device.
29 *
30 * The QEMU firmware configuration device is a custom device emulation
31 * to convey information about the VM to the guests firmware (UEFI for example).
32 * In the case of VirtualBox it is used to directly load a compatible kernel
33 * and initrd image like Linux from the host into the guest and boot it. This allows
34 * efficiently testing/debugging of multiple Linux kernels without having to install
35 * a guest OS. On VirtualBox the EFI firmware supports this interface, the BIOS is
36 * currently unsupported (and probably never will be).
37 *
38 * @section sec_qemufwcfg_config Configuration
39 *
40 * To use this interface for a particular VM the following extra data needs to be
41 * set besides enabling the EFI firmware:
42 *
43 * VBoxManage setextradata &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
1382 if (rc == VERR_NO_MORE_FILES)
1383 rc = VINF_SUCCESS;
1384
1385 RTVfsDirRelease(hVfsIoDir);
1386 return rc;
1387}
1388
1389
1390/**
1391 * Archives a directory recursively.
1392 *
1393 * @returns VBox status code.
1394 * @param hVfsFss The CPIO filesystem stream handle.
1395 * @param pszSrc The directory path or VFS spec. We append to the
1396 * buffer as we decend.
1397 * @param cchSrc The length of the input.
1398 * @param pszDst The name to archive it the under. We append to the
1399 * buffer as we decend.
1400 * @param cchDst The length of the input.
1401 * @param pErrInfo Error info buffer (saves stack space).
1402 */
1403static int qemuFwCfgInitrdArchiveDir(RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
1404 char pszDst[RTPATH_MAX], size_t cchDst,
1405 PRTERRINFOSTATIC pErrInfo)
1406{
1407 RT_NOREF(cchSrc);
1408
1409 char szSrcAbs[RTPATH_MAX];
1410 int rc = RTPathAbs(pszSrc, szSrcAbs, sizeof(szSrcAbs));
1411 if (RT_FAILURE(rc))
1412 return rc;
1413
1414 union
1415 {
1416 uint8_t abPadding[QEMUFWCFG_DIRENTRY_BUF_SIZE];
1417 RTDIRENTRYEX DirEntry;
1418 } uBuf;
1419
1420 return qemuFwCfgInitrdArchiveDirSub(hVfsFss, szSrcAbs, strlen(szSrcAbs), pszDst, cchDst, &uBuf.DirEntry, pErrInfo);
1421}
1422
1423
1424/**
1425 * Creates an on the fly initramfs for a given root directory.
1426 *
1427 * @returns VBox status code.
1428 * @param pThis The QEMU fw config device instance.
1429 * @param pszPath The path to work on.
1430 */
1431static int qemuFwCfgInitrdCreate(PDEVQEMUFWCFG pThis, const char *pszPath)
1432{
1433 /*
1434 * First open the output file.
1435 */
1436 RTVFSFSSTREAM hVfsFss;
1437 int rc = qemuFwCfgCreateOutputArchive(pThis, &hVfsFss);
1438 if (RT_FAILURE(rc))
1439 return rc;
1440
1441 /*
1442 * Construct/copy the source name.
1443 */
1444 char szSrc[RTPATH_MAX];
1445 rc = RTStrCopy(szSrc, sizeof(szSrc), pszPath);
1446 if (RT_SUCCESS(rc))
1447 {
1448 RTERRINFOSTATIC ErrInfo;
1449 char szDst[RTPATH_MAX]; RT_ZERO(szDst);
1450 rc = qemuFwCfgInitrdArchiveDir(hVfsFss, szSrc, strlen(szSrc),
1451 szDst, strlen(szDst), &ErrInfo);
1452 }
1453
1454 /*
1455 * Finalize the archive.
1456 */
1457 int rc2 = RTVfsFsStrmEnd(hVfsFss); AssertRC(rc2);
1458 RTVfsFsStrmRelease(hVfsFss);
1459 return rc;
1460}
1461
1462
1463/**
1464 * Checks whether creation of the initrd should be done on the fly.
1465 *
1466 * @returns VBox status code.
1467 * @param pThis The QEMU fw config device instance.
1468 */
1469static int qemuFwCfgInitrdMaybeCreate(PDEVQEMUFWCFG pThis)
1470{
1471 PPDMDEVINS pDevIns = pThis->pDevIns;
1472 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1473
1474 /* Query the path from the CFGM key. */
1475 char *pszFilePath = NULL;
1476 int rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, "InitrdImage", &pszFilePath);
1477 if (RT_SUCCESS(rc))
1478 {
1479 if (RTDirExists(pszFilePath))
1480 {
1481 rc = qemuFwCfgInitrdCreate(pThis, pszFilePath);
1482 if (RT_FAILURE(rc))
1483 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1484 N_("QemuFwCfg: failed to create the in memory initram filesystem"));
1485 }
1486 /*else: Probably a normal file. */
1487 PDMDevHlpMMHeapFree(pDevIns, pszFilePath);
1488 }
1489 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
1490 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1491 N_("Configuration error: Querying \"InitrdImage\" as a string failed"));
1492 else
1493 rc = VINF_SUCCESS;
1494
1495 return rc;
1496}
1497
1498
1499/**
1500 * Registers a file item with the given name for consumption by the firmware.
1501 *
1502 * @returns VBox status code.
1503 * @param pThis The QEMU fw config device instance.
1504 * @param pszFilename The filename to use.
1505 * @param cbData Size of the file in bytes.
1506 * @param pfnSetup Setup callback - optional.
1507 * @param pfnRead Read callback - optional.
1508 * @param pfnWrite Write callback - optional.
1509 * @param pfnCleanup Cleanup callback when the item gets de-selected - optional.
1510 */
1511static int qemuFwCfgR3FileRegister(PDEVQEMUFWCFG pThis, const char *pszFilename, uint32_t cbData,
1512 PFNQEMUFWCFGITEMSETUP pfnSetup, PFNQEMUFWCFGITEMREAD pfnRead,
1513 PFNQEMUFWCFGITEMWRITE pfnWrite, PFNQEMUFWCFGITEMCLEANUP pfnCleanup)
1514{
1515 AssertReturn(strlen(pszFilename) <= QEMU_FW_CFG_ITEM_FILE_NAME_MAX, VERR_FILENAME_TOO_LONG);
1516
1517 PQEMUFWCFGFILEENTRY pEntry = NULL;
1518 if (pThis->cCfgFiles == pThis->cCfgFilesMax)
1519 {
1520 /* Grow the array. */
1521 PQEMUFWCFGFILEENTRY paCfgFilesNew = (PQEMUFWCFGFILEENTRY)RTMemRealloc(pThis->paCfgFiles,
1522 (pThis->cCfgFilesMax + 10) * sizeof(*pThis->paCfgFiles));
1523 if (!paCfgFilesNew)
1524 return VERR_NO_MEMORY;
1525
1526 pThis->paCfgFiles = paCfgFilesNew;
1527 pThis->cCfgFilesMax += 10;
1528 }
1529
1530 pEntry = &pThis->paCfgFiles[pThis->cCfgFiles];
1531 pThis->cCfgFiles++;
1532
1533 pEntry->cbFile = cbData;
1534 strncpy(&pEntry->szFilename[0], pszFilename, sizeof(pEntry->szFilename));
1535 pEntry->Cfg.uCfgItem = QEMU_FW_CFG_ITEM_FILE_USER_FIRST + pThis->cCfgFiles - 1;
1536 pEntry->Cfg.pszItem = &pEntry->szFilename[0];
1537 pEntry->Cfg.pszCfgmKey = NULL;
1538 pEntry->Cfg.pfnSetup = pfnSetup ? pfnSetup : qemuFwCfgR3SetupFileGeneric;
1539 pEntry->Cfg.pfnRead = pfnRead;
1540 pEntry->Cfg.pfnWrite = pfnWrite;
1541 pEntry->Cfg.pfnCleanup = pfnCleanup;
1542 return VINF_SUCCESS;
1543}
1544
1545
1546/**
1547 * RAM framebuffer config write callback.
1548 *
1549 * @param pThis The QEMU fw config device instance.
1550 * @param pItem Pointer to the selected item.
1551 * @param off Where to start writing to.
1552 * @param pvBuf The data to write.
1553 * @param cbToWrite How much to write.
1554 * @param pcbWritten Where to store the amount of bytes written.
1555 */
1556static DECLCALLBACK(int) qemuFwCfgR3RamfbCfgWrite(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, const void *pvBuf,
1557 uint32_t cbToWrite, uint32_t *pcbWritten)
1558{
1559 RT_NOREF(pItem);
1560
1561 AssertReturn(!off && cbToWrite == sizeof(QEMURAMFBCONFIG), VERR_NOT_SUPPORTED);
1562 *pcbWritten = cbToWrite;
1563
1564 PCQEMURAMFBCONFIG pRamfbCfg = (PCQEMURAMFBCONFIG)pvBuf;
1565 if ( RT_BE2H_U32(pRamfbCfg->u32FourCC) != QEMU_RAMFB_CFG_FORMAT
1566 || RT_BE2H_U32(pRamfbCfg->u32Flags) != 0)
1567 return VERR_NOT_SUPPORTED;
1568
1569 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1570 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1571
1572 pThis->RamfbCfg.GCPhysRamfbBase = RT_BE2H_U64(pRamfbCfg->GCPhysRamfbBase);
1573 pThis->RamfbCfg.cbStride = RT_BE2H_U32(pRamfbCfg->cbStride);
1574 pThis->RamfbCfg.cWidth = RT_BE2H_U32(pRamfbCfg->cWidth);
1575 pThis->RamfbCfg.cHeight = RT_BE2H_U32(pRamfbCfg->cHeight);
1576 pThis->RamfbCfg.u32FourCC = RT_BE2H_U32(pRamfbCfg->u32FourCC);
1577 pThis->RamfbCfg.u32Flags = RT_BE2H_U32(pRamfbCfg->u32Flags);
1578
1579 if (pThis->pDrvL0)
1580 {
1581 int rc = pThis->pDrvL0->pfnResize(pThis->pDrvL0, QEMU_RAMFB_CFG_BPP * 8, NULL /*pvVRAM*/,
1582 pThis->RamfbCfg.cbStride,
1583 pThis->RamfbCfg.cWidth,
1584 pThis->RamfbCfg.cHeight);
1585 AssertRC(rc);
1586 }
1587
1588 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1589
1590 return VINF_SUCCESS;
1591}
1592
1593
1594/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
1595
1596/**
1597 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
1598 */
1599static DECLCALLBACK(int) qemuFwCfgR3RamfbPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
1600{
1601 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1602
1603 LogFlowFunc(("\n"));
1604
1605 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1606 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1607
1608 if ( pThis->fRenderVRam
1609 && pThis->RamfbCfg.GCPhysRamfbBase)
1610 {
1611 if ( pThis->RamfbCfg.cWidth == pThis->pDrvL0->cx
1612 && pThis->RamfbCfg.cHeight == pThis->pDrvL0->cy
1613 && pThis->RamfbCfg.cbStride == pThis->pDrvL0->cbScanline
1614 && pThis->pDrvL0->pbData)
1615 {
1616 PDMDevHlpPhysReadUser(pThis->pDevIns, pThis->RamfbCfg.GCPhysRamfbBase, pThis->pDrvL0->pbData, pThis->RamfbCfg.cbStride * pThis->RamfbCfg.cHeight);
1617 AssertPtr(pThis->pDrvL0);
1618 pThis->pDrvL0->pfnUpdateRect(pThis->pDrvL0, 0, 0, pThis->RamfbCfg.cWidth, pThis->RamfbCfg.cHeight);
1619 }
1620 else
1621 LogFlowFunc(("Framebuffer dimension mismatch ({%u, %u, %u} vs {%u, %u, %u})\n",
1622 pThis->RamfbCfg.cWidth, pThis->RamfbCfg.cHeight, pThis->RamfbCfg.cbStride,
1623 pThis->pDrvL0->cx, pThis->pDrvL0->cy, pThis->pDrvL0->cbScanline));
1624 }
1625 else
1626 LogFlowFunc(("Rendering disabled or no RAM framebuffer set up\n"));
1627
1628 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1629 return VINF_SUCCESS;
1630}
1631
1632
1633/**
1634 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
1635 */
1636static DECLCALLBACK(int) qemuFwCfgR3RamfbPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
1637{
1638 RT_NOREF(fFailOnResize);
1639 return qemuFwCfgR3RamfbPortUpdateDisplay(pInterface);
1640}
1641
1642
1643/**
1644 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
1645 */
1646static DECLCALLBACK(int) qemuFwCfgR3RamfbPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
1647{
1648 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1649
1650 /*
1651 * Update the interval, then restart or stop the timer.
1652 */
1653 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
1654
1655 if (cMilliesInterval)
1656 return PDMDevHlpTimerSetMillies(pThis->pDevIns, pThis->hRamfbRefreshTimer, cMilliesInterval);
1657 return PDMDevHlpTimerStop(pThis->pDevIns, pThis->hRamfbRefreshTimer);
1658}
1659
1660
1661/**
1662 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
1663 */
1664static DECLCALLBACK(int) qemuFwCfgR3RamfbPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
1665{
1666 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
1667
1668 RT_NOREF(pInterface, pcx, pcy);
1669 AssertReleaseFailed();
1670 return VERR_NOT_IMPLEMENTED;
1671}
1672
1673
1674/**
1675 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
1676 */
1677static DECLCALLBACK(int) qemuFwCfgR3RamfbPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
1678 uint32_t *pcx, uint32_t *pcy)
1679{
1680 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1681
1682 LogFlowFunc(("\n"));
1683
1684 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1685 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1686
1687 int rc;
1688 size_t cbData = pThis->RamfbCfg.cHeight * pThis->RamfbCfg.cbStride;
1689 if ( pThis->RamfbCfg.GCPhysRamfbBase
1690 && cbData)
1691 {
1692 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbData);
1693 if (pbData)
1694 {
1695 rc = PDMDevHlpPhysReadUser(pThis->pDevIns, pThis->RamfbCfg.GCPhysRamfbBase, pbData, cbData);
1696 if (RT_SUCCESS(rc))
1697 {
1698 *ppbData = pbData;
1699 *pcbData = cbData;
1700 *pcx = pThis->RamfbCfg.cWidth;
1701 *pcy = pThis->RamfbCfg.cHeight;
1702 }
1703 }
1704 else
1705 rc = VERR_NO_MEMORY;
1706 }
1707 else
1708 rc = VERR_NOT_SUPPORTED;
1709
1710 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1711 return rc;
1712}
1713
1714
1715/**
1716 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
1717 */
1718static DECLCALLBACK(void) qemuFwCfgR3RamfbPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
1719{
1720 NOREF(pInterface);
1721 LogFlowFunc(("pbData=%p\n", pbData));
1722 RTMemFree(pbData);
1723}
1724
1725
1726/**
1727 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
1728 */
1729static DECLCALLBACK(int) qemuFwCfgR3RamfbPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
1730 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1731{
1732 RT_NOREF(pInterface, pvData, x, y, cx, cy);
1733 AssertReleaseFailed();
1734 return VERR_NOT_IMPLEMENTED;
1735}
1736
1737
1738/**
1739 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
1740 */
1741static DECLCALLBACK(void) qemuFwCfgR3RamfbPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y,
1742 uint32_t cx, uint32_t cy)
1743{
1744 RT_NOREF(pInterface, x, y, cx, cy);
1745 AssertReleaseFailed();
1746}
1747
1748
1749/**
1750 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
1751 */
1752static DECLCALLBACK(int)
1753qemuFwCfgR3RamfbPortCopyRect(PPDMIDISPLAYPORT pInterface,
1754 uint32_t cx, uint32_t cy,
1755 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
1756 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
1757 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
1758 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
1759{
1760 RT_NOREF(pInterface, cx, cy, pbSrc, xSrc, ySrc, cxSrc, cySrc, cbSrcLine, cSrcBitsPerPixel, pbDst, xDst, yDst, cxDst, cyDst,
1761 cbDstLine, cDstBitsPerPixel);
1762 AssertReleaseFailed();
1763 return VINF_SUCCESS;
1764}
1765
1766
1767/**
1768 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
1769 */
1770static DECLCALLBACK(void) qemuFwCfgR3RamfbPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
1771{
1772 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IPortRamfb);
1773
1774 LogFlowFunc(("fRender = %d\n", fRender));
1775
1776 int const rcLock = PDMDevHlpCritSectEnter(pThis->pDevIns, &pThis->CritSectRamfb, VERR_SEM_BUSY);
1777 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pThis->pDevIns, &pThis->CritSectRamfb, rcLock);
1778
1779 pThis->fRenderVRam = fRender;
1780
1781 PDMDevHlpCritSectLeave(pThis->pDevIns, &pThis->CritSectRamfb);
1782}
1783
1784
1785/**
1786 * @interface_method_impl{PDMIDISPLAYPORT,pfnSendModeHint}
1787 */
1788static DECLCALLBACK(int) qemuFwCfgR3RamfbPortSendModeHint(PPDMIDISPLAYPORT pInterface, uint32_t cx, uint32_t cy, uint32_t cBPP,
1789 uint32_t iDisplay, uint32_t dx, uint32_t dy, uint32_t fEnabled, uint32_t fNotifyGuest)
1790{
1791 RT_NOREF(pInterface, cx, cy, cBPP, iDisplay, dx, dy, fEnabled, fNotifyGuest);
1792 return VINF_SUCCESS;
1793}
1794
1795
1796/**
1797 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
1798 */
1799static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
1800 bool fSupportsMoveCursor)
1801{
1802 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
1803}
1804
1805
1806/**
1807 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
1808 */
1809static DECLCALLBACK(void) qemuFwCfgR3RamfbPortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
1810{
1811 RT_NOREF(pInterface, x, y, fOutOfRange);
1812}
1813
1814
1815/**
1816 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
1817 */
1818static DECLCALLBACK(void) qemuFwCfgR3RamfbTimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1819{
1820 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1821 RT_NOREF(pvUser);
1822
1823 if (pThis->pDrvL0)
1824 pThis->pDrvL0->pfnRefresh(pThis->pDrvL0);
1825
1826 if (pThis->cMilliesRefreshInterval)
1827 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
1828}
1829
1830
1831/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
1832
1833/**
1834 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1835 */
1836static DECLCALLBACK(void *) qemuFwCfgR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1837{
1838 PDEVQEMUFWCFG pThis = RT_FROM_MEMBER(pInterface, DEVQEMUFWCFG, IBase);
1839 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1840 if (pThis->fRamfbSupported)
1841 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPortRamfb);
1842 return NULL;
1843}
1844
1845
1846/**
1847 * @interface_method_impl{PDMDEVREG,pfnReset}
1848 */
1849static DECLCALLBACK(void) qemuFwCfgReset(PPDMDEVINS pDevIns)
1850{
1851 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1852
1853 qemuFwCfgR3ItemReset(pThis);
1854 pThis->GCPhysDma = 0;
1855
1856 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1857 RTVfsFileRelease(pThis->hVfsFileInitrd);
1858 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1859
1860 qemuFwCfgInitrdMaybeCreate(pThis); /* Ignores status code. */
1861}
1862
1863
1864/**
1865 * @interface_method_impl{PDMDEVREG,pfnAttach}
1866 *
1867 * This is like plugging in the monitor after turning on the PC.
1868 */
1869static DECLCALLBACK(int) qemuFwCfgR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1870{
1871 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1872
1873 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1874 ("QEMU RAM framebuffer device does not support hotplugging\n"),
1875 VERR_INVALID_PARAMETER);
1876
1877 switch (iLUN)
1878 {
1879 /* LUN #0: Display port. */
1880 case 0:
1881 {
1882 AssertLogRelMsgReturn(pThis->fRamfbSupported,
1883 ("QemuFwCfg: Trying to attach a display without the RAM framebuffer support being enabled!\n"),
1884 VERR_NOT_SUPPORTED);
1885
1886 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBaseL0, "Display Port");
1887 if (RT_SUCCESS(rc))
1888 {
1889 pThis->pDrvL0 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBaseL0, PDMIDISPLAYCONNECTOR);
1890 if (pThis->pDrvL0)
1891 {
1892 /* pThis->pDrvL0->pbData can be NULL when there is no framebuffer. */
1893 if ( pThis->pDrvL0->pfnRefresh
1894 && pThis->pDrvL0->pfnResize
1895 && pThis->pDrvL0->pfnUpdateRect)
1896 rc = VINF_SUCCESS;
1897 else
1898 {
1899 Assert(pThis->pDrvL0->pfnRefresh);
1900 Assert(pThis->pDrvL0->pfnResize);
1901 Assert(pThis->pDrvL0->pfnUpdateRect);
1902 pThis->pDrvL0 = NULL;
1903 pThis->pDrvBaseL0 = NULL;
1904 rc = VERR_INTERNAL_ERROR;
1905 }
1906 }
1907 else
1908 {
1909 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
1910 pThis->pDrvBaseL0 = NULL;
1911 rc = VERR_PDM_MISSING_INTERFACE;
1912 }
1913 }
1914 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1915 {
1916 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1917 rc = VINF_SUCCESS;
1918 }
1919 else
1920 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
1921 return rc;
1922 }
1923
1924 default:
1925 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1926 return VERR_PDM_NO_SUCH_LUN;
1927 }
1928}
1929
1930
1931/**
1932 * @interface_method_impl{PDMDEVREG,pfnDetach}
1933 *
1934 * This is like unplugging the monitor while the PC is still running.
1935 */
1936static DECLCALLBACK(void) qemuFwCfgR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1937{
1938 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1939 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("QEMU RAM framebuffer device does not support hotplugging\n"));
1940 RT_NOREF(fFlags);
1941
1942 /*
1943 * Reset the interfaces and update the controller state.
1944 */
1945 switch (iLUN)
1946 {
1947 /* LUN #0: Display port. */
1948 case 0:
1949 AssertLogRelMsg(pThis->fRamfbSupported,
1950 ("QemuFwCfg: Trying to detach a display without the RAM framebuffer support being enabled!\n"));
1951
1952 pThis->pDrvL0 = NULL;
1953 pThis->pDrvBaseL0 = NULL;
1954 break;
1955
1956 default:
1957 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1958 break;
1959 }
1960}
1961
1962
1963/**
1964 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1965 */
1966static DECLCALLBACK(int) qemuFwCfgDestruct(PPDMDEVINS pDevIns)
1967{
1968 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1969 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1970
1971 qemuFwCfgR3ItemReset(pThis);
1972 pThis->GCPhysDma = 0;
1973
1974 if (pThis->paCfgFiles)
1975 {
1976 Assert(pThis->cCfgFiles && pThis->cCfgFilesMax);
1977 RTMemFree(pThis->paCfgFiles);
1978 pThis->paCfgFiles = NULL;
1979 pThis->cCfgFiles = 0;
1980 pThis->cCfgFilesMax = 0;
1981 }
1982 else
1983 Assert(!pThis->cCfgFiles && !pThis->cCfgFilesMax);
1984
1985 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1986 RTVfsFileRelease(pThis->hVfsFileInitrd);
1987 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1988
1989 return VINF_SUCCESS;
1990}
1991
1992
1993/**
1994 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1995 */
1996static DECLCALLBACK(int) qemuFwCfgConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1997{
1998 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1999 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
2000 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2001 Assert(iInstance == 0); RT_NOREF(iInstance);
2002
2003 /*
2004 * Validate configuration.
2005 */
2006 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DmaEnabled"
2007 "|MmioBase"
2008 "|MmioSize"
2009 "|KernelImage"
2010 "|InitrdImage"
2011 "|SetupImage"
2012 "|CmdLine"
2013 "|QemuRamfbSupport",
2014 "");
2015
2016 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DmaEnabled", &pThis->fDmaEnabled, false);
2017 if (RT_FAILURE(rc))
2018 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmaEnabled\""));
2019
2020 /*
2021 * Init the data.
2022 */
2023 pThis->pDevIns = pDevIns;
2024 pThis->pCfg = pCfg;
2025 pThis->u32Version = QEMU_FW_CFG_VERSION_LEGACY | (pThis->fDmaEnabled ? QEMU_FW_CFG_VERSION_DMA : 0);
2026 pThis->GCPhysDma = 0;
2027 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
2028 pThis->paCfgFiles = NULL;
2029 pThis->cCfgFiles = 0;
2030 pThis->cCfgFilesMax = 0;
2031
2032 pThis->IBase.pfnQueryInterface = qemuFwCfgR3PortQueryInterface;
2033
2034 pThis->IPortRamfb.pfnUpdateDisplay = qemuFwCfgR3RamfbPortUpdateDisplay;
2035 pThis->IPortRamfb.pfnUpdateDisplayAll = qemuFwCfgR3RamfbPortUpdateDisplayAll;
2036 pThis->IPortRamfb.pfnQueryVideoMode = qemuFwCfgR3RamfbPortQueryVideoMode;
2037 pThis->IPortRamfb.pfnSetRefreshRate = qemuFwCfgR3RamfbPortSetRefreshRate;
2038 pThis->IPortRamfb.pfnTakeScreenshot = qemuFwCfgR3RamfbPortTakeScreenshot;
2039 pThis->IPortRamfb.pfnFreeScreenshot = qemuFwCfgR3RamfbPortFreeScreenshot;
2040 pThis->IPortRamfb.pfnDisplayBlt = qemuFwCfgR3RamfbPortDisplayBlt;
2041 pThis->IPortRamfb.pfnUpdateDisplayRect = qemuFwCfgR3RamfbPortUpdateDisplayRect;
2042 pThis->IPortRamfb.pfnCopyRect = qemuFwCfgR3RamfbPortCopyRect;
2043 pThis->IPortRamfb.pfnSetRenderVRAM = qemuFwCfgR3RamfbPortSetRenderVRAM;
2044 pThis->IPortRamfb.pfnSetViewport = NULL;
2045 pThis->IPortRamfb.pfnReportMonitorPositions = NULL;
2046 pThis->IPortRamfb.pfnSendModeHint = qemuFwCfgR3RamfbPortSendModeHint;
2047 pThis->IPortRamfb.pfnReportHostCursorCapabilities = qemuFwCfgR3RamfbPortReportHostCursorCapabilities;
2048 pThis->IPortRamfb.pfnReportHostCursorPosition = qemuFwCfgR3RamfbPortReportHostCursorPosition;
2049
2050 RTGCPHYS GCPhysMmioBase = 0;
2051 rc = pHlp->pfnCFGMQueryU64(pCfg, "MmioBase", &GCPhysMmioBase);
2052 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2053 return PDMDEV_SET_ERROR(pDevIns, rc,
2054 N_("Configuration error: Failed to get the \"MmioBase\" value"));
2055 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2056 {
2057 /*
2058 * Register standard I/O Ports
2059 */
2060 IOMIOPORTHANDLE hIoPorts;
2061 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, QEMU_FW_CFG_IO_PORT_START, QEMU_FW_CFG_IO_PORT_SIZE, 0 /*fFlags*/,
2062 qemuFwCfgIoPortWrite, qemuFwCfgIoPortRead,
2063 "QEMU firmware configuration", NULL /*paExtDescs*/, &hIoPorts);
2064 AssertRCReturn(rc, rc);
2065 }
2066 else
2067 {
2068 uint32_t cbMmio = 0;
2069 rc = pHlp->pfnCFGMQueryU32(pCfg, "MmioSize", &cbMmio);
2070 if (RT_FAILURE(rc))
2071 return PDMDEV_SET_ERROR(pDevIns, rc,
2072 N_("Configuration error: Failed to get the \"MmioSize\" value"));
2073
2074 /*
2075 * Register and map the MMIO region.
2076 */
2077 IOMMMIOHANDLE hMmio;
2078 rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysMmioBase, cbMmio, qemuFwCfgMmioWrite, qemuFwCfgMmioRead,
2079 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, "QemuFwCfg", &hMmio);
2080 AssertRCReturn(rc, rc);
2081 }
2082
2083 qemuFwCfgR3ItemReset(pThis);
2084
2085 /* Setup the RAM based framebuffer support if configured. */
2086 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "QemuRamfbSupport", &pThis->fRamfbSupported, false);
2087 if (RT_FAILURE(rc))
2088 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"QemuRamfbSupport\""));
2089
2090 if (pThis->fRamfbSupported)
2091 {
2092 LogRel(("QemuFwCfg: RAM based framebuffer support enabled\n"));
2093 if (!pThis->fDmaEnabled)
2094 return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER, N_("Configuration error: Enabling \"QemuRamfbSupport\" requires \"DmaEnabled\""));
2095
2096 /* Critical section for synchronizing access. */
2097 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectRamfb, RT_SRC_POS, "Ramfb#%u", iInstance);
2098 AssertRCReturn(rc, rc);
2099
2100 /*
2101 * Create the refresh timer.
2102 */
2103 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, qemuFwCfgR3RamfbTimerRefresh, NULL,
2104 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Ramfb Refresh", &pThis->hRamfbRefreshTimer);
2105 AssertRCReturn(rc, rc);
2106
2107 /* Register a config file item and attach the driver below us. */
2108 rc = qemuFwCfgR3Attach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
2109 AssertRCReturn(rc, rc);
2110
2111 rc = qemuFwCfgR3FileRegister(pThis, "etc/ramfb", sizeof(pThis->RamfbCfg),
2112 NULL /* pfnSetup */, NULL /*pfnRead*/,
2113 qemuFwCfgR3RamfbCfgWrite, NULL /*pfnCleanup*/);
2114 AssertRCReturn(rc, rc);
2115 }
2116
2117 rc = qemuFwCfgInitrdMaybeCreate(pThis);
2118 if (RT_FAILURE(rc))
2119 return rc; /* Error set. */
2120
2121 return VINF_SUCCESS;
2122}
2123
2124
2125/**
2126 * The device registration structure.
2127 */
2128const PDMDEVREG g_DeviceQemuFwCfg =
2129{
2130 /* .u32Version = */ PDM_DEVREG_VERSION,
2131 /* .uReserved0 = */ 0,
2132 /* .szName = */ "qemu-fw-cfg",
2133 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2134 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
2135 /* .cMaxInstances = */ 1,
2136 /* .uSharedVersion = */ 42,
2137 /* .cbInstanceShared = */ sizeof(DEVQEMUFWCFG),
2138 /* .cbInstanceCC = */ 0,
2139 /* .cbInstanceRC = */ 0,
2140 /* .cMaxPciDevices = */ 0,
2141 /* .cMaxMsixVectors = */ 0,
2142 /* .pszDescription = */ "QEMU Firmware Config compatible device",
2143#if defined(IN_RING3)
2144 /* .pszRCMod = */ "",
2145 /* .pszR0Mod = */ "",
2146 /* .pfnConstruct = */ qemuFwCfgConstruct,
2147 /* .pfnDestruct = */ qemuFwCfgDestruct,
2148 /* .pfnRelocate = */ NULL,
2149 /* .pfnMemSetup = */ NULL,
2150 /* .pfnPowerOn = */ NULL,
2151 /* .pfnReset = */ qemuFwCfgReset,
2152 /* .pfnSuspend = */ NULL,
2153 /* .pfnResume = */ NULL,
2154 /* .pfnAttach = */ qemuFwCfgR3Attach,
2155 /* .pfnDetach = */ qemuFwCfgR3Detach,
2156 /* .pfnQueryInterface = */ NULL,
2157 /* .pfnInitComplete = */ NULL,
2158 /* .pfnPowerOff = */ NULL,
2159 /* .pfnSoftReset = */ NULL,
2160 /* .pfnReserved0 = */ NULL,
2161 /* .pfnReserved1 = */ NULL,
2162 /* .pfnReserved2 = */ NULL,
2163 /* .pfnReserved3 = */ NULL,
2164 /* .pfnReserved4 = */ NULL,
2165 /* .pfnReserved5 = */ NULL,
2166 /* .pfnReserved6 = */ NULL,
2167 /* .pfnReserved7 = */ NULL,
2168#elif defined(IN_RING0)
2169 /* .pfnEarlyConstruct = */ NULL,
2170 /* .pfnConstruct = */ NULL,
2171 /* .pfnDestruct = */ NULL,
2172 /* .pfnFinalDestruct = */ NULL,
2173 /* .pfnRequest = */ NULL,
2174 /* .pfnReserved0 = */ NULL,
2175 /* .pfnReserved1 = */ NULL,
2176 /* .pfnReserved2 = */ NULL,
2177 /* .pfnReserved3 = */ NULL,
2178 /* .pfnReserved4 = */ NULL,
2179 /* .pfnReserved5 = */ NULL,
2180 /* .pfnReserved6 = */ NULL,
2181 /* .pfnReserved7 = */ NULL,
2182#elif defined(IN_RC)
2183 /* .pfnConstruct = */ NULL,
2184 /* .pfnReserved0 = */ NULL,
2185 /* .pfnReserved1 = */ NULL,
2186 /* .pfnReserved2 = */ NULL,
2187 /* .pfnReserved3 = */ NULL,
2188 /* .pfnReserved4 = */ NULL,
2189 /* .pfnReserved5 = */ NULL,
2190 /* .pfnReserved6 = */ NULL,
2191 /* .pfnReserved7 = */ NULL,
2192#else
2193# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2194#endif
2195 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2196};
2197
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