VirtualBox

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

Last change on this file since 97726 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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