VirtualBox

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

Last change on this file since 89394 was 86034, checked in by vboxsync, 4 years ago

Devices/DevQemuFwCfg: Add device emulation implementing QEMUs firmware configuration interface [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/* $Id: DevQemuFwCfg.cpp 86034 2020-09-06 08:37:06Z vboxsync $ */
2/** @file
3 * DevQemuFwCfg - QEMU firmware configuration compatible device.
4 */
5
6/*
7 * Copyright (C) 2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_qemufwcfg The QEMU firmware configuration Device.
19 *
20 * The QEMU firmware configuration device is a custom device emulation
21 * to convey information about the VM to the guests firmware (UEFI for example).
22 * In the case of VirtualBox it is used to directly load a compatible kernel
23 * and initrd image like Linux from the host into the guest and boot it. This allows
24 * efficiently testing/debugging of multiple Linux kernels without having to install
25 * a guest OS. On VirtualBox the EFI firmware supports this interface, the BIOS is
26 * currently unsupported (and probably never will be).
27 *
28 * @section sec_qemufwcfg_config Configuration
29 *
30 * To use this interface for a particular VM the following extra data needs to be
31 * set besides enabling the EFI firmware:
32 *
33 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/KernelImage" /path/to/kernel
34 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/InitrdImage" /path/to/initrd
35 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/CmdLine" "<cmd line string>"
36 *
37 * The only mandatory item is the KernelImage one, the others are optional if the
38 * kernel is configured to not require it. If the kernel is not an EFI compatible
39 * executable (CONFIG_EFI_STUB=y for Linux) a dedicated setup image might be required
40 * which can be set with:
41 *
42 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/SetupImage" /path/to/setup_image
43 *
44 * @section sec_qemufwcfg_dma DMA
45 *
46 * The QEMU firmware configuration device supports an optional DMA interface to speed up transferring the data into the guest.
47 * It currently is not enabled by default but needs to be enabled with:
48 *
49 * VBoxManage setextradata <VM name> "VBoxInternal/Devices/qemu-fw-cfg/0/Config/DmaEnabled" 1
50 */
51
52
53/*********************************************************************************************************************************
54* Header Files *
55*********************************************************************************************************************************/
56#define LOG_GROUP LOG_GROUP_DEV_QEMUFWCFG
57#include <VBox/vmm/pdmdev.h>
58#include <VBox/vmm/mm.h>
59#include <VBox/vmm/pgm.h>
60#include <VBox/log.h>
61#include <iprt/errcore.h>
62#include <iprt/assert.h>
63#include <iprt/file.h>
64#include <iprt/string.h>
65#include <iprt/vfs.h>
66#include <iprt/zero.h>
67
68#include "VBoxDD.h"
69
70
71/*********************************************************************************************************************************
72* Defined Constants And Macros *
73*********************************************************************************************************************************/
74
75/** Start of the I/O port region. */
76#define QEMU_FW_CFG_IO_PORT_START 0x510
77/** Number of I/O ports reserved for this device. */
78#define QEMU_FW_CFG_IO_PORT_SIZE 12
79/** Offset of the config item selector register from the start. */
80#define QEMU_FW_CFG_OFF_SELECTOR 0
81/** Offset of the data port from the start. */
82#define QEMU_FW_CFG_OFF_DATA 1
83/** Offset of the high 32bit of the DMA address. */
84#define QEMU_FW_CFG_OFF_DMA_HIGH 4
85/** Offset of the low 32bit of the DMA address. */
86#define QEMU_FW_CFG_OFF_DMA_LOW 8
87
88
89/** Set if legacy interface is supported (always set).*/
90#define QEMU_FW_CFG_VERSION_LEGACY RT_BIT_32(0)
91/** Set if DMA is supported.*/
92#define QEMU_FW_CFG_VERSION_DMA RT_BIT_32(1)
93
94
95/** Error happened during the DMA access. */
96#define QEMU_FW_CFG_DMA_ERROR RT_BIT_32(0)
97/** Read requested. */
98#define QEMU_FW_CFG_DMA_READ RT_BIT_32(1)
99/** Skipping bytes requested. */
100#define QEMU_FW_CFG_DMA_SKIP RT_BIT_32(2)
101/** The config item is selected. */
102#define QEMU_FW_CFG_DMA_SELECT RT_BIT_32(3)
103/** Write requested. */
104#define QEMU_FW_CFG_DMA_WRITE RT_BIT_32(4)
105/** Extracts the selected config item. */
106#define QEMU_FW_CFG_DMA_GET_CFG_ITEM(a_Control) ((uint16_t)((a_Control) >> 16))
107
108
109/** @name Known config items.
110 * @{ */
111#define QEMU_FW_CFG_ITEM_SIGNATURE UINT16_C(0x0000)
112#define QEMU_FW_CFG_ITEM_VERSION UINT16_C(0x0001)
113#define QEMU_FW_CFG_ITEM_SYSTEM_UUID UINT16_C(0x0002)
114#define QEMU_FW_CFG_ITEM_RAM_SIZE UINT16_C(0x0003)
115#define QEMU_FW_CFG_ITEM_GRAPHICS_ENABLED UINT16_C(0x0004)
116#define QEMU_FW_CFG_ITEM_SMP_CPU_COUNT UINT16_C(0x0005)
117#define QEMU_FW_CFG_ITEM_MACHINE_ID UINT16_C(0x0006)
118#define QEMU_FW_CFG_ITEM_KERNEL_ADDRESS UINT16_C(0x0007)
119#define QEMU_FW_CFG_ITEM_KERNEL_SIZE UINT16_C(0x0008)
120#define QEMU_FW_CFG_ITEM_KERNEL_CMD_LINE UINT16_C(0x0009)
121#define QEMU_FW_CFG_ITEM_INITRD_ADDRESS UINT16_C(0x000a)
122#define QEMU_FW_CFG_ITEM_INITRD_SIZE UINT16_C(0x000b)
123#define QEMU_FW_CFG_ITEM_BOOT_DEVICE UINT16_C(0x000c)
124#define QEMU_FW_CFG_ITEM_NUMA_DATA UINT16_C(0x000d)
125#define QEMU_FW_CFG_ITEM_BOOT_MENU UINT16_C(0x000e)
126#define QEMU_FW_CFG_ITEM_MAX_CPU_COUNT UINT16_C(0x000f)
127#define QEMU_FW_CFG_ITEM_KERNEL_ENTRY UINT16_C(0x0010)
128#define QEMU_FW_CFG_ITEM_KERNEL_DATA UINT16_C(0x0011)
129#define QEMU_FW_CFG_ITEM_INITRD_DATA UINT16_C(0x0012)
130#define QEMU_FW_CFG_ITEM_CMD_LINE_ADDRESS UINT16_C(0x0013)
131#define QEMU_FW_CFG_ITEM_CMD_LINE_SIZE UINT16_C(0x0014)
132#define QEMU_FW_CFG_ITEM_CMD_LINE_DATA UINT16_C(0x0015)
133#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_ADDRESS UINT16_C(0x0016)
134#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE UINT16_C(0x0017)
135#define QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA UINT16_C(0x0018)
136#define QEMU_FW_CFG_ITEM_FILE_DIR UINT16_C(0x0019)
137/** @} */
138
139
140/*********************************************************************************************************************************
141* Structures and Typedefs *
142*********************************************************************************************************************************/
143
144/**
145 * QEMU firmware config DMA descriptor.
146 */
147typedef struct QEMUFWDMADESC
148{
149 /** Control field. */
150 uint32_t u32Ctrl;
151 /** Length of the transfer in bytes. */
152 uint32_t u32Length;
153 /** Address of the buffer to transfer from/to. */
154 uint64_t u64GCPhysBuf;
155} QEMUFWDMADESC;
156AssertCompileSize(QEMUFWDMADESC, 2 * 4 + 8);
157/** Pointer to a QEMU firmware config DMA descriptor. */
158typedef QEMUFWDMADESC *PQEMUFWDMADESC;
159/** Pointer to a const QEMU firmware config DMA descriptor. */
160typedef const QEMUFWDMADESC *PCQEMUFWDMADESC;
161
162
163/** Pointer to a const configuration item descriptor. */
164typedef const struct QEMUFWCFGITEM *PCQEMUFWCFGITEM;
165
166/**
167 * QEMU firmware config instance data structure.
168 */
169typedef struct DEVQEMUFWCFG
170{
171 /** Pointer back to the device instance. */
172 PPDMDEVINS pDevIns;
173 /** The configuration handle. */
174 PCFGMNODE pCfg;
175 /** Pointer to the currently selected item. */
176 PCQEMUFWCFGITEM pCfgItem;
177 /** Offset of the next byte to read from the start of the data item. */
178 uint32_t offCfgItemNext;
179 /** How many bytes are left for transfer. */
180 uint32_t cbCfgItemLeft;
181 /** Version register. */
182 uint32_t u32Version;
183 /** Guest physical address of the DMA descriptor. */
184 RTGCPHYS GCPhysDma;
185
186 /** Scratch buffer for config item specific data. */
187 union
188 {
189 uint8_t u8;
190 uint16_t u16;
191 uint32_t u32;
192 uint64_t u64;
193 /** VFS file handle. */
194 RTVFSFILE hVfsFile;
195 /** Byte view. */
196 uint8_t ab[8];
197 } u;
198} DEVQEMUFWCFG;
199/** Pointer to the QEMU firmware config device instance. */
200typedef DEVQEMUFWCFG *PDEVQEMUFWCFG;
201
202
203/**
204 * A supported configuration item descriptor.
205 */
206typedef struct QEMUFWCFGITEM
207{
208 /** The config tiem value. */
209 uint16_t uCfgItem;
210 /** Name of the item. */
211 const char *pszItem;
212 /** Optional CFGM key to lookup the content. */
213 const char *pszCfgmKey;
214 /**
215 * Setup callback for when the guest writes the selector.
216 *
217 * @returns VBox status code.
218 * @param pThis The QEMU fw config device instance.
219 * @param pItem Pointer to the selected item.
220 * @param pcbItem Where to store the size of the item on success.
221 */
222 DECLCALLBACKMEMBER(int, pfnSetup, (PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem));
223 /**
224 * Read callback to return the data.
225 *
226 * @returns VBox status code.
227 * @param pThis The QEMU fw config device instance.
228 * @param pItem Pointer to the selected item.
229 * @param off Where to start reading from.
230 * @param pvBuf Where to store the read data.
231 * @param cbToRead How much to read.
232 * @param pcbRead Where to store the amount of bytes read.
233 */
234 DECLCALLBACKMEMBER(int, pfnRead, (PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
235 uint32_t cbToRead, uint32_t *pcbRead));
236
237 /**
238 * Cleans up any allocated resources when the item is de-selected.
239 *
240 * @returns nothing.
241 * @param pThis The QEMU fw config device instance.
242 * @param pItem Pointer to the selected item.
243 */
244 DECLCALLBACKMEMBER(void, pfnCleanup, (PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem));
245} QEMUFWCFGITEM;
246/** Pointer to a configuration item descriptor. */
247typedef QEMUFWCFGITEM *PQEMUFWCFGITEM;
248
249
250
251/**
252 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the signature configuration item.}
253 */
254static DECLCALLBACK(int) qemuFwCfgR3SetupSignature(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
255{
256 RT_NOREF(pThis, pItem);
257 uint8_t abSig[] = { 'Q', 'E', 'M', 'U' };
258 memcpy(&pThis->u.ab[0], &abSig[0], sizeof(abSig));
259 *pcbItem = sizeof(abSig);
260 return VINF_SUCCESS;
261}
262
263
264/**
265 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the version configuration item.}
266 */
267static DECLCALLBACK(int) qemuFwCfgR3SetupVersion(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
268{
269 RT_NOREF(pThis, pItem);
270 memcpy(&pThis->u.ab[0], &pThis->u32Version, sizeof(pThis->u32Version));
271 *pcbItem = sizeof(pThis->u32Version);
272 return VINF_SUCCESS;
273}
274
275
276/**
277 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the file directory configuration item.}
278 */
279static DECLCALLBACK(int) qemuFwCfgR3SetupFileDir(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
280{
281 RT_NOREF(pThis, pItem);
282 memset(&pThis->u.ab[0], 0, sizeof(uint32_t)); /** @todo Implement */
283 *pcbItem = sizeof(uint32_t);
284 return VINF_SUCCESS;
285}
286
287
288/**
289 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a VFS file type configuration item.}
290 */
291static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFileSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
292{
293 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
294
295 /* Query the path from the CFGM key. */
296 char *pszFilePath = NULL;
297 int rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
298 if (RT_SUCCESS(rc))
299 {
300 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
301 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
302 if (RT_SUCCESS(rc))
303 {
304 uint64_t cbFile = 0;
305 rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
306 if (RT_SUCCESS(rc))
307 {
308 if (cbFile < _4G)
309 {
310 pThis->u.u32 = (uint32_t)cbFile;
311 *pcbItem = sizeof(uint32_t);
312 }
313 else
314 {
315 rc = VERR_BUFFER_OVERFLOW;
316 LogRel(("QemuFwCfg: File \"%s\" exceeds 4G limit (%llu)\n", pszFilePath, cbFile));
317 }
318 }
319 else
320 LogRel(("QemuFwCfg: Failed to query file size from \"%s\" -> %Rrc\n", pszFilePath, rc));
321 RTVfsFileRelease(hVfsFile);
322 }
323 else
324 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
325 MMR3HeapFree(pszFilePath);
326 }
327 else
328 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
329
330 return rc;
331}
332
333
334/**
335 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for the size config item belonging to a string type configuration item.}
336 */
337static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStrSz(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
338{
339 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
340
341 /* Query the string from the CFGM key. */
342 char sz[_4K];
343 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
344 if (RT_SUCCESS(rc))
345 {
346 pThis->u.u32 = (uint32_t)strlen(&sz[0]) + 1;
347 *pcbItem = sizeof(uint32_t);
348 }
349 else
350 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
351
352 return rc;
353}
354
355
356/**
357 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a string type configuration item gathered from CFGM.}
358 */
359static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
360{
361 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
362
363 /* Query the string from the CFGM key. */
364 char sz[_4K];
365 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
366 if (RT_SUCCESS(rc))
367 *pcbItem = (uint32_t)strlen(&sz[0]) + 1;
368 else
369 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
370
371 return rc;
372}
373
374
375/**
376 * @interface_method_impl{QEMUFWCFGITEM,pfnSetup, Sets up the data for a VFS file type configuration item.}
377 */
378static DECLCALLBACK(int) qemuFwCfgR3SetupCfgmFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t *pcbItem)
379{
380 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
381
382 /* Query the path from the CFGM key. */
383 char *pszFilePath = NULL;
384 int rc = pHlp->pfnCFGMQueryStringAlloc(pThis->pCfg, pItem->pszCfgmKey, &pszFilePath);
385 if (RT_SUCCESS(rc))
386 {
387 rc = RTVfsFileOpenNormal(pszFilePath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &pThis->u.hVfsFile);
388 if (RT_SUCCESS(rc))
389 {
390 uint64_t cbFile = 0;
391 rc = RTVfsFileQuerySize(pThis->u.hVfsFile, &cbFile);
392 if (RT_SUCCESS(rc))
393 {
394 if (cbFile < _4G)
395 *pcbItem = (uint32_t)cbFile;
396 else
397 {
398 rc = VERR_BUFFER_OVERFLOW;
399 LogRel(("QemuFwCfg: File \"%s\" exceeds 4G limit (%llu)\n", pszFilePath, cbFile));
400 }
401 }
402 else
403 LogRel(("QemuFwCfg: Failed to query file size from \"%s\" -> %Rrc\n", pszFilePath, rc));
404 }
405 else
406 LogRel(("QemuFwCfg: Failed to open file \"%s\" -> %Rrc\n", pszFilePath, rc));
407 MMR3HeapFree(pszFilePath);
408 }
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,pfnRead, Reads data from a configuration item having its data stored in the scratch buffer.}
418 */
419static DECLCALLBACK(int) qemuFwCfgR3ReadSimple(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
420 uint32_t cbToRead, uint32_t *pcbRead)
421{
422 RT_NOREF(pThis, pItem);
423 memcpy(pvBuf, &pThis->u.ab[off], cbToRead);
424 *pcbRead = cbToRead;
425 return VINF_SUCCESS;
426}
427
428
429/**
430 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads data from a VFS file type configuration item.}
431 */
432static DECLCALLBACK(int) qemuFwCfgR3ReadVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
433 uint32_t cbToRead, uint32_t *pcbRead)
434{
435 RT_NOREF(pItem);
436 size_t cbRead = 0;
437 int rc = RTVfsFileReadAt(pThis->u.hVfsFile, off, pvBuf, cbToRead, &cbRead);
438 if (RT_SUCCESS(rc))
439 *pcbRead = (uint32_t)cbRead;
440
441 return rc;
442}
443
444
445/**
446 * @interface_method_impl{QEMUFWCFGITEM,pfnRead, Reads a string item gathered from CFGM.}
447 */
448static DECLCALLBACK(int) qemuFwCfgR3ReadStr(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem, uint32_t off, void *pvBuf,
449 uint32_t cbToRead, uint32_t *pcbRead)
450{
451 PCPDMDEVHLPR3 pHlp = pThis->pDevIns->pHlpR3;
452
453 /* Query the string from the CFGM key. */
454 char sz[_4K];
455 int rc = pHlp->pfnCFGMQueryString(pThis->pCfg, pItem->pszCfgmKey, &sz[0], sizeof(sz));
456 if (RT_SUCCESS(rc))
457 {
458 uint32_t cch = (uint32_t)strlen(sz) + 1;
459 if (off < cch)
460 {
461 uint32_t cbRead = RT_MIN(cbToRead, off - cch);
462 memcpy(pvBuf, &sz[off], cbRead);
463 *pcbRead = cbRead;
464 }
465 else
466 rc = VERR_BUFFER_OVERFLOW;
467 }
468 else
469 LogRel(("QemuFwCfg: Failed to query \"%s\" -> %Rrc\n", pItem->pszCfgmKey, rc));
470
471 return rc;
472}
473
474
475/**
476 * @interface_method_impl{QEMUFWCFGITEM,pfnCleanup, Cleans up a VFS file type configuration item.}
477 */
478static DECLCALLBACK(void) qemuFwCfgR3CleanupVfsFile(PDEVQEMUFWCFG pThis, PCQEMUFWCFGITEM pItem)
479{
480 RT_NOREF(pItem);
481 RTVfsFileRelease(pThis->u.hVfsFile);
482 pThis->u.hVfsFile = NIL_RTVFSFILE;
483}
484
485
486/**
487 * Supported config items.
488 */
489static const QEMUFWCFGITEM g_aQemuFwCfgItems[] =
490{
491 /** u16Selector pszItem pszCfgmKey pfnSetup pfnRead pfnCleanup */
492 { QEMU_FW_CFG_ITEM_SIGNATURE, "Signature", NULL, qemuFwCfgR3SetupSignature, qemuFwCfgR3ReadSimple, NULL },
493 { QEMU_FW_CFG_ITEM_VERSION, "Version", NULL, qemuFwCfgR3SetupVersion, qemuFwCfgR3ReadSimple, NULL },
494 { QEMU_FW_CFG_ITEM_KERNEL_SIZE, "KrnlSz", "KernelImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL },
495 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "KrnlDat", "KernelImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, qemuFwCfgR3CleanupVfsFile },
496 { QEMU_FW_CFG_ITEM_INITRD_SIZE, "InitrdSz", "InitrdImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL },
497 { QEMU_FW_CFG_ITEM_KERNEL_DATA, "InitrdDat", "InitrdImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, qemuFwCfgR3CleanupVfsFile },
498 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_SIZE, "SetupSz", "SetupImage", qemuFwCfgR3SetupCfgmFileSz, qemuFwCfgR3ReadSimple, NULL },
499 { QEMU_FW_CFG_ITEM_KERNEL_SETUP_DATA, "SetupDat", "SetupImage", qemuFwCfgR3SetupCfgmFile, qemuFwCfgR3ReadVfsFile, qemuFwCfgR3CleanupVfsFile },
500 { QEMU_FW_CFG_ITEM_CMD_LINE_SIZE, "CmdLineSz", "CmdLine", qemuFwCfgR3SetupCfgmStrSz, qemuFwCfgR3ReadSimple, NULL },
501 { QEMU_FW_CFG_ITEM_CMD_LINE_DATA, "CmdLineDat", "CmdLine", qemuFwCfgR3SetupCfgmStr, qemuFwCfgR3ReadStr, NULL },
502 { QEMU_FW_CFG_ITEM_FILE_DIR, "FileDir", NULL, qemuFwCfgR3SetupFileDir, qemuFwCfgR3ReadSimple, NULL }
503};
504
505
506/**
507 * Resets the currently selected item.
508 *
509 * @returns nothing.
510 * @param pThis The QEMU fw config device instance.
511 */
512static void qemuFwCfgR3ItemReset(PDEVQEMUFWCFG pThis)
513{
514 if ( pThis->pCfgItem
515 && pThis->pCfgItem->pfnCleanup)
516 pThis->pCfgItem->pfnCleanup(pThis, pThis->pCfgItem);
517
518 pThis->pCfgItem = NULL;
519 pThis->offCfgItemNext = 0;
520 pThis->cbCfgItemLeft = 0;
521}
522
523
524/**
525 * Selects the given config item.
526 *
527 * @returns VBox status code.
528 * @param pThis The QEMU fw config device instance.
529 * @param uCfgItem The configuration item to select.
530 */
531static int qemuFwCfgItemSelect(PDEVQEMUFWCFG pThis, uint16_t uCfgItem)
532{
533 qemuFwCfgR3ItemReset(pThis);
534
535 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQemuFwCfgItems); i++)
536 {
537 PCQEMUFWCFGITEM pCfgItem = &g_aQemuFwCfgItems[i];
538
539 if (pCfgItem->uCfgItem == uCfgItem)
540 {
541 uint32_t cbItem = 0;
542 int rc = pCfgItem->pfnSetup(pThis, pCfgItem, &cbItem);
543 if (RT_SUCCESS(rc))
544 {
545 pThis->pCfgItem = pCfgItem;
546 pThis->cbCfgItemLeft = cbItem;
547 return VINF_SUCCESS;
548 }
549
550 return rc;
551 }
552 }
553
554 return VERR_NOT_FOUND;
555}
556
557
558/**
559 * Processes a DMA transfer.
560 *
561 * @returns nothing.
562 * @param pThis The QEMU fw config device instance.
563 * @param GCPhysDma The guest physical address of the DMA descriptor.
564 */
565static void qemuFwCfgDmaXfer(PDEVQEMUFWCFG pThis, RTGCPHYS GCPhysDma)
566{
567 QEMUFWDMADESC DmaDesc; RT_ZERO(DmaDesc);
568
569 LogFlowFunc(("pThis=%p GCPhysDma=%RGp\n", pThis, GCPhysDma));
570
571 PDMDevHlpPhysReadMeta(pThis->pDevIns, GCPhysDma, &DmaDesc, sizeof(DmaDesc));
572
573 /* Convert from big endianess to host endianess. */
574 DmaDesc.u32Ctrl = RT_BE2H_U32(DmaDesc.u32Ctrl);
575 DmaDesc.u32Length = RT_BE2H_U32(DmaDesc.u32Length);
576 DmaDesc.u64GCPhysBuf = RT_BE2H_U64(DmaDesc.u64GCPhysBuf);
577
578 LogFlowFunc(("u32Ctrl=%#x u32Length=%u u64GCPhysBuf=%llx\n",
579 DmaDesc.u32Ctrl, DmaDesc.u32Length, DmaDesc.u64GCPhysBuf));
580
581 /* If the select bit is set a select is performed. */
582 int rc = VINF_SUCCESS;
583 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_SELECT)
584 rc = qemuFwCfgItemSelect(pThis, QEMU_FW_CFG_DMA_GET_CFG_ITEM(DmaDesc.u32Ctrl));
585
586 if (RT_SUCCESS(rc))
587 {
588 /* We don't support any writes right now. */
589 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_WRITE)
590 rc = VERR_INVALID_PARAMETER;
591 else if ( !pThis->pCfgItem
592 || !pThis->cbCfgItemLeft)
593 {
594 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
595 {
596 /* Item is not supported, just zero out the indicated area. */
597 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
598 uint32_t cbLeft = DmaDesc.u32Length;
599
600 while ( RT_SUCCESS(rc)
601 && cbLeft)
602 {
603 uint32_t cbZero = RT_MIN(_64K, cbLeft);
604
605 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &g_abRTZero64K[0], cbZero);
606
607 cbLeft -= cbZero;
608 GCPhysCur += cbZero;
609 }
610 }
611 /* else: Assume Skip */
612 }
613 else
614 {
615 /* Read or skip. */
616 RTGCPHYS GCPhysCur = DmaDesc.u64GCPhysBuf;
617 uint32_t cbLeft = RT_MIN(DmaDesc.u32Length, pThis->cbCfgItemLeft);
618
619 while ( RT_SUCCESS(rc)
620 && cbLeft)
621 {
622 uint8_t abTmp[_1K];
623 uint32_t cbThisRead = RT_MIN(sizeof(abTmp), cbLeft);
624 uint32_t cbRead;
625
626 rc = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &abTmp[0],
627 cbThisRead, &cbRead);
628 if (RT_SUCCESS(rc))
629 {
630 if (DmaDesc.u32Ctrl & QEMU_FW_CFG_DMA_READ)
631 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysCur, &abTmp[0], cbRead);
632 /* else: Assume Skip */
633
634 cbLeft -= cbRead;
635 GCPhysCur += cbRead;
636
637 pThis->offCfgItemNext += cbRead;
638 pThis->cbCfgItemLeft -= cbRead;
639 }
640 }
641 }
642 }
643
644 LogFlowFunc(("pThis=%p GCPhysDma=%RGp -> %Rrc\n", pThis, GCPhysDma, rc));
645
646 /* Write back the control field. */
647 uint32_t u32Resp = RT_SUCCESS(rc) ? 0 : RT_H2BE_U32(QEMU_FW_CFG_DMA_ERROR);
648 PDMDevHlpPhysWriteMeta(pThis->pDevIns, GCPhysDma, &u32Resp, sizeof(u32Resp));
649}
650
651
652/**
653 * @callback_method_impl{FNIOMIOPORTNEWOUT, QEMU firmware configuration write.}
654 */
655static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
656{
657 int rc = VINF_SUCCESS;
658 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
659 NOREF(pvUser);
660
661 LogFlowFunc(("offPort=%RTiop u32=%#x cb=%u\n", offPort, u32, cb));
662
663 switch (offPort)
664 {
665 case QEMU_FW_CFG_OFF_SELECTOR:
666 {
667 if (cb == 2)
668 qemuFwCfgItemSelect(pThis, (uint16_t)u32);
669 break;
670 }
671 case QEMU_FW_CFG_OFF_DATA: /* Readonly, ignore */
672 break;
673 case QEMU_FW_CFG_OFF_DMA_HIGH:
674 {
675 if (cb == 4)
676 pThis->GCPhysDma = ((RTGCPHYS)RT_BE2H_U32(u32)) << 32;
677 break;
678 }
679 case QEMU_FW_CFG_OFF_DMA_LOW:
680 {
681 if (cb == 4)
682 {
683 pThis->GCPhysDma |= ((RTGCPHYS)RT_BE2H_U32(u32));
684 qemuFwCfgDmaXfer(pThis, pThis->GCPhysDma);
685 pThis->GCPhysDma = 0;
686 }
687 break;
688 }
689 default:
690 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", offPort, cb, u32);
691 break;
692 }
693
694 LogFlowFunc((" -> rc=%Rrc\n", rc));
695 return rc;
696}
697
698
699/**
700 * @callback_method_impl{FNIOMIOPORTNEWIN, QEMU firmware configuration read.}
701 */
702static DECLCALLBACK(VBOXSTRICTRC) qemuFwCfgIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
703{
704 int rc = VINF_SUCCESS;
705 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
706 NOREF(pvUser);
707
708 *pu32 = 0;
709
710 LogFlowFunc(("offPort=%RTiop cb=%u\n", offPort, cb));
711
712 switch (offPort)
713 {
714 /* Selector (Writeonly, ignore). */
715 case QEMU_FW_CFG_OFF_SELECTOR:
716 break;
717 case QEMU_FW_CFG_OFF_DATA:
718 {
719 if (cb == 1)
720 {
721 if ( pThis->cbCfgItemLeft
722 && pThis->pCfgItem)
723 {
724 uint8_t bRead = 0;
725 uint32_t cbRead = 0;
726 int rc2 = pThis->pCfgItem->pfnRead(pThis, pThis->pCfgItem, pThis->offCfgItemNext, &bRead,
727 sizeof(bRead), &cbRead);
728 if ( RT_SUCCESS(rc2)
729 && cbRead == sizeof(bRead))
730 {
731 pThis->offCfgItemNext += cbRead;
732 pThis->cbCfgItemLeft -= cbRead;
733 *pu32 = bRead;
734 }
735 }
736 }
737 else
738 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
739 break;
740 }
741
742 default:
743 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d\n", offPort, cb);
744 break;
745 }
746
747 LogFlowFunc(("offPort=%RTiop cb=%u -> rc=%Rrc u32=%#x\n", offPort, cb, rc, *pu32));
748
749 return rc;
750}
751
752
753/**
754 * @interface_method_impl{PDMDEVREG,pfnReset}
755 */
756static DECLCALLBACK(void) qemuFwCfgReset(PPDMDEVINS pDevIns)
757{
758 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
759
760 qemuFwCfgR3ItemReset(pThis);
761 pThis->GCPhysDma = 0;
762}
763
764
765/**
766 * @interface_method_impl{PDMDEVREG,pfnDestruct}
767 */
768static DECLCALLBACK(int) qemuFwCfgDestruct(PPDMDEVINS pDevIns)
769{
770 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
771 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
772
773 qemuFwCfgR3ItemReset(pThis);
774 pThis->GCPhysDma = 0;
775
776 return VINF_SUCCESS;
777}
778
779
780/**
781 * @interface_method_impl{PDMDEVREG,pfnConstruct}
782 */
783static DECLCALLBACK(int) qemuFwCfgConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
784{
785 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
786 PDEVQEMUFWCFG pThis = PDMDEVINS_2_DATA(pDevIns, PDEVQEMUFWCFG);
787 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
788 Assert(iInstance == 0); RT_NOREF(iInstance);
789
790 /*
791 * Validate configuration.
792 */
793 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DmaEnabled"
794 "|KernelImage"
795 "|InitrdImage"
796 "|SetupImage"
797 "|CmdLine",
798 "");
799
800 bool fDmaEnabled = false;
801 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DmaEnabled", &fDmaEnabled, false);
802 if (RT_FAILURE(rc))
803 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmaEnabled\""));
804
805 /*
806 * Init the data.
807 */
808 pThis->pDevIns = pDevIns;
809 pThis->pCfg = pCfg;
810 pThis->u32Version = QEMU_FW_CFG_VERSION_LEGACY | (fDmaEnabled ? QEMU_FW_CFG_VERSION_DMA : 0);
811 pThis->GCPhysDma = 0;
812
813 qemuFwCfgR3ItemReset(pThis);
814
815 /*
816 * Register I/O Ports
817 */
818 IOMIOPORTHANDLE hIoPorts;
819 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, QEMU_FW_CFG_IO_PORT_START, QEMU_FW_CFG_IO_PORT_SIZE, 0 /*fFlags*/,
820 qemuFwCfgIoPortWrite, qemuFwCfgIoPortRead,
821 "QEMU firmware configuration", NULL /*paExtDescs*/, &hIoPorts);
822 AssertRCReturn(rc, rc);
823
824 return VINF_SUCCESS;
825}
826
827
828/**
829 * The device registration structure.
830 */
831const PDMDEVREG g_DeviceQemuFwCfg =
832{
833 /* .u32Version = */ PDM_DEVREG_VERSION,
834 /* .uReserved0 = */ 0,
835 /* .szName = */ "qemu-fw-cfg",
836 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
837 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
838 /* .cMaxInstances = */ 1,
839 /* .uSharedVersion = */ 42,
840 /* .cbInstanceShared = */ sizeof(DEVQEMUFWCFG),
841 /* .cbInstanceCC = */ 0,
842 /* .cbInstanceRC = */ 0,
843 /* .cMaxPciDevices = */ 0,
844 /* .cMaxMsixVectors = */ 0,
845 /* .pszDescription = */ "QEMU Firmware Config compatible device",
846#if defined(IN_RING3)
847 /* .pszRCMod = */ "",
848 /* .pszR0Mod = */ "",
849 /* .pfnConstruct = */ qemuFwCfgConstruct,
850 /* .pfnDestruct = */ qemuFwCfgDestruct,
851 /* .pfnRelocate = */ NULL,
852 /* .pfnMemSetup = */ NULL,
853 /* .pfnPowerOn = */ NULL,
854 /* .pfnReset = */ qemuFwCfgReset,
855 /* .pfnSuspend = */ NULL,
856 /* .pfnResume = */ NULL,
857 /* .pfnAttach = */ NULL,
858 /* .pfnDetach = */ NULL,
859 /* .pfnQueryInterface = */ NULL,
860 /* .pfnInitComplete = */ NULL,
861 /* .pfnPowerOff = */ NULL,
862 /* .pfnSoftReset = */ NULL,
863 /* .pfnReserved0 = */ NULL,
864 /* .pfnReserved1 = */ NULL,
865 /* .pfnReserved2 = */ NULL,
866 /* .pfnReserved3 = */ NULL,
867 /* .pfnReserved4 = */ NULL,
868 /* .pfnReserved5 = */ NULL,
869 /* .pfnReserved6 = */ NULL,
870 /* .pfnReserved7 = */ NULL,
871#elif defined(IN_RING0)
872 /* .pfnEarlyConstruct = */ NULL,
873 /* .pfnConstruct = */ NULL,
874 /* .pfnDestruct = */ NULL,
875 /* .pfnFinalDestruct = */ NULL,
876 /* .pfnRequest = */ NULL,
877 /* .pfnReserved0 = */ NULL,
878 /* .pfnReserved1 = */ NULL,
879 /* .pfnReserved2 = */ NULL,
880 /* .pfnReserved3 = */ NULL,
881 /* .pfnReserved4 = */ NULL,
882 /* .pfnReserved5 = */ NULL,
883 /* .pfnReserved6 = */ NULL,
884 /* .pfnReserved7 = */ NULL,
885#elif defined(IN_RC)
886 /* .pfnConstruct = */ NULL,
887 /* .pfnReserved0 = */ NULL,
888 /* .pfnReserved1 = */ NULL,
889 /* .pfnReserved2 = */ NULL,
890 /* .pfnReserved3 = */ NULL,
891 /* .pfnReserved4 = */ NULL,
892 /* .pfnReserved5 = */ NULL,
893 /* .pfnReserved6 = */ NULL,
894 /* .pfnReserved7 = */ NULL,
895#else
896# error "Not in IN_RING3, IN_RING0 or IN_RC!"
897#endif
898 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
899};
900
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