VirtualBox

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

Last change on this file since 99211 was 99193, checked in by vboxsync, 23 months ago

Devices/DevQemuFwCfg: Add support for the MMIO interface for ARM, bugref:10386

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.0 KB
Line 
1/* $Id: DevQemuFwCfg.cpp 99193 2023-03-28 09:09:30Z vboxsync $ */
2/** @file
3 * DevQemuFwCfg - QEMU firmware configuration compatible device.
4 */
5
6/*
7 * Copyright (C) 2020-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_qemufwcfg The QEMU firmware configuration Device.
29 *
30 * The QEMU firmware configuration device is a custom device emulation
31 * to convey information about the VM to the guests firmware (UEFI for example).
32 * In the case of VirtualBox it is used to directly load a compatible kernel
33 * and initrd image like Linux from the host into the guest and boot it. This allows
34 * efficiently testing/debugging of multiple Linux kernels without having to install
35 * a guest OS. On VirtualBox the EFI firmware supports this interface, the BIOS is
36 * currently unsupported (and probably never will be).
37 *
38 * @section sec_qemufwcfg_config Configuration
39 *
40 * To use this interface for a particular VM the following extra data needs to be
41 * set besides enabling the EFI firmware:
42 *
43 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/KernelImage" /path/to/kernel
44 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/InitrdImage" /path/to/initrd
45 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/CmdLine" "<cmd line string>"
46 *
47 * The only mandatory item is the KernelImage one, the others are optional if the
48 * kernel is configured to not require it. The InitrdImage item accepts a path to a directory as well.
49 * If a directory is encountered, the CPIO initrd image is created on the fly and passed to the guest.
50 * If the kernel is not an EFI compatible executable (CONFIG_EFI_STUB=y for Linux) a dedicated setup image might be required
51 * which can be set with:
52 *
53 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/SetupImage" /path/to/setup_image
54 *
55 * @section sec_qemufwcfg_dma DMA
56 *
57 * The QEMU firmware configuration device supports an optional DMA interface to speed up transferring the data into the guest.
58 * It currently is not enabled by default but needs to be enabled with:
59 *
60 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/DmaEnabled" 1
61 */
62
63
64/*********************************************************************************************************************************
65* Header Files *
66*********************************************************************************************************************************/
67#define LOG_GROUP LOG_GROUP_DEV_QEMUFWCFG
68#include <VBox/vmm/pdmdev.h>
69#include <VBox/vmm/mm.h>
70#include <VBox/vmm/pgm.h>
71#include <VBox/log.h>
72#include <iprt/errcore.h>
73#include <iprt/assert.h>
74#include <iprt/dir.h>
75#include <iprt/file.h>
76#include <iprt/path.h>
77#include <iprt/string.h>
78#include <iprt/vfs.h>
79#include <iprt/zero.h>
80#include <iprt/zip.h>
81
82#include "VBoxDD.h"
83
84
85/*********************************************************************************************************************************
86* Defined Constants And Macros *
87*********************************************************************************************************************************/
88
89/** Start of the I/O port region. */
90#define QEMU_FW_CFG_IO_PORT_START 0x510
91/** Number of I/O ports reserved for this device. */
92#define QEMU_FW_CFG_IO_PORT_SIZE 12
93/** Offset of the config item selector register from the start. */
94#define QEMU_FW_CFG_OFF_SELECTOR 0
95/** Offset of the data port from the start. */
96#define QEMU_FW_CFG_OFF_DATA 1
97/** Offset of the high 32bit of the DMA address. */
98#define QEMU_FW_CFG_OFF_DMA_HIGH 4
99/** Offset of the low 32bit of the DMA address. */
100#define QEMU_FW_CFG_OFF_DMA_LOW 8
101
102
103/** @name MMIO register offsets.
104 * @{ */
105/** Data register offset. */
106#define QEU_FW_CFG_MMIO_OFF_DATA 0
107/** Selector register offset. */
108#define QEU_FW_CFG_MMIO_OFF_SELECTOR 8
109/** DMA base address register offset. */
110#define QEU_FW_CFG_MMIO_OFF_DMA 16
111/** @} */
112
113
114/** Set if legacy interface is supported (always set).*/
115#define QEMU_FW_CFG_VERSION_LEGACY RT_BIT_32(0)
116/** Set if DMA is supported.*/
117#define QEMU_FW_CFG_VERSION_DMA RT_BIT_32(1)
118
119
120/** Error happened during the DMA access. */
121#define QEMU_FW_CFG_DMA_ERROR RT_BIT_32(0)
122/** Read requested. */
123#define QEMU_FW_CFG_DMA_READ RT_BIT_32(1)
124/** Skipping bytes requested. */
125#define QEMU_FW_CFG_DMA_SKIP RT_BIT_32(2)
126/** The config item is selected. */
127#define QEMU_FW_CFG_DMA_SELECT RT_BIT_32(3)
128/** Write requested. */
129#define QEMU_FW_CFG_DMA_WRITE RT_BIT_32(4)
130/** Extracts the selected config item. */
131#define QEMU_FW_CFG_DMA_GET_CFG_ITEM(a_Control) ((uint16_t)((a_Control) >> 16))
132
133
134/** @name Known config items.
135 * @{ */
136#define QEMU_FW_CFG_ITEM_SIGNATURE UINT16_C(0x0000)
137#define QEMU_FW_CFG_ITEM_VERSION UINT16_C(0x0001)
138#define QEMU_FW_CFG_ITEM_SYSTEM_UUID UINT16_C(0x0002)
139#define QEMU_FW_CFG_ITEM_RAM_SIZE UINT16_C(0x0003)
140#define QEMU_FW_CFG_ITEM_GRAPHICS_ENABLED UINT16_C(0x0004)
141#define QEMU_FW_CFG_ITEM_SMP_CPU_COUNT UINT16_C(0x0005)
142#define QEMU_FW_CFG_ITEM_MACHINE_ID UINT16_C(0x0006)
143#define QEMU_FW_CFG_ITEM_KERNEL_ADDRESS UINT16_C(0x0007)
144#define QEMU_FW_CFG_ITEM_KERNEL_SIZE UINT16_C(0x0008)
145#define QEMU_FW_CFG_ITEM_KERNEL_CMD_LINE UINT16_C(0x0009)
146#define QEMU_FW_CFG_ITEM_INITRD_ADDRESS UINT16_C(0x000a)
147#define QEMU_FW_CFG_ITEM_INITRD_SIZE UINT16_C(0x000b)
148#define QEMU_FW_CFG_ITEM_BOOT_DEVICE UINT16_C(0x000c)
149#define QEMU_FW_CFG_ITEM_NUMA_DATA UINT16_C(0x000d)
150#define QEMU_FW_CFG_ITEM_BOOT_MENU UINT16_C(0x000e)
151#define QEMU_FW_CFG_ITEM_MAX_CPU_COUNT UINT16_C(0x000f)
152#define QEMU_FW_CFG_ITEM_KERNEL_ENTRY UINT16_C(0x0010)
153#define QEMU_FW_CFG_ITEM_KERNEL_DATA UINT16_C(0x0011)
154#define QEMU_FW_CFG_ITEM_INITRD_DATA UINT16_C(0x0012)
155#define QEMU_FW_CFG_ITEM_CMD_LINE_ADDRESS UINT16_C(0x0013)
156#define QEMU_FW_CFG_ITEM_CMD_LINE_SIZE UINT16_C(0x0014)
157#define QEMU_FW_CFG_ITEM_CMD_LINE_DATA UINT16_C(0x0015)
158#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_ADDRESS UINT16_C(0x0016)
159#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE UINT16_C(0x0017)
160#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA UINT16_C(0x0018)
161#define QEMU_FW_CFG_ITEM_FILE_DIR UINT16_C(0x0019)
162/** @} */
163
164/** The size of the directory entry buffer we're using. */
165#define QEMUFWCFG_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
166
167
168/*********************************************************************************************************************************
169* Structures and Typedefs *
170*********************************************************************************************************************************/
171
172/**
173 * QEMU firmware config DMA descriptor.
174 */
175typedef struct QEMUFWDMADESC
176{
177 /** Control field. */
178 uint32_t u32Ctrl;
179 /** Length of the transfer in bytes. */
180 uint32_t u32Length;
181 /** Address of the buffer to transfer from/to. */
182 uint64_t u64GCPhysBuf;
183} QEMUFWDMADESC;
184AssertCompileSize(QEMUFWDMADESC, 2 * 4 + 8);
185/** Pointer to a QEMU firmware config DMA descriptor. */
186typedef QEMUFWDMADESC *PQEMUFWDMADESC;
187/** Pointer to a const QEMU firmware config DMA descriptor. */
188typedef const QEMUFWDMADESC *PCQEMUFWDMADESC;
189
190
191/** Pointer to a const configuration item descriptor. */
192typedef const struct QEMUFWCFGITEM *PCQEMUFWCFGITEM;
193
194/**
195 * QEMU firmware config instance data structure.
196 */
197typedef struct DEVQEMUFWCFG
198{
199 /** Pointer back to the device instance. */
200 PPDMDEVINS pDevIns;
201 /** The configuration handle. */
202 PCFGMNODE pCfg;
203 /** Pointer to the currently selected item. */
204 PCQEMUFWCFGITEM pCfgItem;
205 /** Offset of the next byte to read from the start of the data item. */
206 uint32_t offCfgItemNext;
207 /** How many bytes are left for transfer. */
208 uint32_t cbCfgItemLeft;
209 /** Version register. */
210 uint32_t u32Version;
211 /** Guest physical address of the DMA descriptor. */
212 RTGCPHYS GCPhysDma;
213 /** VFS file of the on-the-fly created initramfs. */
214 RTVFSFILE hVfsFileInitrd;
215
216 /** Scratch buffer for config item specific data. */
217 union
218 {
219 uint8_t u8;
220 uint16_t u16;
221 uint32_t u32;
222 uint64_t u64;
223 /** VFS file handle. */
224 RTVFSFILE hVfsFile;
225 /** Byte view. */
226 uint8_t ab[8];
227 } u;
228} DEVQEMUFWCFG;
229/** Pointer to the QEMU firmware config device instance. */
230typedef DEVQEMUFWCFG *PDEVQEMUFWCFG;
231
232
233/**
234 * A supported configuration item descriptor.
235 */
236typedef struct QEMUFWCFGITEM
237{
238 /** The config tiem value. */
239 uint16_t uCfgItem;
240 /** Name of the item. */
241 const char *pszItem;
242 /** Optional CFGM key to lookup the content. */
243 const char *pszCfgmKey;
244 /**
245 * Setup callback for when the guest writes the selector.
246 *
247 * @returns VBox status code.
248 * @param pThis The QEMU fw config device instance.
249 * @param pItem Pointer to the selected item.
250 * @param pcbItem Where to store the size of the item on success.
251 */
252 DECLCALLBACKMEMBER(int, pfnSetup, (PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem));
253 /**
254 * Read callback to return the data.
255 *
256 * @returns VBox status code.
257 * @param pThis The QEMU fw config device instance.
258 * @param pItem Pointer to the selected item.
259 * @param off Where to start reading from.
260 * @param pvBuf Where to store the read data.
261 * @param cbToRead How much to read.
262 * @param pcbRead Where to store the amount of bytes read.
263 */
264 DECLCALLBACKMEMBER(int, pfnRead, (PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
265 uint32_t cbToRead, uint32_t *pcbRead));
266
267 /**
268 * Cleans up any allocated resources when the item is de-selected.
269 *
270 * @returns nothing.
271 * @param pThis The QEMU fw config device instance.
272 * @param pItem Pointer to the selected item.
273 */
274 DECLCALLBACKMEMBER(void, pfnCleanup, (PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem));
275} QEMUFWCFGITEM;
276/** Pointer to a configuration item descriptor. */
277typedef QEMUFWCFGITEM *PQEMUFWCFGITEM;
278
279
280
281/**
282 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the signature configuration item.}
283 */
284static DECLCALLBACK(int) qemuFwCfgR3SetupSignature(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
285{
286 RT_NOREF(pThis, pItem);
287 uint8_t abSig[] = { 'Q', 'E', 'M', 'U' };
288 memcpy(&pThis->u.ab[0], &abSig[0], sizeof(abSig));
289 *pcbItem = sizeof(abSig);
290 return VINF_SUCCESS;
291}
292
293
294/**
295 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the version configuration item.}
296 */
297static DECLCALLBACK(int) qemuFwCfgR3SetupVersion(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
298{
299 RT_NOREF(pThis, pItem);
300 memcpy(&pThis->u.ab[0], &pThis->u32Version, sizeof(pThis->u32Version));
301 *pcbItem = sizeof(pThis->u32Version);
302 return VINF_SUCCESS;
303}
304
305
306/**
307 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the file directory configuration item.}
308 */
309static DECLCALLBACK(int) qemuFwCfgR3SetupFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
310{
311 RT_NOREF(pThis, pItem);
312 memset(&pThis->u.ab[0], 0, sizeof(uint32_t)); /** @todo Implement */
313 *pcbItem = sizeof(uint32_t);
314 return VINF_SUCCESS;
315}
316
317
318/**
319 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a VFS file type configuration item.}
320 */
321static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFileSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
322{
323 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
324
325 int rc = VINF_SUCCESS;
326 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
327 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_SIZE
328 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
329 {
330 RTVfsFileRetain(pThis->hVfsFileInitrd);
331 hVfsFile = pThis->hVfsFileInitrd;
332 }
333 else
334 {
335 /* Query the path from the CFGM key. */
336 char *pszFilePath = NULL;
337 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
338 if (RT_SUCCESS(rc))
339 {
340 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
341 if (RT_FAILURE(rc))
342 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
343 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
344 }
345 else
346 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
347 }
348
349 if (RT_SUCCESS(rc))
350 {
351 uint64_t cbFile = 0;
352 rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
353 if (RT_SUCCESS(rc))
354 {
355 if (cbFile < _4G)
356 {
357 pThis->u.u32 = (uint32_t)cbFile;
358 *pcbItem = sizeof(uint32_t);
359 }
360 else
361 {
362 rc = VERR_BUFFER_OVERFLOW;
363 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
364 }
365 }
366 else
367 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
368 RTVfsFileRelease(hVfsFile);
369 }
370
371 return rc;
372}
373
374
375/**
376 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a string type configuration item.}
377 */
378static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStrSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
379{
380 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
381
382 /* Query the string from the CFGM key. */
383 char sz[_4K];
384 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
385 if (RT_SUCCESS(rc))
386 {
387 pThis->u.u32 = (uint32_t)strlen(&sz[0]) + 1;
388 *pcbItem = sizeof(uint32_t);
389 }
390 else
391 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
392
393 return rc;
394}
395
396
397/**
398 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a string type configuration item gathered from CFGM.}
399 */
400static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
401{
402 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
403
404 /* Query the string from the CFGM key. */
405 char sz[_4K];
406 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
407 if (RT_SUCCESS(rc))
408 *pcbItem = (uint32_t)strlen(&sz[0]) + 1;
409 else
410 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
411
412 return rc;
413}
414
415
416/**
417 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a VFS file type configuration item.}
418 */
419static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
420{
421 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
422
423 int rc = VINF_SUCCESS;
424 if ( pItem->uCfgItem == QEMU_FW_CFG_ITEM_INITRD_DATA
425 && pThis->hVfsFileInitrd != NIL_RTVFSFILE)
426 {
427 RTVfsFileRetain(pThis->hVfsFileInitrd);
428 pThis->u.hVfsFile = pThis->hVfsFileInitrd;
429 }
430 else
431 {
432 /* Query the path from the CFGM key. */
433 char *pszFilePath = NULL;
434 rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
435 if (RT_SUCCESS(rc))
436 {
437 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &pThis->u.hVfsFile);
438 if (RT_FAILURE(rc))
439 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
440 PDMDevHlpMMHeapFree(pThis->pDevIns, pszFilePath);
441 }
442 else
443 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
444 }
445
446 if (RT_SUCCESS(rc))
447 {
448 uint64_t cbFile = 0;
449 rc = RTVfsFileQuerySize(pThis->u.hVfsFile, &cbFile);
450 if (RT_SUCCESS(rc))
451 {
452 if (cbFile < _4G)
453 *pcbItem = (uint32_t)cbFile;
454 else
455 {
456 rc = VERR_BUFFER_OVERFLOW;
457 LogRel(("QemuFwCfg: File exceeds 4G limit (%llu)\n", cbFile));
458 }
459 }
460 else
461 LogRel(("QemuFwCfg: Failed to query file size from -> %Rrc\n", rc));
462 }
463
464 return rc;
465}
466
467
468/**
469 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a configuration item having its data stored in the scratch buffer.}
470 */
471static DECLCALLBACK(int) qemuFwCfgR3ReadSimple(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
472 uint32_t cbToRead, uint32_t *pcbRead)
473{
474 RT_NOREF(pThis, pItem);
475 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
476 *pcbRead = cbToRead;
477 return VINF_SUCCESS;
478}
479
480
481/**
482 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a VFS file type configuration item.}
483 */
484static DECLCALLBACK(int) qemuFwCfgR3ReadVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
485 uint32_t cbToRead, uint32_t *pcbRead)
486{
487 RT_NOREF(pItem);
488 size_t cbRead = 0;
489 int rc = RTVfsFileReadAt(pThis->u.hVfsFile, off, pvBuf, cbToRead, &cbRead);
490 if (RT_SUCCESS(rc))
491 *pcbRead = (uint32_t)cbRead;
492
493 return rc;
494}
495
496
497/**
498 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads a string item gathered from CFGM.}
499 */
500static DECLCALLBACK(int) qemuFwCfgR3ReadStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
501 uint32_t cbToRead, uint32_t *pcbRead)
502{
503 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
504
505 /* Query the string from the CFGM key. */
506 char sz[_4K];
507 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
508 if (RT_SUCCESS(rc))
509 {
510 uint32_t cch = (uint32_t)strlen(sz) + 1;
511 if (off < cch)
512 {
513 uint32_t cbRead = RT_MIN(cbToRead, off - cch);
514 memcpy(pvBuf, &sz[off], cbRead);
515 *pcbRead = cbRead;
516 }
517 else
518 rc = VERR_BUFFER_OVERFLOW;
519 }
520 else
521 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
522
523 return rc;
524}
525
526
527/**
528 * @interface_method_impl{QEMUFWCFGITEM,pfnCleanup, Cleans up a VFS file type configuration item.}
529 */
530static DECLCALLBACK(void) qemuFwCfgR3CleanupVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem)
531{
532 RT_NOREF(pItem);
533 RTVfsFileRelease(pThis->u.hVfsFile);
534 pThis->u.hVfsFile = NIL_RTVFSFILE;
535}
536
537
538/**
539 * Supported config items.
540 */
541static const QEMUFWCFGITEM g_aQemuFwCfgItems[] =
542{
543 /** u16Selector pszItem pszCfgmKey pfnSetup pfnRead pfnCleanup */
544 { QEMU_FW_CFG_ITEM_SIGNATURE, "Signature", NULL, qemuFwCfgR3SetupSignature, qemuFwCfgR3ReadSimple, NULL },
545 { QEMU_FW_CFG_ITEM_VERSION, "Version", NULL, qemuFwCfgR3SetupVersion, qemuFwCfgR3ReadSimple, NULL },
546 { QEMU_FW_CFG_ITEM_KERNEL_SIZE, "KrnlSz", "KernelImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL },
547 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "KrnlDat", "KernelImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, qemuFwCfgR3CleanupVfsFile },
548 { QEMU_FW_CFG_ITEM_INITRD_SIZE, "InitrdSz", "InitrdImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL },
549 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "InitrdDat", "InitrdImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, qemuFwCfgR3CleanupVfsFile },
550 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE, "SetupSz", "SetupImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL },
551 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA, "SetupDat", "SetupImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, qemuFwCfgR3CleanupVfsFile },
552 { QEMU_FW_CFG_ITEM_CMD_LINE_SIZE, "CmdLineSz", "CmdLine", qemuFwCfgR3SetupCfgmStrSz, qemuFwCfgR3ReadSimple, NULL },
553 { QEMU_FW_CFG_ITEM_CMD_LINE_DATA, "CmdLineDat", "CmdLine", qemuFwCfgR3SetupCfgmStr, qemuFwCfgR3ReadStr, NULL },
554 { QEMU_FW_CFG_ITEM_FILE_DIR, "FileDir", NULL, qemuFwCfgR3SetupFileDir, qemuFwCfgR3ReadSimple, NULL }
555};
556
557
558/**
559 * Resets the currently selected item.
560 *
561 * @returns nothing.
562 * @param pThis The QEMU fw config device instance.
563 */
564static void qemuFwCfgR3ItemReset(PDEVQEMUFWCFG pThis)
565{
566 if ( pThis->pCfgItem
567 && pThis->pCfgItem->pfnCleanup)
568 pThis->pCfgItem->pfnCleanup(pThis, pThis->pCfgItem);
569
570 pThis->pCfgItem = NULL;
571 pThis->offCfgItemNext = 0;
572 pThis->cbCfgItemLeft = 0;
573}
574
575
576/**
577 * Selects the given config item.
578 *
579 * @returns VBox status code.
580 * @param pThis The QEMU fw config device instance.
581 * @param uCfgItem The configuration item to select.
582 */
583static int qemuFwCfgItemSelect(PDEVQEMUFWCFG pThis, uint16_t uCfgItem)
584{
585 LogFlowFunc(("uCfgItem=%#x\n", uCfgItem));
586
587 qemuFwCfgR3ItemReset(pThis);
588
589 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQemuFwCfgItems); i++)
590 {
591 PCQEMUFWCFGITEM pCfgItem = &g_aQemuFwCfgItems[i];
592
593 if (pCfgItem->uCfgItem == uCfgItem)
594 {
595 uint32_t cbItem = 0;
596 int rc = pCfgItem->pfnSetup(pThis, pCfgItem, &cbItem);
597 if (RT_SUCCESS(rc))
598 {
599 pThis->pCfgItem = pCfgItem;
600 pThis->cbCfgItemLeft = cbItem;
601 return VINF_SUCCESS;
602 }
603
604 return rc;
605 }
606 }
607
608 return VERR_NOT_FOUND;
609}
610
611
612/**
613 * Processes a DMA transfer.
614 *
615 * @returns nothing.
616 * @param pThis The QEMU fw config device instance.
617 * @param GCPhysDma The guest physical address of the DMA descriptor.
618 */
619static void qemuFwCfgDmaXfer(PDEVQEMUFWCFG pThis, RTGCPHYS GCPhysDma)
620{
621 QEMUFWDMADESC DmaDesc; RT_ZERO(DmaDesc);
622
623 LogFlowFunc(("pThis=%p GCPhysDma=%RGp\n", pThis, GCPhysDma));
624
625 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysDma, &DmaDesc, sizeof(DmaDesc));
626
627 /* Convert from big endianess to host endianess. */
628 DmaDesc.u32Ctrl = RT_BE2H_U32(DmaDesc.u32Ctrl);
629 DmaDesc.u32Length = RT_BE2H_U32(DmaDesc.u32Length);
630 DmaDesc.u64GCPhysBuf = RT_BE2H_U64(DmaDesc.u64GCPhysBuf);
631
632 LogFlowFunc(("u32Ctrl=%#x u32Length=%u u64GCPhysBuf=%llx\n",
633 DmaDesc.u32Ctrl, DmaDesc.u32Length, DmaDesc.u64GCPhysBuf));
634
635 /* If the select bit is set a select is performed. */
636 int rc = VINF_SUCCESS;
637 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_SELECT)
638 rc = qemuFwCfgItemSelect(pThis, QEMU_FW_CFG_DMA_GET_CFG_ITEM(DmaDesc.u32Ctrl));
639
640 if (RT_SUCCESS(rc))
641 {
642 /* We don't support any writes right now. */
643 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE)
644 rc = VERR_INVALID_PARAMETER;
645 else if ( !pThis->pCfgItem
646 || !pThis->cbCfgItemLeft)
647 {
648 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
649 {
650 /* Item is not supported, just zero out the indicated area. */
651 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
652 uint32_t cbLeft = DmaDesc.u32Length;
653
654 while ( RT_SUCCESS(rc)
655 && cbLeft)
656 {
657 uint32_t cbZero = RT_MIN(_64K, cbLeft);
658
659 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &g_abRTZero64K[0], cbZero);
660
661 cbLeft -= cbZero;
662 GCPhysCur += cbZero;
663 }
664 }
665 /* else: Assume Skip */
666 }
667 else
668 {
669 /* Read or skip. */
670 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
671 uint32_t cbLeft = RT_MIN(DmaDesc.u32Length, pThis->cbCfgItemLeft);
672
673 while ( RT_SUCCESS(rc)
674 && cbLeft)
675 {
676 uint8_t abTmp[_1K];
677 uint32_t cbThisRead = RT_MIN(sizeof(abTmp), cbLeft);
678 uint32_t cbRead;
679
680 rc = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
681 cbThisRead, &cbRead);
682 if (RT_SUCCESS(rc))
683 {
684 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
685 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbRead);
686 /* else: Assume Skip */
687
688 cbLeft -= cbRead;
689 GCPhysCur += cbRead;
690
691 pThis->offCfgItemNext += cbRead;
692 pThis->cbCfgItemLeft -= cbRead;
693 }
694 }
695 }
696 }
697
698 LogFlowFunc(("pThis=%p GCPhysDma=%RGp -> %Rrc\n", pThis, GCPhysDma, rc));
699
700 /* Write back the control field. */
701 uint32_t u32Resp = RT_SUCCESS(rc) ? 0 : RT_H2BE_U32(QEMU_FW_CFG_DMA_ERROR);
702 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysDma, &u32Resp, sizeof(u32Resp));
703}
704
705
706/**
707 * @callback_method_impl{FNIOMIOPORTNEWOUT, QEMU firmware configuration write.}
708 */
709static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
710{
711 int rc = VINF_SUCCESS;
712 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
713 NOREF(pvUser);
714
715 LogFlowFunc(("offPort=%RTiop u32=%#x cb=%u\n", offPort, u32, cb));
716
717 switch (offPort)
718 {
719 case QEMU_FW_CFG_OFF_SELECTOR:
720 {
721 if (cb == 2)
722 qemuFwCfgItemSelect(pThis, (uint16_t)u32);
723 break;
724 }
725 case QEMU_FW_CFG_OFF_DATA: /* Readonly, ignore */
726 break;
727 case QEMU_FW_CFG_OFF_DMA_HIGH:
728 {
729 if (cb == 4)
730 pThis->GCPhysDma = ((RTGCPHYS)RT_BE2H_U32(u32)) << 32;
731 break;
732 }
733 case QEMU_FW_CFG_OFF_DMA_LOW:
734 {
735 if (cb == 4)
736 {
737 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U32(u32));
738 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
739 pThis->GCPhysDma = 0;
740 }
741 break;
742 }
743 default:
744 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", offPort, cb, u32);
745 break;
746 }
747
748 LogFlowFunc((" -> rc=%Rrc\n", rc));
749 return rc;
750}
751
752
753/**
754 * @callback_method_impl{FNIOMIOPORTNEWIN, QEMU firmware configuration read.}
755 */
756static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
757{
758 int rc = VINF_SUCCESS;
759 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
760 NOREF(pvUser);
761
762 *pu32 = 0;
763
764 LogFlowFunc(("offPort=%RTiop cb=%u\n", offPort, cb));
765
766 switch (offPort)
767 {
768 /* Selector (Writeonly, ignore). */
769 case QEMU_FW_CFG_OFF_SELECTOR:
770 break;
771 case QEMU_FW_CFG_OFF_DATA:
772 {
773 if (cb == 1)
774 {
775 if ( pThis->cbCfgItemLeft
776 && pThis->pCfgItem)
777 {
778 uint8_t bRead = 0;
779 uint32_t cbRead = 0;
780 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &bRead,
781 sizeof(bRead), &cbRead);
782 if ( RT_SUCCESS(rc2)
783 && cbRead == sizeof(bRead))
784 {
785 pThis->offCfgItemNext += cbRead;
786 pThis->cbCfgItemLeft -= cbRead;
787 *pu32 = bRead;
788 }
789 }
790 }
791 else
792 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
793 break;
794 }
795
796 default:
797 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
798 break;
799 }
800
801 LogFlowFunc(("offPort=%RTiop cb=%u -> rc=%Rrc u32=%#x\n", offPort, cb, rc, *pu32));
802
803 return rc;
804}
805
806
807/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
808
809
810/**
811 * @callback_method_impl{FNIOMMMIONEWREAD}
812 */
813static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
814{
815 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
816 RT_NOREF(pvUser);
817
818 LogFlowFunc(("%RGp cb=%u\n", off, cb));
819
820 AssertReturn(cb <= sizeof(uint64_t), VERR_INVALID_PARAMETER);
821
822 VBOXSTRICTRC rc = VINF_SUCCESS;
823 switch (off)
824 {
825 case QEU_FW_CFG_MMIO_OFF_DATA:
826 {
827 if ( pThis->cbCfgItemLeft
828 && pThis->pCfgItem)
829 {
830 uint32_t cbRead = 0;
831 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, pv,
832 cb, &cbRead);
833 if (RT_SUCCESS(rc2))
834 {
835 pThis->offCfgItemNext += cbRead;
836 pThis->cbCfgItemLeft -= cbRead;
837 }
838 }
839 else
840 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
841 break;
842 }
843 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
844 case QEU_FW_CFG_MMIO_OFF_DMA:
845 /* Writeonly, ignore. */
846 break;
847 default:
848 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
849 break;
850 }
851
852 return rc;
853}
854
855
856/**
857 * @callback_method_impl{FNIOMMMIONEWWRITE}
858 */
859static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
860{
861 int rc = VINF_SUCCESS;
862 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
863 RT_NOREF(pvUser);
864
865 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
866
867 switch (off)
868 {
869 case QEU_FW_CFG_MMIO_OFF_DATA: /* Readonly, ignore */
870 break;
871 case QEU_FW_CFG_MMIO_OFF_SELECTOR:
872 {
873 if (cb == sizeof(uint16_t))
874 qemuFwCfgItemSelect(pThis, *(uint16_t *)pv);
875 else
876 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
877 break;
878 }
879 case QEU_FW_CFG_MMIO_OFF_DMA:
880 {
881 if (cb == sizeof(uint64_t))
882 {
883 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U64(*(uint64_t *)pv));
884 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
885 pThis->GCPhysDma = 0;
886 }
887 else
888 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
889 break;
890 }
891 default:
892 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offMmio=%#x cb=%d\n", off, cb);
893 break;
894 }
895
896 LogFlowFunc((" -> rc=%Rrc\n", rc));
897 return rc;
898}
899
900
901/**
902 * Sets up the initrd memory file and CPIO filesystem stream for writing.
903 *
904 * @returns VBox status code.
905 * @param pThis The QEMU fw config device instance.
906 * @param phVfsFss Where to return the filesystem stream handle.
907 */
908static int qemuFwCfgCreateOutputArchive(PDEVQEMUFWCFG pThis, PRTVFSFSSTREAM phVfsFss)
909{
910 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &pThis->hVfsFileInitrd);
911 if (RT_SUCCESS(rc))
912 {
913 RTVFSFSSTREAM hVfsFss;
914 RTVFSIOSTREAM hVfsIosOut = RTVfsFileToIoStream(pThis->hVfsFileInitrd);
915 rc = RTZipTarFsStreamToIoStream(hVfsIosOut, RTZIPTARFORMAT_CPIO_ASCII_NEW, 0 /*fFlags*/, &hVfsFss);
916 if (RT_SUCCESS(rc))
917 {
918 rc = RTZipTarFsStreamSetOwner(hVfsFss, 0, "root");
919 if (RT_SUCCESS(rc))
920 rc = RTZipTarFsStreamSetGroup(hVfsFss, 0, "root");
921
922 if (RT_SUCCESS(rc))
923 *phVfsFss = hVfsFss;
924 else
925 {
926 RTVfsFsStrmRelease(hVfsFss);
927 *phVfsFss = NIL_RTVFSFSSTREAM;
928 }
929 }
930 RTVfsIoStrmRelease(hVfsIosOut);
931 }
932
933 return rc;
934}
935
936
937/**
938 * Archives a file.
939 *
940 * @returns VBox status code.
941 * @param hVfsFss The TAR filesystem stream handle.
942 * @param pszSrc The file path or VFS spec.
943 * @param pszDst The name to archive the file under.
944 * @param pErrInfo Error info buffer (saves stack space).
945 */
946static int qemuFwCfgInitrdArchiveFile(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
947 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
948{
949 /* Open the file. */
950 uint32_t offError;
951 RTVFSIOSTREAM hVfsIosSrc;
952 int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
953 &hVfsIosSrc, &offError, RTErrInfoInitStatic(pErrInfo));
954 if (RT_FAILURE(rc))
955 return rc;
956
957 /* I/O stream to base object. */
958 RTVFSOBJ hVfsObjSrc = RTVfsObjFromIoStream(hVfsIosSrc);
959 if (hVfsObjSrc != NIL_RTVFSOBJ)
960 {
961 /*
962 * Add it to the stream.
963 */
964 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
965 RTVfsIoStrmRelease(hVfsIosSrc);
966 RTVfsObjRelease(hVfsObjSrc);
967 return rc;
968 }
969 RTVfsIoStrmRelease(hVfsIosSrc);
970 return VERR_INVALID_POINTER;
971}
972
973
974/**
975 * Archives a symlink.
976 *
977 * @returns VBox status code.
978 * @param hVfsFss The TAR filesystem stream handle.
979 * @param pszSrc The file path or VFS spec.
980 * @param pszDst The name to archive the file under.
981 * @param pErrInfo Error info buffer (saves stack space).
982 */
983static int qemuFwCfgInitrdArchiveSymlink(RTVFSFSSTREAM hVfsFss, const char *pszSrc,
984 const char *pszDst, PRTERRINFOSTATIC pErrInfo)
985{
986 /* Open the file. */
987 uint32_t offError;
988 RTVFSOBJ hVfsObjSrc;
989 int rc = RTVfsChainOpenObj(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
990 RTVFSOBJ_F_OPEN_SYMLINK | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
991 &hVfsObjSrc, &offError, RTErrInfoInitStatic(pErrInfo));
992 if (RT_FAILURE(rc))
993 return rc;
994
995 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
996 RTVfsObjRelease(hVfsObjSrc);
997 return rc;
998}
999
1000
1001/**
1002 * Sub-directory helper for creating archives.
1003 *
1004 * @returns VBox status code.
1005 * @param hVfsFss The TAR filesystem stream handle.
1006 * @param pszSrc The directory path or VFS spec. We append to the
1007 * buffer as we decend.
1008 * @param cchSrc The length of the input.
1009 * @param pszDst The name to archive it the under. We append to the
1010 * buffer as we decend.
1011 * @param cchDst The length of the input.
1012 * @param pDirEntry Directory entry to use for the directory to handle.
1013 * @param pErrInfo Error info buffer (saves stack space).
1014 */
1015static int qemuFwCfgInitrdArchiveDirSub(RTVFSFSSTREAM hVfsFss,
1016 char *pszSrc, size_t cchSrc,
1017 char pszDst[RTPATH_MAX], size_t cchDst, PRTDIRENTRYEX pDirEntry,
1018 PRTERRINFOSTATIC pErrInfo)
1019{
1020 uint32_t offError;
1021 RTVFSDIR hVfsIoDir;
1022 int rc = RTVfsChainOpenDir(pszSrc, 0 /*fFlags*/,
1023 &hVfsIoDir, &offError, RTErrInfoInitStatic(pErrInfo));
1024 if (RT_FAILURE(rc))
1025 return rc;
1026
1027 /* Make sure we've got some room in the path, to save us extra work further down. */
1028 if (cchSrc + 3 >= RTPATH_MAX)
1029 return VERR_FILENAME_TOO_LONG;
1030
1031 /* Ensure we've got a trailing slash (there is space for it see above). */
1032 if (!RTPATH_IS_SEP(pszSrc[cchSrc - 1]))
1033 {
1034 pszSrc[cchSrc++] = RTPATH_SLASH;
1035 pszSrc[cchSrc] = '\0';
1036 }
1037
1038 /* Ditto for destination. */
1039 if (cchDst + 3 >= RTPATH_MAX)
1040 return VERR_FILENAME_TOO_LONG;
1041
1042 /* Don't try adding the root directory. */
1043 if (*pszDst != '\0')
1044 {
1045 RTVFSOBJ hVfsObjSrc = RTVfsObjFromDir(hVfsIoDir);
1046 rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObjSrc, 0 /*fFlags*/);
1047 RTVfsObjRelease(hVfsObjSrc);
1048 if (RT_FAILURE(rc))
1049 return rc;
1050
1051 if (!RTPATH_IS_SEP(pszDst[cchDst - 1]))
1052 {
1053 pszDst[cchDst++] = RTPATH_SLASH;
1054 pszDst[cchDst] = '\0';
1055 }
1056 }
1057
1058 /*
1059 * Process the files and subdirs.
1060 */
1061 for (;;)
1062 {
1063 size_t cbDirEntry = QEMUFWCFG_DIRENTRY_BUF_SIZE;
1064 rc = RTVfsDirReadEx(hVfsIoDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1065 if (RT_FAILURE(rc))
1066 break;
1067
1068 /* Check length. */
1069 if (pDirEntry->cbName + cchSrc + 3 >= RTPATH_MAX)
1070 {
1071 rc = VERR_BUFFER_OVERFLOW;
1072 break;
1073 }
1074
1075 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1076 {
1077 case RTFS_TYPE_DIRECTORY:
1078 {
1079 if (RTDirEntryExIsStdDotLink(pDirEntry))
1080 continue;
1081
1082 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1083 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1084 rc = qemuFwCfgInitrdArchiveDirSub(hVfsFss, pszSrc, cchSrc + pDirEntry->cbName,
1085 pszDst, cchDst + pDirEntry->cbName, pDirEntry, pErrInfo);
1086 break;
1087 }
1088
1089 case RTFS_TYPE_FILE:
1090 {
1091 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1092 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1093 rc = qemuFwCfgInitrdArchiveFile(hVfsFss, pszSrc, pszDst, pErrInfo);
1094 break;
1095 }
1096
1097 case RTFS_TYPE_SYMLINK:
1098 {
1099 memcpy(&pszSrc[cchSrc], pDirEntry->szName, pDirEntry->cbName + 1);
1100 memcpy(&pszDst[cchDst], pDirEntry->szName, pDirEntry->cbName + 1);
1101 rc = qemuFwCfgInitrdArchiveSymlink(hVfsFss, pszSrc, pszDst, pErrInfo);
1102 break;
1103 }
1104
1105 default:
1106 {
1107 LogRel(("Warning: File system type %#x for '%s' not implemented yet, sorry! Skipping ...\n",
1108 pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK, pDirEntry->szName));
1109 break;
1110 }
1111 }
1112 }
1113
1114 if (rc == VERR_NO_MORE_FILES)
1115 rc = VINF_SUCCESS;
1116
1117 RTVfsDirRelease(hVfsIoDir);
1118 return rc;
1119}
1120
1121
1122/**
1123 * Archives a directory recursively.
1124 *
1125 * @returns VBox status code.
1126 * @param hVfsFss The CPIO filesystem stream handle.
1127 * @param pszSrc The directory path or VFS spec. We append to the
1128 * buffer as we decend.
1129 * @param cchSrc The length of the input.
1130 * @param pszDst The name to archive it the under. We append to the
1131 * buffer as we decend.
1132 * @param cchDst The length of the input.
1133 * @param pErrInfo Error info buffer (saves stack space).
1134 */
1135static int qemuFwCfgInitrdArchiveDir(RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
1136 char pszDst[RTPATH_MAX], size_t cchDst,
1137 PRTERRINFOSTATIC pErrInfo)
1138{
1139 RT_NOREF(cchSrc);
1140
1141 char szSrcAbs[RTPATH_MAX];
1142 int rc = RTPathAbs(pszSrc, szSrcAbs, sizeof(szSrcAbs));
1143 if (RT_FAILURE(rc))
1144 return rc;
1145
1146 union
1147 {
1148 uint8_t abPadding[QEMUFWCFG_DIRENTRY_BUF_SIZE];
1149 RTDIRENTRYEX DirEntry;
1150 } uBuf;
1151
1152 return qemuFwCfgInitrdArchiveDirSub(hVfsFss, szSrcAbs, strlen(szSrcAbs), pszDst, cchDst, &uBuf.DirEntry, pErrInfo);
1153}
1154
1155
1156/**
1157 * Creates an on the fly initramfs for a given root directory.
1158 *
1159 * @returns VBox status code.
1160 * @param pThis The QEMU fw config device instance.
1161 * @param pszPath The path to work on.
1162 */
1163static int qemuFwCfgInitrdCreate(PDEVQEMUFWCFG pThis, const char *pszPath)
1164{
1165 /*
1166 * First open the output file.
1167 */
1168 RTVFSFSSTREAM hVfsFss;
1169 int rc = qemuFwCfgCreateOutputArchive(pThis, &hVfsFss);
1170 if (RT_FAILURE(rc))
1171 return rc;
1172
1173 /*
1174 * Construct/copy the source name.
1175 */
1176 char szSrc[RTPATH_MAX];
1177 rc = RTStrCopy(szSrc, sizeof(szSrc), pszPath);
1178 if (RT_SUCCESS(rc))
1179 {
1180 RTERRINFOSTATIC ErrInfo;
1181 char szDst[RTPATH_MAX]; RT_ZERO(szDst);
1182 rc = qemuFwCfgInitrdArchiveDir(hVfsFss, szSrc, strlen(szSrc),
1183 szDst, strlen(szDst), &ErrInfo);
1184 }
1185
1186 /*
1187 * Finalize the archive.
1188 */
1189 int rc2 = RTVfsFsStrmEnd(hVfsFss); AssertRC(rc2);
1190 RTVfsFsStrmRelease(hVfsFss);
1191 return rc;
1192}
1193
1194
1195/**
1196 * Checks whether creation of the initrd should be done on the fly.
1197 *
1198 * @returns VBox status code.
1199 * @param pThis The QEMU fw config device instance.
1200 */
1201static int qemuFwCfgInitrdMaybeCreate(PDEVQEMUFWCFG pThis)
1202{
1203 PPDMDEVINS pDevIns = pThis->pDevIns;
1204 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1205
1206 /* Query the path from the CFGM key. */
1207 char *pszFilePath = NULL;
1208 int rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, "InitrdImage", &pszFilePath);
1209 if (RT_SUCCESS(rc))
1210 {
1211 if (RTDirExists(pszFilePath))
1212 {
1213 rc = qemuFwCfgInitrdCreate(pThis, pszFilePath);
1214 if (RT_FAILURE(rc))
1215 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1216 N_("QemuFwCfg: failed to create the in memory initram filesystem"));
1217 }
1218 /*else: Probably a normal file. */
1219 PDMDevHlpMMHeapFree(pDevIns, pszFilePath);
1220 }
1221 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
1222 rc = PDMDEV_SET_ERROR(pDevIns, rc,
1223 N_("Configuration error: Querying \"InitrdImage\" as a string failed"));
1224 else
1225 rc = VINF_SUCCESS;
1226
1227 return rc;
1228}
1229
1230
1231/**
1232 * @interface_method_impl{PDMDEVREG,pfnReset}
1233 */
1234static DECLCALLBACK(void) qemuFwCfgReset(PPDMDEVINS pDevIns)
1235{
1236 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1237
1238 qemuFwCfgR3ItemReset(pThis);
1239 pThis->GCPhysDma = 0;
1240
1241 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1242 RTVfsFileRelease(pThis->hVfsFileInitrd);
1243 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1244
1245 qemuFwCfgInitrdMaybeCreate(pThis); /* Ignores status code. */
1246}
1247
1248
1249/**
1250 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1251 */
1252static DECLCALLBACK(int) qemuFwCfgDestruct(PPDMDEVINS pDevIns)
1253{
1254 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1255 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1256
1257 qemuFwCfgR3ItemReset(pThis);
1258 pThis->GCPhysDma = 0;
1259
1260 if (pThis->hVfsFileInitrd != NIL_RTVFSFILE)
1261 RTVfsFileRelease(pThis->hVfsFileInitrd);
1262 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1263
1264 return VINF_SUCCESS;
1265}
1266
1267
1268/**
1269 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1270 */
1271static DECLCALLBACK(int) qemuFwCfgConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1272{
1273 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1274 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
1275 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1276 Assert(iInstance == 0); RT_NOREF(iInstance);
1277
1278 /*
1279 * Validate configuration.
1280 */
1281 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DmaEnabled"
1282 "|MmioBase"
1283 "|MmioSize"
1284 "|KernelImage"
1285 "|InitrdImage"
1286 "|SetupImage"
1287 "|CmdLine",
1288 "");
1289
1290 bool fDmaEnabled = false;
1291 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DmaEnabled", &fDmaEnabled, false);
1292 if (RT_FAILURE(rc))
1293 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmaEnabled\""));
1294
1295 /*
1296 * Init the data.
1297 */
1298 pThis->pDevIns = pDevIns;
1299 pThis->pCfg = pCfg;
1300 pThis->u32Version = QEMU_FW_CFG_VERSION_LEGACY | (fDmaEnabled ? QEMU_FW_CFG_VERSION_DMA : 0);
1301 pThis->GCPhysDma = 0;
1302 pThis->hVfsFileInitrd = NIL_RTVFSFILE;
1303
1304 RTGCPHYS GCPhysMmioBase = 0;
1305 rc = pHlp->pfnCFGMQueryU64(pCfg, "MmioBase", &GCPhysMmioBase);
1306 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
1307 return PDMDEV_SET_ERROR(pDevIns, rc,
1308 N_("Configuration error: Failed to get the \"MmioBase\" value"));
1309 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1310 {
1311 /*
1312 * Register standard I/O Ports
1313 */
1314 IOMIOPORTHANDLE hIoPorts;
1315 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, QEMU_FW_CFG_IO_PORT_START, QEMU_FW_CFG_IO_PORT_SIZE, 0 /*fFlags*/,
1316 qemuFwCfgIoPortWrite, qemuFwCfgIoPortRead,
1317 "QEMU firmware configuration", NULL /*paExtDescs*/, &hIoPorts);
1318 AssertRCReturn(rc, rc);
1319 }
1320 else
1321 {
1322 uint32_t cbMmio = 0;
1323 rc = pHlp->pfnCFGMQueryU32(pCfg, "MmioSize", &cbMmio);
1324 if (RT_FAILURE(rc))
1325 return PDMDEV_SET_ERROR(pDevIns, rc,
1326 N_("Configuration error: Failed to get the \"MmioSize\" value"));
1327
1328 /*
1329 * Register and map the MMIO region.
1330 */
1331 IOMMMIOHANDLE hMmio;
1332 rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysMmioBase, cbMmio, qemuFwCfgMmioWrite, qemuFwCfgMmioRead,
1333 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, "QemuFwCfg", &hMmio);
1334 AssertRCReturn(rc, rc);
1335 }
1336
1337 qemuFwCfgR3ItemReset(pThis);
1338
1339 rc = qemuFwCfgInitrdMaybeCreate(pThis);
1340 if (RT_FAILURE(rc))
1341 return rc; /* Error set. */
1342
1343 return VINF_SUCCESS;
1344}
1345
1346
1347/**
1348 * The device registration structure.
1349 */
1350const PDMDEVREG g_DeviceQemuFwCfg =
1351{
1352 /* .u32Version = */ PDM_DEVREG_VERSION,
1353 /* .uReserved0 = */ 0,
1354 /* .szName = */ "qemu-fw-cfg",
1355 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
1356 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
1357 /* .cMaxInstances = */ 1,
1358 /* .uSharedVersion = */ 42,
1359 /* .cbInstanceShared = */ sizeof(DEVQEMUFWCFG),
1360 /* .cbInstanceCC = */ 0,
1361 /* .cbInstanceRC = */ 0,
1362 /* .cMaxPciDevices = */ 0,
1363 /* .cMaxMsixVectors = */ 0,
1364 /* .pszDescription = */ "QEMU Firmware Config compatible device",
1365#if defined(IN_RING3)
1366 /* .pszRCMod = */ "",
1367 /* .pszR0Mod = */ "",
1368 /* .pfnConstruct = */ qemuFwCfgConstruct,
1369 /* .pfnDestruct = */ qemuFwCfgDestruct,
1370 /* .pfnRelocate = */ NULL,
1371 /* .pfnMemSetup = */ NULL,
1372 /* .pfnPowerOn = */ NULL,
1373 /* .pfnReset = */ qemuFwCfgReset,
1374 /* .pfnSuspend = */ NULL,
1375 /* .pfnResume = */ NULL,
1376 /* .pfnAttach = */ NULL,
1377 /* .pfnDetach = */ NULL,
1378 /* .pfnQueryInterface = */ NULL,
1379 /* .pfnInitComplete = */ NULL,
1380 /* .pfnPowerOff = */ NULL,
1381 /* .pfnSoftReset = */ NULL,
1382 /* .pfnReserved0 = */ NULL,
1383 /* .pfnReserved1 = */ NULL,
1384 /* .pfnReserved2 = */ NULL,
1385 /* .pfnReserved3 = */ NULL,
1386 /* .pfnReserved4 = */ NULL,
1387 /* .pfnReserved5 = */ NULL,
1388 /* .pfnReserved6 = */ NULL,
1389 /* .pfnReserved7 = */ NULL,
1390#elif defined(IN_RING0)
1391 /* .pfnEarlyConstruct = */ NULL,
1392 /* .pfnConstruct = */ NULL,
1393 /* .pfnDestruct = */ NULL,
1394 /* .pfnFinalDestruct = */ NULL,
1395 /* .pfnRequest = */ NULL,
1396 /* .pfnReserved0 = */ NULL,
1397 /* .pfnReserved1 = */ NULL,
1398 /* .pfnReserved2 = */ NULL,
1399 /* .pfnReserved3 = */ NULL,
1400 /* .pfnReserved4 = */ NULL,
1401 /* .pfnReserved5 = */ NULL,
1402 /* .pfnReserved6 = */ NULL,
1403 /* .pfnReserved7 = */ NULL,
1404#elif defined(IN_RC)
1405 /* .pfnConstruct = */ NULL,
1406 /* .pfnReserved0 = */ NULL,
1407 /* .pfnReserved1 = */ NULL,
1408 /* .pfnReserved2 = */ NULL,
1409 /* .pfnReserved3 = */ NULL,
1410 /* .pfnReserved4 = */ NULL,
1411 /* .pfnReserved5 = */ NULL,
1412 /* .pfnReserved6 = */ NULL,
1413 /* .pfnReserved7 = */ NULL,
1414#else
1415# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1416#endif
1417 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1418};
1419
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