VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/DevEFI-armv8.cpp@ 100039

Last change on this file since 100039 was 100039, checked in by vboxsync, 12 months ago

Devices/DevEFI-armv8.cpp: Implement ability to inject the FDT for the VM into the guest RAM, bugref:10400

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: DevEFI-armv8.cpp 100039 2023-06-01 18:19:52Z vboxsync $ */
2/** @file
3 * DevEFI - EFI <-> VirtualBox Integration Framework.
4 */
5
6/*
7 * Copyright (C) 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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_EFI
33
34#include <VBox/vmm/pdmdev.h>
35#include <VBox/vmm/pgm.h>
36#include <VBox/vmm/cpum.h>
37#include <VBox/vmm/mm.h>
38#include <VBox/log.h>
39#include <VBox/err.h>
40#include <VBox/param.h>
41#include <VBox/vmm/dbgf.h>
42
43#include <iprt/asm.h>
44#include <iprt/assert.h>
45#include <iprt/ctype.h>
46#include <iprt/file.h>
47#include <iprt/mem.h>
48#include <iprt/string.h>
49#include <iprt/uuid.h>
50#include <iprt/path.h>
51#include <iprt/string.h>
52
53#include "DevEFI.h"
54#include "VBoxDD.h"
55#include "VBoxDD2.h"
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/**
62 * The EFI device shared state structure.
63 */
64typedef struct DEVEFI
65{
66 uint32_t uEmpty;
67} DEVEFI;
68/** Pointer to the shared EFI state. */
69typedef DEVEFI *PDEVEFI;
70
71
72/**
73 * The EFI device state structure for ring-3.
74 */
75typedef struct DEVEFIR3
76{
77 /** Pointer back to the device instance. */
78 PPDMDEVINS pDevIns;
79
80 /** The system EFI ROM data. */
81 uint8_t const *pu8EfiRom;
82 /** The system EFI ROM data pointer to be passed to RTFileReadAllFree. */
83 uint8_t *pu8EfiRomFree;
84 /** The size of the system EFI ROM. */
85 uint64_t cbEfiRom;
86 /** Offset into the actual ROM within EFI FW volume. */
87 uint64_t offEfiRom;
88 /** The name of the EFI ROM file. */
89 char *pszEfiRomFile;
90 /** EFI firmware physical load address. */
91 RTGCPHYS GCPhysLoadAddress;
92 /** The FDT load address, RTGCPHYS_MAX if not configured to be loaded. */
93 RTGCPHYS GCPhysFdtAddress;
94 /** The FDT id used to load from the resource store driver below. */
95 char *pszFdtId;
96
97 /**
98 * Resource port - LUN\#0.
99 */
100 struct
101 {
102 /** The base interface we provide the resource driver. */
103 PDMIBASE IBase;
104 /** The resource driver base interface. */
105 PPDMIBASE pDrvBase;
106 /** The VFS interface of the driver below for resource state loading and storing. */
107 PPDMIVFSCONNECTOR pDrvVfs;
108 } Lun0;
109} DEVEFIR3;
110/** Pointer to the ring-3 EFI state. */
111typedef DEVEFIR3 *PDEVEFIR3;
112
113
114/**
115 * The EFI device state structure for ring-0.
116 */
117typedef struct DEVEFIR0
118{
119 uint32_t uEmpty;
120} DEVEFIR0;
121/** Pointer to the ring-0 EFI state. */
122typedef DEVEFIR0 *PDEVEFIR0;
123
124
125/**
126 * The EFI device state structure for raw-mode.
127 */
128typedef struct DEVEFIRC
129{
130 uint32_t uEmpty;
131} DEVEFIRC;
132/** Pointer to the raw-mode EFI state. */
133typedef DEVEFIRC *PDEVEFIRC;
134
135
136/** @typedef DEVEFICC
137 * The instance data for the current context. */
138/** @typedef PDEVEFICC
139 * Pointer to the instance data for the current context. */
140#ifdef IN_RING3
141typedef DEVEFIR3 DEVEFICC;
142typedef PDEVEFIR3 PDEVEFICC;
143#elif defined(IN_RING0)
144typedef DEVEFIR0 DEVEFICC;
145typedef PDEVEFIR0 PDEVEFICC;
146#elif defined(IN_RC)
147typedef DEVEFIRC DEVEFICC;
148typedef PDEVEFIRC PDEVEFICC;
149#else
150# error "Not IN_RING3, IN_RING0 or IN_RC"
151#endif
152
153
154/*********************************************************************************************************************************
155* Defined Constants And Macros *
156*********************************************************************************************************************************/
157/** The saved state version. */
158#define EFI_SSM_VERSION 1
159
160
161/*********************************************************************************************************************************
162* Global Variables *
163*********************************************************************************************************************************/
164#ifdef IN_RING3
165
166# ifdef VBOX_WITH_EFI_IN_DD2
167/** Special file name value for indicating the 32-bit built-in EFI firmware. */
168static const char g_szEfiBuiltinAArch32[] = "VBoxEFIAArch32.fd";
169/** Special file name value for indicating the 64-bit built-in EFI firmware. */
170static const char g_szEfiBuiltinAArch64[] = "VBoxEFIAArch64.fd";
171# endif
172#endif /* IN_RING3 */
173
174
175#ifdef IN_RING3
176
177/**
178 * @copydoc(PDMIBASE::pfnQueryInterface)
179 */
180static DECLCALLBACK(void *) devR3EfiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
181{
182 LogFlowFunc(("ENTER: pIBase=%p pszIID=%p\n", pInterface, pszIID));
183 PDEVEFIR3 pThisCC = RT_FROM_MEMBER(pInterface, DEVEFIR3, Lun0.IBase);
184
185 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->Lun0.IBase);
186 return NULL;
187}
188
189
190/**
191 * @interface_method_impl{PDMDEVREG,pfnReset}
192 */
193static DECLCALLBACK(void) efiR3Reset(PPDMDEVINS pDevIns)
194{
195 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
196 LogFlow(("efiR3Reset\n"));
197
198 if (pThisCC->GCPhysFdtAddress != RTGCPHYS_MAX)
199 {
200 AssertPtr(pThisCC->Lun0.pDrvVfs);
201
202 uint64_t cbFdt = 0;
203 int rc = pThisCC->Lun0.pDrvVfs->pfnQuerySize(pThisCC->Lun0.pDrvVfs, pThisCC->pszFdtId, pThisCC->pszFdtId, &cbFdt);
204 if (RT_SUCCESS(rc))
205 {
206 /** @todo Need to add a proper read callback to avoid allocating temporary memory. */
207 void *pvFdt = RTMemAllocZ(cbFdt);
208 if (pvFdt)
209 {
210 rc = pThisCC->Lun0.pDrvVfs->pfnReadAll(pThisCC->Lun0.pDrvVfs, pThisCC->pszFdtId, pThisCC->pszFdtId, pvFdt, cbFdt);
211 if (RT_SUCCESS(rc))
212 rc = PDMDevHlpPhysWrite(pDevIns, pThisCC->GCPhysFdtAddress, pvFdt, cbFdt);
213
214 RTMemFree(pvFdt);
215 }
216 else
217 rc = VERR_NO_MEMORY;
218 }
219 AssertLogRelRC(rc);
220 }
221}
222
223
224/**
225 * Destruct a device instance.
226 *
227 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
228 * resources can be freed correctly.
229 *
230 * @param pDevIns The device instance data.
231 */
232static DECLCALLBACK(int) efiR3Destruct(PPDMDEVINS pDevIns)
233{
234 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
235 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
236
237 if (pThisCC->pu8EfiRomFree)
238 {
239 RTFileReadAllFree(pThisCC->pu8EfiRomFree, (size_t)pThisCC->cbEfiRom + pThisCC->offEfiRom);
240 pThisCC->pu8EfiRomFree = NULL;
241 }
242
243 /*
244 * Free MM heap pointers (waste of time, but whatever).
245 */
246 if (pThisCC->pszEfiRomFile)
247 {
248 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszEfiRomFile);
249 pThisCC->pszEfiRomFile = NULL;
250 }
251
252 if (pThisCC->pszFdtId)
253 {
254 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszFdtId);
255 pThisCC->pszFdtId = NULL;
256 }
257
258 return VINF_SUCCESS;
259}
260
261
262/**
263 * Load EFI ROM file into the memory.
264 *
265 * @returns VBox status code.
266 * @param pDevIns The device instance.
267 * @param pThisCC The device state for the current context.
268 * @param pCfg Configuration node handle for the device.
269 */
270static int efiR3LoadRom(PPDMDEVINS pDevIns, PDEVEFIR3 pThisCC, PCFGMNODE pCfg)
271{
272 RT_NOREF(pCfg);
273
274 /*
275 * Read the entire firmware volume into memory.
276 */
277 int rc;
278#ifdef VBOX_WITH_EFI_IN_DD2
279 if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltinAArch32) == 0)
280 {
281 pThisCC->pu8EfiRomFree = NULL;
282 pThisCC->pu8EfiRom = g_abEfiFirmwareAArch32;
283 pThisCC->cbEfiRom = g_cbEfiFirmwareAArch32;
284 }
285 else if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltinAArch64) == 0)
286 {
287 pThisCC->pu8EfiRomFree = NULL;
288 pThisCC->pu8EfiRom = g_abEfiFirmwareAArch64;
289 pThisCC->cbEfiRom = g_cbEfiFirmwareAArch64;
290 }
291 else
292#endif
293 {
294 void *pvFile;
295 size_t cbFile;
296 rc = RTFileReadAllEx(pThisCC->pszEfiRomFile,
297 0 /*off*/,
298 RTFOFF_MAX /*cbMax*/,
299 RTFILE_RDALL_O_DENY_WRITE,
300 &pvFile,
301 &cbFile);
302 if (RT_FAILURE(rc))
303 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
304 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
305 pThisCC->pszEfiRomFile, rc);
306 pThisCC->pu8EfiRomFree = (uint8_t *)pvFile;
307 pThisCC->pu8EfiRom = (uint8_t *)pvFile;
308 pThisCC->cbEfiRom = cbFile;
309 }
310
311 rc = PDMDevHlpROMRegister(pDevIns, pThisCC->GCPhysLoadAddress, pThisCC->cbEfiRom, pThisCC->pu8EfiRom, pThisCC->cbEfiRom,
312 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Firmware Volume");
313 AssertRCReturn(rc, rc);
314
315 return VINF_SUCCESS;
316}
317
318
319/**
320 * @interface_method_impl{PDMDEVREG,pfnConstruct}
321 */
322static DECLCALLBACK(int) efiR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
323{
324 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
325 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
326 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
327 int rc;
328
329 RT_NOREF(iInstance);
330 Assert(iInstance == 0);
331
332 /*
333 * Initalize the basic variables so that the destructor always works.
334 */
335 pThisCC->pDevIns = pDevIns;
336 pThisCC->pszFdtId = NULL;
337 pThisCC->GCPhysFdtAddress = RTGCPHYS_MAX;
338 pThisCC->Lun0.IBase.pfnQueryInterface = devR3EfiQueryInterface;
339
340 /*
341 * Validate and read the configuration.
342 */
343 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "EfiRom|GCPhysLoadAddress|GCPhysFdtAddress|FdtId", "");
344
345 rc = pHlp->pfnCFGMQueryU64(pCfg, "GCPhysLoadAddress", &pThisCC->GCPhysLoadAddress);
346 if (RT_FAILURE(rc))
347 return PDMDEV_SET_ERROR(pDevIns, rc,
348 N_("Configuration error: Querying \"GCPhysLoadAddress\" as integer failed"));
349
350 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "GCPhysFdtAddress", &pThisCC->GCPhysFdtAddress, RTGCPHYS_MAX);
351 if (RT_FAILURE(rc))
352 return PDMDEV_SET_ERROR(pDevIns, rc,
353 N_("Configuration error: Querying \"GCPhysFdtAddress\" as integer failed"));
354
355 if (pThisCC->GCPhysFdtAddress != RTGCPHYS_MAX)
356 {
357 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FdtId", &pThisCC->pszFdtId);
358 if (RT_FAILURE(rc))
359 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
360 N_("Configuration error: Querying \"FdtId\" as a string failed"));
361 }
362
363 /*
364 * Get the system EFI ROM file name.
365 */
366#ifdef VBOX_WITH_EFI_IN_DD2
367 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "EfiRom", &pThisCC->pszEfiRomFile, g_szEfiBuiltinAArch32);
368 if (RT_FAILURE(rc))
369#else
370 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "EfiRom", &pThisCC->pszEfiRomFile);
371 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
372 {
373 pThisCC->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
374 AssertReturn(pThisCC->pszEfiRomFile, VERR_NO_MEMORY);
375 rc = RTPathAppPrivateArchTop(pThisCC->pszEfiRomFile, RTPATH_MAX);
376 AssertRCReturn(rc, rc);
377 rc = RTPathAppend(pThisCC->pszEfiRomFile, RTPATH_MAX, "VBoxEFIAArch32.fd");
378 AssertRCReturn(rc, rc);
379 }
380 else if (RT_FAILURE(rc))
381#endif
382 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
383 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
384
385 /*
386 * Load firmware volume and thunk ROM.
387 */
388 rc = efiR3LoadRom(pDevIns, pThisCC, pCfg);
389 if (RT_FAILURE(rc))
390 return rc;
391
392 /*
393 * Resource storage.
394 */
395 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->Lun0.IBase, &pThisCC->Lun0.pDrvBase, "ResourceStorage");
396 if (RT_SUCCESS(rc))
397 {
398 pThisCC->Lun0.pDrvVfs = PDMIBASE_QUERY_INTERFACE(pThisCC->Lun0.pDrvBase, PDMIVFSCONNECTOR);
399 if (!pThisCC->Lun0.pDrvVfs)
400 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Resource storage driver is missing VFS interface below"));
401 }
402 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
403 && pThisCC->GCPhysFdtAddress == RTGCPHYS_MAX)
404 rc = VINF_SUCCESS; /* Missing driver is no error condition if no FDT is going to be loaded. */
405 else
406 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach resource Storage driver"));
407
408 efiR3Reset(pDevIns);
409 return VINF_SUCCESS;
410}
411
412#else /* IN_RING3 */
413
414
415/**
416 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
417 */
418static DECLCALLBACK(int) efiRZConstruct(PPDMDEVINS pDevIns)
419{
420 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
421 RT_NOREF(pDevIns);
422
423 return VINF_SUCCESS;
424}
425
426
427#endif /* IN_RING3 */
428
429/**
430 * The device registration structure.
431 */
432const PDMDEVREG g_DeviceEfiArmV8 =
433{
434 /* .u32Version = */ PDM_DEVREG_VERSION,
435 /* .uReserved0 = */ 0,
436 /* .szName = */ "efi-armv8",
437 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
438 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
439 /* .cMaxInstances = */ 1,
440 /* .uSharedVersion = */ 42,
441 /* .cbInstanceShared = */ sizeof(DEVEFI),
442 /* .cbInstanceCC = */ sizeof(DEVEFICC),
443 /* .cbInstanceRC = */ sizeof(DEVEFIRC),
444 /* .cMaxPciDevices = */ 0,
445 /* .cMaxMsixVectors = */ 0,
446 /* .pszDescription = */ "Extensible Firmware Interface Device for ARMv8 platforms.\n",
447#if defined(IN_RING3)
448 /* .pszRCMod = */ "VBoxDDRC.rc",
449 /* .pszR0Mod = */ "VBoxDDR0.r0",
450 /* .pfnConstruct = */ efiR3Construct,
451 /* .pfnDestruct = */ efiR3Destruct,
452 /* .pfnRelocate = */ NULL,
453 /* .pfnMemSetup = */ NULL,
454 /* .pfnPowerOn = */ NULL,
455 /* .pfnReset = */ efiR3Reset,
456 /* .pfnSuspend = */ NULL,
457 /* .pfnResume = */ NULL,
458 /* .pfnAttach = */ NULL,
459 /* .pfnDetach = */ NULL,
460 /* .pfnQueryInterface = */ NULL,
461 /* .pfnInitComplete = */ NULL,
462 /* .pfnPowerOff = */ NULL,
463 /* .pfnSoftReset = */ NULL,
464 /* .pfnReserved0 = */ NULL,
465 /* .pfnReserved1 = */ NULL,
466 /* .pfnReserved2 = */ NULL,
467 /* .pfnReserved3 = */ NULL,
468 /* .pfnReserved4 = */ NULL,
469 /* .pfnReserved5 = */ NULL,
470 /* .pfnReserved6 = */ NULL,
471 /* .pfnReserved7 = */ NULL,
472#elif defined(IN_RING0)
473 /* .pfnEarlyConstruct = */ NULL,
474 /* .pfnConstruct = */ efiRZConstruct,
475 /* .pfnDestruct = */ NULL,
476 /* .pfnFinalDestruct = */ NULL,
477 /* .pfnRequest = */ NULL,
478 /* .pfnReserved0 = */ NULL,
479 /* .pfnReserved1 = */ NULL,
480 /* .pfnReserved2 = */ NULL,
481 /* .pfnReserved3 = */ NULL,
482 /* .pfnReserved4 = */ NULL,
483 /* .pfnReserved5 = */ NULL,
484 /* .pfnReserved6 = */ NULL,
485 /* .pfnReserved7 = */ NULL,
486#elif defined(IN_RC)
487 /* .pfnConstruct = */ efiRZConstruct,
488 /* .pfnReserved0 = */ NULL,
489 /* .pfnReserved1 = */ NULL,
490 /* .pfnReserved2 = */ NULL,
491 /* .pfnReserved3 = */ NULL,
492 /* .pfnReserved4 = */ NULL,
493 /* .pfnReserved5 = */ NULL,
494 /* .pfnReserved6 = */ NULL,
495 /* .pfnReserved7 = */ NULL,
496#else
497# error "Not in IN_RING3, IN_RING0 or IN_RC!"
498#endif
499 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
500};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use