VirtualBox

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

Last change on this file since 98989 was 98481, checked in by vboxsync, 2 years ago

Devices/PC/DevQemuFwCfg: Add ability to create a initrd on the fly from a given directory. Allows remastering an initrd on the host which will be picked by the guest after a VM reset

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