VirtualBox

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

Last change on this file was 100116, checked in by vboxsync, 11 months ago

Devices/DevEFI-armv8.cpp: Move the FDT injection to the memory setup phase instead of after construction or reset because the memory gets zeroed _after_ the device was reset so the FDT got overwritten making UEFI fail to come up after a VM reset, 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 100116 2023-06-08 12:38:00Z 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,pfnMemSetup}
192 */
193static DECLCALLBACK(void) efiR3MemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
194{
195 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
196 LogFlow(("efiR3MemSetup\n"));
197
198 RT_NOREF(enmCtx);
199 if (pThisCC->GCPhysFdtAddress != RTGCPHYS_MAX)
200 {
201 AssertPtr(pThisCC->Lun0.pDrvVfs);
202
203 uint64_t cbFdt = 0;
204 int rc = pThisCC->Lun0.pDrvVfs->pfnQuerySize(pThisCC->Lun0.pDrvVfs, pThisCC->pszFdtId, pThisCC->pszFdtId, &cbFdt);
205 if (RT_SUCCESS(rc))
206 {
207 /** @todo Need to add a proper read callback to avoid allocating temporary memory. */
208 void *pvFdt = RTMemAllocZ(cbFdt);
209 if (pvFdt)
210 {
211 rc = pThisCC->Lun0.pDrvVfs->pfnReadAll(pThisCC->Lun0.pDrvVfs, pThisCC->pszFdtId, pThisCC->pszFdtId, pvFdt, cbFdt);
212 if (RT_SUCCESS(rc))
213 rc = PDMDevHlpPhysWrite(pDevIns, pThisCC->GCPhysFdtAddress, pvFdt, cbFdt);
214
215 RTMemFree(pvFdt);
216 }
217 else
218 rc = VERR_NO_MEMORY;
219 }
220 AssertLogRelRC(rc);
221 }
222}
223
224
225/**
226 * Destruct a device instance.
227 *
228 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
229 * resources can be freed correctly.
230 *
231 * @param pDevIns The device instance data.
232 */
233static DECLCALLBACK(int) efiR3Destruct(PPDMDEVINS pDevIns)
234{
235 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
236 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
237
238 if (pThisCC->pu8EfiRomFree)
239 {
240 RTFileReadAllFree(pThisCC->pu8EfiRomFree, (size_t)pThisCC->cbEfiRom + pThisCC->offEfiRom);
241 pThisCC->pu8EfiRomFree = NULL;
242 }
243
244 /*
245 * Free MM heap pointers (waste of time, but whatever).
246 */
247 if (pThisCC->pszEfiRomFile)
248 {
249 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszEfiRomFile);
250 pThisCC->pszEfiRomFile = NULL;
251 }
252
253 if (pThisCC->pszFdtId)
254 {
255 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszFdtId);
256 pThisCC->pszFdtId = NULL;
257 }
258
259 return VINF_SUCCESS;
260}
261
262
263/**
264 * Load EFI ROM file into the memory.
265 *
266 * @returns VBox status code.
267 * @param pDevIns The device instance.
268 * @param pThisCC The device state for the current context.
269 * @param pCfg Configuration node handle for the device.
270 */
271static int efiR3LoadRom(PPDMDEVINS pDevIns, PDEVEFIR3 pThisCC, PCFGMNODE pCfg)
272{
273 RT_NOREF(pCfg);
274
275 /*
276 * Read the entire firmware volume into memory.
277 */
278 int rc;
279#ifdef VBOX_WITH_EFI_IN_DD2
280 if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltinAArch32) == 0)
281 {
282 pThisCC->pu8EfiRomFree = NULL;
283 pThisCC->pu8EfiRom = g_abEfiFirmwareAArch32;
284 pThisCC->cbEfiRom = g_cbEfiFirmwareAArch32;
285 }
286 else if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltinAArch64) == 0)
287 {
288 pThisCC->pu8EfiRomFree = NULL;
289 pThisCC->pu8EfiRom = g_abEfiFirmwareAArch64;
290 pThisCC->cbEfiRom = g_cbEfiFirmwareAArch64;
291 }
292 else
293#endif
294 {
295 void *pvFile;
296 size_t cbFile;
297 rc = RTFileReadAllEx(pThisCC->pszEfiRomFile,
298 0 /*off*/,
299 RTFOFF_MAX /*cbMax*/,
300 RTFILE_RDALL_O_DENY_WRITE,
301 &pvFile,
302 &cbFile);
303 if (RT_FAILURE(rc))
304 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
305 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
306 pThisCC->pszEfiRomFile, rc);
307 pThisCC->pu8EfiRomFree = (uint8_t *)pvFile;
308 pThisCC->pu8EfiRom = (uint8_t *)pvFile;
309 pThisCC->cbEfiRom = cbFile;
310 }
311
312 rc = PDMDevHlpROMRegister(pDevIns, pThisCC->GCPhysLoadAddress, pThisCC->cbEfiRom, pThisCC->pu8EfiRom, pThisCC->cbEfiRom,
313 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Firmware Volume");
314 AssertRCReturn(rc, rc);
315
316 return VINF_SUCCESS;
317}
318
319
320/**
321 * @interface_method_impl{PDMDEVREG,pfnConstruct}
322 */
323static DECLCALLBACK(int) efiR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
324{
325 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
326 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
327 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
328 int rc;
329
330 RT_NOREF(iInstance);
331 Assert(iInstance == 0);
332
333 /*
334 * Initalize the basic variables so that the destructor always works.
335 */
336 pThisCC->pDevIns = pDevIns;
337 pThisCC->pszFdtId = NULL;
338 pThisCC->GCPhysFdtAddress = RTGCPHYS_MAX;
339 pThisCC->Lun0.IBase.pfnQueryInterface = devR3EfiQueryInterface;
340
341 /*
342 * Validate and read the configuration.
343 */
344 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "EfiRom|GCPhysLoadAddress|GCPhysFdtAddress|FdtId", "");
345
346 rc = pHlp->pfnCFGMQueryU64(pCfg, "GCPhysLoadAddress", &pThisCC->GCPhysLoadAddress);
347 if (RT_FAILURE(rc))
348 return PDMDEV_SET_ERROR(pDevIns, rc,
349 N_("Configuration error: Querying \"GCPhysLoadAddress\" as integer failed"));
350
351 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "GCPhysFdtAddress", &pThisCC->GCPhysFdtAddress, RTGCPHYS_MAX);
352 if (RT_FAILURE(rc))
353 return PDMDEV_SET_ERROR(pDevIns, rc,
354 N_("Configuration error: Querying \"GCPhysFdtAddress\" as integer failed"));
355
356 if (pThisCC->GCPhysFdtAddress != RTGCPHYS_MAX)
357 {
358 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FdtId", &pThisCC->pszFdtId);
359 if (RT_FAILURE(rc))
360 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
361 N_("Configuration error: Querying \"FdtId\" as a string failed"));
362 }
363
364 /*
365 * Get the system EFI ROM file name.
366 */
367#ifdef VBOX_WITH_EFI_IN_DD2
368 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "EfiRom", &pThisCC->pszEfiRomFile, g_szEfiBuiltinAArch32);
369 if (RT_FAILURE(rc))
370#else
371 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "EfiRom", &pThisCC->pszEfiRomFile);
372 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
373 {
374 pThisCC->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
375 AssertReturn(pThisCC->pszEfiRomFile, VERR_NO_MEMORY);
376 rc = RTPathAppPrivateArchTop(pThisCC->pszEfiRomFile, RTPATH_MAX);
377 AssertRCReturn(rc, rc);
378 rc = RTPathAppend(pThisCC->pszEfiRomFile, RTPATH_MAX, "VBoxEFIAArch32.fd");
379 AssertRCReturn(rc, rc);
380 }
381 else if (RT_FAILURE(rc))
382#endif
383 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
384 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
385
386 /*
387 * Load firmware volume and thunk ROM.
388 */
389 rc = efiR3LoadRom(pDevIns, pThisCC, pCfg);
390 if (RT_FAILURE(rc))
391 return rc;
392
393 /*
394 * Resource storage.
395 */
396 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->Lun0.IBase, &pThisCC->Lun0.pDrvBase, "ResourceStorage");
397 if (RT_SUCCESS(rc))
398 {
399 pThisCC->Lun0.pDrvVfs = PDMIBASE_QUERY_INTERFACE(pThisCC->Lun0.pDrvBase, PDMIVFSCONNECTOR);
400 if (!pThisCC->Lun0.pDrvVfs)
401 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Resource storage driver is missing VFS interface below"));
402 }
403 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
404 && pThisCC->GCPhysFdtAddress == RTGCPHYS_MAX)
405 rc = VINF_SUCCESS; /* Missing driver is no error condition if no FDT is going to be loaded. */
406 else
407 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach resource Storage driver"));
408
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 = */ efiR3MemSetup,
454 /* .pfnPowerOn = */ NULL,
455 /* .pfnReset = */ NULL,
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