VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/DevEFI.cpp@ 95685

Last change on this file since 95685 was 94465, checked in by vboxsync, 3 years ago

Devices/EFI: Fix possible triple fault with SMP configurations during reset

During initialization UEFI enumerates the available CPUs by issuing an IPI to all APs
and waiting a fixed amount of time (50ms by default) for all APs to serve the request.
The timeout is far too low for a hypervisor due to host scheduling interference resulting in the
timed wait to expire before the APs could startup. The BSP will then free the wakeup buffer which
the APs use to startup (switching from real mode to the final execution mode) and overwrite it with 0.
This causes the APs to execute garbage when the respective EMTs get finally scheduled causing all sorts
of weird behavior.

Because it is impossible to set a sane upper timeout without causing execessive delay this change
makes the plartform code query the maximum and current number of CPUs present for the guest so
it can exactly wait for the amount of available CPUs without using any fixed timeouts.

(I'm still puzzled why this only happens when the VM is reset and not during startup...)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.1 KB
Line 
1/* $Id: DevEFI.cpp 94465 2022-04-05 11:43:09Z vboxsync $ */
2/** @file
3 * DevEFI - EFI <-> VirtualBox Integration Framework.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_EFI
23
24#include <VBox/vmm/pdmdev.h>
25#include <VBox/vmm/pgm.h>
26#include <VBox/vmm/cpum.h>
27#include <VBox/vmm/mm.h>
28#include <VBox/log.h>
29#include <VBox/err.h>
30#include <VBox/param.h>
31#include <VBox/vmm/dbgf.h>
32
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/ctype.h>
36#include <iprt/file.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/uuid.h>
40#include <iprt/path.h>
41#include <iprt/string.h>
42#include <iprt/mp.h>
43#include <iprt/list.h>
44#if defined(DEBUG) && defined(IN_RING3)
45# include <iprt/stream.h>
46# define DEVEFI_WITH_VBOXDBG_SCRIPT
47#endif
48#include <iprt/utf16.h>
49
50#include "DevEFI.h"
51#include "FlashCore.h"
52#include "VBoxDD.h"
53#include "VBoxDD2.h"
54#include "../PC/DevFwCommon.h"
55
56/* EFI includes */
57#ifdef IN_RING3
58# ifdef _MSC_VER
59# pragma warning(push)
60# pragma warning(disable:4668)
61# endif
62# include <ProcessorBind.h>
63# ifdef _MSC_VER
64# pragma warning(pop)
65# endif
66# include <Common/UefiBaseTypes.h>
67# include <Common/PiFirmwareVolume.h>
68# include <Common/PiFirmwareFile.h>
69#endif
70
71
72/*********************************************************************************************************************************
73* Structures and Typedefs *
74*********************************************************************************************************************************/
75/**
76 * The EFI device shared state structure.
77 */
78typedef struct DEVEFI
79{
80 /** The flash device containing the NVRAM. */
81 FLASHCORE Flash;
82 /** The 8 I/O ports at 0xEF10 (EFI_PORT_BASE). */
83 IOMIOPORTHANDLE hIoPorts;
84 /** The flash MMIO handle. */
85 IOMMMIOHANDLE hMmioFlash;
86} DEVEFI;
87/** Pointer to the shared EFI state. */
88typedef DEVEFI *PDEVEFI;
89
90/**
91 * The EFI device state structure for ring-3.
92 */
93typedef struct DEVEFIR3
94{
95 /** Pointer back to the device instance. */
96 PPDMDEVINS pDevIns;
97
98 /** EFI message buffer. */
99 char szMsg[VBOX_EFI_DEBUG_BUFFER];
100 /** EFI message buffer index. */
101 uint32_t iMsg;
102
103 /** EFI panic message buffer. */
104 char szPanicMsg[2048];
105 /** EFI panic message buffer index. */
106 uint32_t iPanicMsg;
107
108 struct
109 {
110 /** The current/last image event. */
111 uint8_t uEvt;
112 /** Module path/name offset. */
113 uint8_t offName;
114 /** The offset of the last component in the module path/name. */
115 uint8_t offNameLastComponent;
116 /** Alignment padding. */
117 uint8_t abPadding[5];
118 /** First address associated with the event (image address). */
119 uint64_t uAddr0;
120 /** Second address associated with the event (old image address). */
121 uint64_t uAddr1;
122 /** The size associated with the event (0 if none). */
123 uint64_t cb0;
124 /** The module name. */
125 char szName[256];
126 } ImageEvt;
127
128 /** The system EFI ROM data. */
129 uint8_t const *pu8EfiRom;
130 /** The system EFI ROM data pointer to be passed to RTFileReadAllFree. */
131 uint8_t *pu8EfiRomFree;
132 /** The size of the system EFI ROM. */
133 uint64_t cbEfiRom;
134 /** Offset into the actual ROM within EFI FW volume. */
135 uint64_t offEfiRom;
136 /** The name of the EFI ROM file. */
137 char *pszEfiRomFile;
138 /** Thunk page pointer. */
139 uint8_t *pu8EfiThunk;
140 /** First entry point of the EFI firmware. */
141 RTGCPHYS GCEntryPoint0;
142 /** Second Entry Point (PeiCore)*/
143 RTGCPHYS GCEntryPoint1;
144 /** EFI firmware physical load address. */
145 RTGCPHYS GCLoadAddress;
146 /** Current info selector. */
147 uint32_t iInfoSelector;
148 /** Current info position. */
149 int32_t offInfo;
150
151 /** Number of virtual CPUs. (Config) */
152 uint32_t cCpus;
153
154 /** The size of the DMI tables. */
155 uint16_t cbDmiTables;
156 /** Number of the DMI tables. */
157 uint16_t cNumDmiTables;
158 /** The DMI tables. */
159 uint8_t au8DMIPage[0x1000];
160
161 /** I/O-APIC enabled? */
162 uint8_t u8IOAPIC;
163
164 /** APIC mode to be set up by firmware. */
165 uint8_t u8APIC;
166
167 /** Boot parameters passed to the firmware. */
168 char szBootArgs[256];
169
170 /** Host UUID (for DMI). */
171 RTUUID aUuid;
172
173 /** Device properties buffer. */
174 R3PTRTYPE(uint8_t *) pbDeviceProps;
175 /** Device properties buffer size. */
176 uint32_t cbDeviceProps;
177
178 /** Virtual machine front side bus frequency. */
179 uint64_t u64FsbFrequency;
180 /** Virtual machine time stamp counter frequency. */
181 uint64_t u64TscFrequency;
182 /** Virtual machine CPU frequency. */
183 uint64_t u64CpuFrequency;
184 /** EFI Graphics mode (used as fallback if resolution is not known). */
185 uint32_t u32GraphicsMode;
186 /** EFI Graphics (GOP or UGA) horizontal resolution. */
187 uint32_t u32HorizontalResolution;
188 /** EFI Graphics (GOP or UGA) vertical resolution. */
189 uint32_t u32VerticalResolution;
190 /** Physical address of PCI config space MMIO region */
191 uint64_t u64McfgBase;
192 /** Length of PCI config space MMIO region */
193 uint64_t cbMcfgLength;
194 /** Size of the configured NVRAM device. */
195 uint32_t cbNvram;
196 /** Start address of the NVRAM flash. */
197 RTGCPHYS GCPhysNvram;
198
199 /** Filename of the file containing the NVRAM store. */
200 char *pszNvramFile;
201
202 /**
203 * NVRAM port - LUN\#0.
204 */
205 struct
206 {
207 /** The base interface we provide the NVRAM driver. */
208 PDMIBASE IBase;
209 /** The NVRAM driver base interface. */
210 PPDMIBASE pDrvBase;
211 /** The VFS interface of the driver below for NVRAM state loading and storing. */
212 PPDMIVFSCONNECTOR pDrvVfs;
213 } Lun0;
214} DEVEFIR3;
215/** Pointer to the ring-3 EFI state. */
216typedef DEVEFIR3 *PDEVEFIR3;
217
218
219/**
220 * The EFI device state structure for ring-0.
221 */
222typedef struct DEVEFIR0
223{
224 uint32_t uEmpty;
225} DEVEFIR0;
226/** Pointer to the ring-0 EFI state. */
227typedef DEVEFIR0 *PDEVEFIR0;
228
229
230/**
231 * The EFI device state structure for raw-mode.
232 */
233typedef struct DEVEFIRC
234{
235 uint32_t uEmpty;
236} DEVEFIRC;
237/** Pointer to the raw-mode EFI state. */
238typedef DEVEFIRC *PDEVEFIRC;
239
240
241/** @typedef DEVEFICC
242 * The instance data for the current context. */
243/** @typedef PDEVEFICC
244 * Pointer to the instance data for the current context. */
245#ifdef IN_RING3
246typedef DEVEFIR3 DEVEFICC;
247typedef PDEVEFIR3 PDEVEFICC;
248#elif defined(IN_RING0)
249typedef DEVEFIR0 DEVEFICC;
250typedef PDEVEFIR0 PDEVEFICC;
251#elif defined(IN_RC)
252typedef DEVEFIRC DEVEFICC;
253typedef PDEVEFIRC PDEVEFICC;
254#else
255# error "Not IN_RING3, IN_RING0 or IN_RC"
256#endif
257
258
259/*********************************************************************************************************************************
260* Defined Constants And Macros *
261*********************************************************************************************************************************/
262/** The saved state version. */
263#define EFI_SSM_VERSION 3
264/** The saved state version before working NVRAM support was implemented. */
265#define EFI_SSM_VERSION_PRE_PROPER_NVRAM 2
266/** The saved state version from VBox 4.2. */
267#define EFI_SSM_VERSION_4_2 1
268
269/** Non-volatile EFI variable. */
270#define VBOX_EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
271/** Non-volatile EFI variable. */
272#define VBOX_EFI_VARIABLE_READ_ONLY UINT32_C(0x00000008)
273
274
275/*********************************************************************************************************************************
276* Global Variables *
277*********************************************************************************************************************************/
278#ifdef IN_RING3
279/** The EfiSystemNvDataFv GUID for NVRAM storage. */
280static const RTUUID g_UuidNvDataFv = { { 0x8d, 0x2b, 0xf1, 0xff, 0x96, 0x76, 0x8b, 0x4c, 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50} };
281
282# ifdef VBOX_WITH_EFI_IN_DD2
283/** Special file name value for indicating the 32-bit built-in EFI firmware. */
284static const char g_szEfiBuiltin32[] = "VBoxEFI32.fd";
285/** Special file name value for indicating the 64-bit built-in EFI firmware. */
286static const char g_szEfiBuiltin64[] = "VBoxEFI64.fd";
287# endif
288#endif /* IN_RING3 */
289
290
291#ifdef IN_RING3
292
293/**
294 * Gets the info item size.
295 *
296 * @returns Size in bytes, UINT32_MAX on error.
297 * @param pThisCC The EFI state for the current context.
298 */
299static uint32_t efiInfoSize(PDEVEFIR3 pThisCC)
300{
301 switch (pThisCC->iInfoSelector)
302 {
303 case EFI_INFO_INDEX_VOLUME_BASE:
304 case EFI_INFO_INDEX_VOLUME_SIZE:
305 case EFI_INFO_INDEX_TEMPMEM_BASE:
306 case EFI_INFO_INDEX_TEMPMEM_SIZE:
307 case EFI_INFO_INDEX_STACK_BASE:
308 case EFI_INFO_INDEX_STACK_SIZE:
309 case EFI_INFO_INDEX_GRAPHICS_MODE:
310 case EFI_INFO_INDEX_VERTICAL_RESOLUTION:
311 case EFI_INFO_INDEX_HORIZONTAL_RESOLUTION:
312 case EFI_INFO_INDEX_CPU_COUNT_CURRENT:
313 case EFI_INFO_INDEX_CPU_COUNT_MAX:
314 return 4;
315 case EFI_INFO_INDEX_BOOT_ARGS:
316 return (uint32_t)RTStrNLen(pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs)) + 1;
317 case EFI_INFO_INDEX_DEVICE_PROPS:
318 return pThisCC->cbDeviceProps;
319 case EFI_INFO_INDEX_FSB_FREQUENCY:
320 case EFI_INFO_INDEX_CPU_FREQUENCY:
321 case EFI_INFO_INDEX_TSC_FREQUENCY:
322 case EFI_INFO_INDEX_MCFG_BASE:
323 case EFI_INFO_INDEX_MCFG_SIZE:
324 return 8;
325 case EFI_INFO_INDEX_APIC_MODE:
326 return 1;
327 }
328 return UINT32_MAX;
329}
330
331
332/**
333 * efiInfoNextByte for a uint8_t value.
334 *
335 * @returns Next (current) byte.
336 * @param pThisCC The EFI state for the current context.
337 * @param u8 The value.
338 */
339static uint8_t efiInfoNextByteU8(PDEVEFIR3 pThisCC, uint8_t u8)
340{
341 uint32_t off = pThisCC->offInfo;
342 if (off >= 1)
343 return 0;
344 return (uint8_t)u8;
345}
346
347
348/**
349 * efiInfoNextByte for a uint64_t value.
350 *
351 * @returns Next (current) byte.
352 * @param pThisCC The EFI state for the current context.
353 * @param u64 The value.
354 */
355static uint8_t efiInfoNextByteU64(PDEVEFIR3 pThisCC, uint64_t u64)
356{
357 uint64_t off = pThisCC->offInfo;
358 if (off >= 8)
359 return 0;
360 return (uint8_t)(u64 >> (off * 8));
361}
362
363/**
364 * efiInfoNextByte for a uint32_t value.
365 *
366 * @returns Next (current) byte.
367 * @param pThisCC The EFI state for the current context.
368 * @param u32 The value.
369 */
370static uint8_t efiInfoNextByteU32(PDEVEFIR3 pThisCC, uint32_t u32)
371{
372 uint32_t off = pThisCC->offInfo;
373 if (off >= 4)
374 return 0;
375 return (uint8_t)(u32 >> (off * 8));
376}
377
378/**
379 * efiInfoNextByte for a buffer.
380 *
381 * @returns Next (current) byte.
382 * @param pThisCC The EFI state for the current context.
383 * @param pvBuf The buffer.
384 * @param cbBuf The buffer size.
385 */
386static uint8_t efiInfoNextByteBuf(PDEVEFIR3 pThisCC, void const *pvBuf, size_t cbBuf)
387{
388 uint32_t off = pThisCC->offInfo;
389 if (off >= cbBuf)
390 return 0;
391 return ((uint8_t const *)pvBuf)[off];
392}
393
394/**
395 * Gets the next info byte.
396 *
397 * @returns Next (current) byte.
398 * @param pThisCC The EFI state for the current context.
399 */
400static uint8_t efiInfoNextByte(PDEVEFIR3 pThisCC)
401{
402 switch (pThisCC->iInfoSelector)
403 {
404
405 case EFI_INFO_INDEX_VOLUME_BASE: return efiInfoNextByteU64(pThisCC, pThisCC->GCLoadAddress);
406 case EFI_INFO_INDEX_VOLUME_SIZE: return efiInfoNextByteU64(pThisCC, pThisCC->cbEfiRom);
407 case EFI_INFO_INDEX_TEMPMEM_BASE: return efiInfoNextByteU32(pThisCC, VBOX_EFI_TOP_OF_STACK); /* just after stack */
408 case EFI_INFO_INDEX_TEMPMEM_SIZE: return efiInfoNextByteU32(pThisCC, _512K);
409 case EFI_INFO_INDEX_FSB_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64FsbFrequency);
410 case EFI_INFO_INDEX_TSC_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64TscFrequency);
411 case EFI_INFO_INDEX_CPU_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64CpuFrequency);
412 case EFI_INFO_INDEX_BOOT_ARGS: return efiInfoNextByteBuf(pThisCC, pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs));
413 case EFI_INFO_INDEX_DEVICE_PROPS: return efiInfoNextByteBuf(pThisCC, pThisCC->pbDeviceProps, pThisCC->cbDeviceProps);
414 case EFI_INFO_INDEX_GRAPHICS_MODE: return efiInfoNextByteU32(pThisCC, pThisCC->u32GraphicsMode);
415 case EFI_INFO_INDEX_HORIZONTAL_RESOLUTION: return efiInfoNextByteU32(pThisCC, pThisCC->u32HorizontalResolution);
416 case EFI_INFO_INDEX_VERTICAL_RESOLUTION: return efiInfoNextByteU32(pThisCC, pThisCC->u32VerticalResolution);
417 case EFI_INFO_INDEX_CPU_COUNT_CURRENT: return efiInfoNextByteU32(pThisCC, pThisCC->cCpus); /** @todo CPU hotplugging. */
418 case EFI_INFO_INDEX_CPU_COUNT_MAX: return efiInfoNextByteU32(pThisCC, pThisCC->cCpus);
419
420 /* Keep in sync with value in EfiThunk.asm */
421 case EFI_INFO_INDEX_STACK_BASE: return efiInfoNextByteU32(pThisCC, VBOX_EFI_TOP_OF_STACK - _128K); /* 2M - 128 K */
422 case EFI_INFO_INDEX_STACK_SIZE: return efiInfoNextByteU32(pThisCC, _128K);
423 case EFI_INFO_INDEX_MCFG_BASE: return efiInfoNextByteU64(pThisCC, pThisCC->u64McfgBase);
424 case EFI_INFO_INDEX_MCFG_SIZE: return efiInfoNextByteU64(pThisCC, pThisCC->cbMcfgLength);
425 case EFI_INFO_INDEX_APIC_MODE: return efiInfoNextByteU8(pThisCC, pThisCC->u8APIC);
426
427 default:
428 PDMDevHlpDBGFStop(pThisCC->pDevIns, RT_SRC_POS, "%#x", pThisCC->iInfoSelector);
429 return 0;
430 }
431}
432
433
434#ifdef IN_RING3
435static void efiVBoxDbgScript(const char *pszFormat, ...)
436{
437# ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
438 PRTSTREAM pStrm;
439 int rc2 = RTStrmOpen("./DevEFI.VBoxDbg", "a", &pStrm);
440 if (RT_SUCCESS(rc2))
441 {
442 va_list va;
443 va_start(va, pszFormat);
444 RTStrmPrintfV(pStrm, pszFormat, va);
445 va_end(va);
446 RTStrmClose(pStrm);
447 }
448# else
449 RT_NOREF(pszFormat);
450# endif
451}
452#endif /* IN_RING3 */
453
454
455/**
456 * Handles writes to the image event port.
457 *
458 * @returns VBox status suitable for I/O port write handler.
459 *
460 * @param pThisCC The EFI state for the current context.
461 * @param u32 The value being written.
462 * @param cb The size of the value.
463 */
464static int efiPortImageEventWrite(PDEVEFIR3 pThisCC, uint32_t u32, unsigned cb)
465{
466 RT_NOREF(cb);
467 switch (u32 & EFI_IMAGE_EVT_CMD_MASK)
468 {
469 case EFI_IMAGE_EVT_CMD_START_LOAD32:
470 case EFI_IMAGE_EVT_CMD_START_LOAD64:
471 case EFI_IMAGE_EVT_CMD_START_UNLOAD32:
472 case EFI_IMAGE_EVT_CMD_START_UNLOAD64:
473 case EFI_IMAGE_EVT_CMD_START_RELOC32:
474 case EFI_IMAGE_EVT_CMD_START_RELOC64:
475 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
476
477 /* Reset the state. */
478 RT_ZERO(pThisCC->ImageEvt);
479 pThisCC->ImageEvt.uEvt = (uint8_t)u32; Assert(pThisCC->ImageEvt.uEvt == u32);
480 return VINF_SUCCESS;
481
482 case EFI_IMAGE_EVT_CMD_COMPLETE:
483 {
484# ifdef IN_RING3
485 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
486
487 /* For now, just log it. */
488 static uint64_t s_cImageEvtLogged = 0;
489 if (s_cImageEvtLogged < 2048)
490 {
491 s_cImageEvtLogged++;
492 switch (pThisCC->ImageEvt.uEvt)
493 {
494 /* ASSUMES the name ends with .pdb and the image file ends with .efi! */
495 case EFI_IMAGE_EVT_CMD_START_LOAD32:
496 LogRel(("EFI: VBoxDbg> loadimage32 '%.*s.efi' %#llx LB %#llx\n",
497 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
498 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent],
499 pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0));
500 if (pThisCC->ImageEvt.offName > 4)
501 efiVBoxDbgScript("loadimage32 '%.*s.efi' %#llx\n",
502 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
503 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent],
504 pThisCC->ImageEvt.uAddr0);
505 break;
506 case EFI_IMAGE_EVT_CMD_START_LOAD64:
507 LogRel(("EFI: VBoxDbg> loadimage64 '%.*s.efi' %#llx LB %#llx\n",
508 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
509 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent],
510 pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0));
511 if (pThisCC->ImageEvt.offName > 4)
512 efiVBoxDbgScript("loadimage64 '%.*s.efi' %#llx\n",
513 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
514 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent],
515 pThisCC->ImageEvt.uAddr0);
516 break;
517 case EFI_IMAGE_EVT_CMD_START_UNLOAD32:
518 case EFI_IMAGE_EVT_CMD_START_UNLOAD64:
519 {
520 LogRel(("EFI: VBoxDbg> unload '%.*s.efi' # %#llx LB %#llx\n",
521 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
522 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent],
523 pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0));
524 if (pThisCC->ImageEvt.offName > 4)
525 efiVBoxDbgScript("unload '%.*s.efi'\n",
526 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
527 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent]);
528 break;
529 }
530 case EFI_IMAGE_EVT_CMD_START_RELOC32:
531 case EFI_IMAGE_EVT_CMD_START_RELOC64:
532 {
533 LogRel(("EFI: relocate module to %#llx from %#llx\n",
534 pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.uAddr1));
535 break;
536 }
537 }
538 }
539 return VINF_SUCCESS;
540# else
541 return VINF_IOM_R3_IOPORT_WRITE;
542# endif
543 }
544
545 case EFI_IMAGE_EVT_CMD_ADDR0:
546 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
547 pThisCC->ImageEvt.uAddr0 <<= 16;
548 pThisCC->ImageEvt.uAddr0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
549 return VINF_SUCCESS;
550
551 case EFI_IMAGE_EVT_CMD_ADDR1:
552 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
553 pThisCC->ImageEvt.uAddr1 <<= 16;
554 pThisCC->ImageEvt.uAddr1 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
555 return VINF_SUCCESS;
556
557 case EFI_IMAGE_EVT_CMD_SIZE0:
558 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
559 pThisCC->ImageEvt.cb0 <<= 16;
560 pThisCC->ImageEvt.cb0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
561 return VINF_SUCCESS;
562
563 case EFI_IMAGE_EVT_CMD_NAME:
564 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= 0x7f);
565 if (pThisCC->ImageEvt.offName < sizeof(pThisCC->ImageEvt.szName) - 1)
566 {
567 char ch = EFI_IMAGE_EVT_GET_PAYLOAD_U8(u32);
568 if (ch == '\\')
569 ch = '/';
570 pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offName++] = ch;
571 if (ch == '/' || ch == ':')
572 pThisCC->ImageEvt.offNameLastComponent = pThisCC->ImageEvt.offName;
573 }
574 else
575 Log(("EFI: Image name overflow\n"));
576 return VINF_SUCCESS;
577 }
578
579 Log(("EFI: Unknown image event: %#x (cb=%d)\n", u32, cb));
580 return VINF_SUCCESS;
581}
582
583
584/**
585 * @callback_method_impl{FNIOMIOPORTNEWIN}
586 *
587 * @note The @a offPort parameter is absolute!
588 */
589static DECLCALLBACK(VBOXSTRICTRC) efiR3IoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
590{
591 RT_NOREF(pvUser);
592 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
593 Log4(("EFI in: %x %x\n", offPort, cb));
594
595 switch (offPort)
596 {
597 case EFI_INFO_PORT:
598 if (pThisCC->offInfo == -1 && cb == 4)
599 {
600 pThisCC->offInfo = 0;
601 uint32_t cbInfo = *pu32 = efiInfoSize(pThisCC);
602 if (cbInfo == UINT32_MAX)
603 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "iInfoSelector=%#x (%d)\n",
604 pThisCC->iInfoSelector, pThisCC->iInfoSelector);
605 }
606 else
607 {
608 if (cb != 1)
609 return VERR_IOM_IOPORT_UNUSED;
610 *pu32 = efiInfoNextByte(pThisCC);
611 pThisCC->offInfo++;
612 }
613 return VINF_SUCCESS;
614
615 case EFI_PANIC_PORT:
616# ifdef IN_RING3
617 LogRel(("EFI panic port read!\n"));
618 /* Insert special code here on panic reads */
619 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: panic port read!\n");
620# else
621 /* Reschedule to R3 */
622 return VINF_IOM_R3_IOPORT_READ;
623# endif
624
625 case EFI_PORT_VARIABLE_OP: /* Obsolete */
626 case EFI_PORT_VARIABLE_PARAM:
627 case EFI_PORT_DEBUG_POINT:
628 case EFI_PORT_IMAGE_EVENT:
629 *pu32 = UINT32_MAX;
630 return VINF_SUCCESS;
631 }
632
633 return VERR_IOM_IOPORT_UNUSED;
634}
635
636
637/**
638 * Translates a debug point value into a string for logging.
639 *
640 * @returns read-only string
641 * @param enmDbgPoint Valid debug point value.
642 */
643static const char *efiDbgPointName(EFIDBGPOINT enmDbgPoint)
644{
645 switch (enmDbgPoint)
646 {
647 case EFIDBGPOINT_SEC_PREMEM: return "SEC_PREMEM";
648 case EFIDBGPOINT_SEC_POSTMEM: return "SEC_POSTMEM";
649 case EFIDBGPOINT_DXE_CORE: return "DXE_CORE";
650 case EFIDBGPOINT_SMM: return "SMM";
651 case EFIDBGPOINT_SMI_ENTER: return "SMI_ENTER";
652 case EFIDBGPOINT_SMI_EXIT: return "SMI_EXIT";
653 case EFIDBGPOINT_GRAPHICS: return "GRAPHICS";
654 case EFIDBGPOINT_DXE_AP: return "DXE_AP";
655 default:
656 AssertFailed();
657 return "Unknown";
658 }
659}
660
661
662/**
663 * @callback_method_impl{FNIOMIOPORTNEWOUT}
664 *
665 * @note The @a offPort parameter is absolute!
666 */
667static DECLCALLBACK(VBOXSTRICTRC) efiR3IoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
668{
669 RT_NOREF(pvUser);
670 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
671 VBOXSTRICTRC rc = VINF_SUCCESS;
672 Log4(("efi: out %x %x %d\n", offPort, u32, cb));
673
674 switch (offPort)
675 {
676 case EFI_INFO_PORT:
677 Log2(("EFI_INFO_PORT: iInfoSelector=%#x\n", u32));
678 pThisCC->iInfoSelector = u32;
679 pThisCC->offInfo = -1;
680 break;
681
682 case EFI_DEBUG_PORT:
683 {
684 /* The raw version. */
685 switch (u32)
686 {
687 case '\r': Log3(("efi: <return>\n")); break;
688 case '\n': Log3(("efi: <newline>\n")); break;
689 case '\t': Log3(("efi: <tab>\n")); break;
690 default: Log3(("efi: %c (%02x)\n", u32, u32)); break;
691 }
692 /* The readable, buffered version. */
693 if (u32 == '\n' || u32 == '\r')
694 {
695 Assert(pThisCC->iMsg < sizeof(pThisCC->szMsg));
696 pThisCC->szMsg[pThisCC->iMsg] = '\0';
697 if (pThisCC->iMsg)
698 LogRel2(("efi: %s\n", pThisCC->szMsg));
699 pThisCC->iMsg = 0;
700 }
701 else
702 {
703 if (pThisCC->iMsg >= sizeof(pThisCC->szMsg) - 1)
704 {
705 pThisCC->szMsg[pThisCC->iMsg] = '\0';
706 LogRel2(("efi: %s\n", pThisCC->szMsg));
707 pThisCC->iMsg = 0;
708 }
709 pThisCC->szMsg[pThisCC->iMsg] = (char)u32;
710 pThisCC->szMsg[++pThisCC->iMsg] = '\0';
711 }
712 break;
713 }
714
715 case EFI_PANIC_PORT:
716 {
717 switch (u32)
718 {
719 case EFI_PANIC_CMD_BAD_ORG: /* Legacy */
720 case EFI_PANIC_CMD_THUNK_TRAP:
721#ifdef IN_RING3
722 LogRel(("EFI: Panic! Unexpected trap!!\n"));
723# ifdef VBOX_STRICT
724 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n");
725# else
726 AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
727# endif
728 break;
729#else
730 return VINF_IOM_R3_IOPORT_WRITE;
731#endif
732
733 case EFI_PANIC_CMD_START_MSG:
734 LogRel(("Receiving EFI panic...\n"));
735 pThisCC->iPanicMsg = 0;
736 pThisCC->szPanicMsg[0] = '\0';
737 break;
738
739 case EFI_PANIC_CMD_END_MSG:
740#ifdef IN_RING3
741 LogRel(("EFI: Panic! %s\n", pThisCC->szPanicMsg));
742# ifdef VBOX_STRICT
743 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThisCC->szPanicMsg);
744# else
745 return VERR_INTERNAL_ERROR;
746# endif
747#else
748 return VINF_IOM_R3_IOPORT_WRITE;
749#endif
750
751
752 default:
753 if ( u32 >= EFI_PANIC_CMD_MSG_FIRST
754 && u32 <= EFI_PANIC_CMD_MSG_LAST)
755 {
756 /* Add the message char to the buffer. */
757 uint32_t i = pThisCC->iPanicMsg;
758 if (i + 1 < sizeof(pThisCC->szPanicMsg))
759 {
760 char ch = EFI_PANIC_CMD_MSG_GET_CHAR(u32);
761 if ( ch == '\n'
762 && i > 0
763 && pThisCC->szPanicMsg[i - 1] == '\r')
764 i--;
765 pThisCC->szPanicMsg[i] = ch;
766 pThisCC->szPanicMsg[i + 1] = '\0';
767 pThisCC->iPanicMsg = i + 1;
768 }
769 }
770 else
771 Log(("EFI: Unknown panic command: %#x (cb=%d)\n", u32, cb));
772 break;
773 }
774 break;
775 }
776
777 case EFI_PORT_VARIABLE_OP:
778 case EFI_PORT_VARIABLE_PARAM:
779 {
780 /* Ignore access to the obsolete variable handling port. */
781 Log(("EFI: Write to obsolete variable handling port %RTiop: %#x (cb=%d)\n", offPort, u32, cb));
782 break;
783 }
784
785 case EFI_PORT_DEBUG_POINT:
786# ifdef IN_RING3
787 if (u32 > EFIDBGPOINT_INVALID && u32 < EFIDBGPOINT_END)
788 {
789 /* For now, just log it. */
790 LogRelMax(1024, ("EFI: debug point %s\n", efiDbgPointName((EFIDBGPOINT)u32)));
791 rc = VINF_SUCCESS;
792 }
793 else
794 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Invalid debug point %#x\n", u32);
795 break;
796# else
797 return VINF_IOM_R3_IOPORT_WRITE;
798# endif
799
800 case EFI_PORT_IMAGE_EVENT:
801 rc = efiPortImageEventWrite(pThisCC, u32, cb);
802 break;
803
804 default:
805 Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", offPort, u32, cb));
806 break;
807 }
808 return rc;
809}
810
811#endif /* IN_RING3 */
812
813/**
814 * @callback_method_impl{FNIOMMMIONEWWRITE, Flash memory write}
815 */
816static DECLCALLBACK(VBOXSTRICTRC) efiR3NvMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
817{
818 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
819 RT_NOREF(pvUser);
820
821 return flashWrite(&pThis->Flash, off, pv, cb);
822}
823
824
825/**
826 * @callback_method_impl{FNIOMMMIONEWREAD, Flash memory read}
827 */
828static DECLCALLBACK(VBOXSTRICTRC) efiR3NvMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
829{
830 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
831 RT_NOREF(pvUser);
832
833 return flashRead(&pThis->Flash, off, pv, cb);
834}
835
836#ifdef IN_RING3
837
838static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
839{
840 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
841 LogFlow(("efiSaveExec:\n"));
842
843 return flashR3SaveExec(&pThis->Flash, pDevIns, pSSM);
844}
845
846static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
847{
848 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
849 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
850 LogFlow(("efiLoadExec: uVersion=%d uPass=%d\n", uVersion, uPass));
851
852 /*
853 * Validate input.
854 */
855 if (uPass != SSM_PASS_FINAL)
856 return VERR_SSM_UNEXPECTED_PASS;
857 if ( uVersion != EFI_SSM_VERSION
858 && uVersion != EFI_SSM_VERSION_PRE_PROPER_NVRAM
859 && uVersion != EFI_SSM_VERSION_4_2
860 )
861 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
862
863 int rc;
864 if (uVersion > EFI_SSM_VERSION_PRE_PROPER_NVRAM)
865 rc = flashR3LoadExec(&pThis->Flash, pDevIns, pSSM);
866 else
867 {
868 /*
869 * Ignore the old NVRAM state.
870 */
871 rc = pHlp->pfnSSMSkipToEndOfUnit(pSSM);
872 }
873
874 return rc;
875}
876
877
878/**
879 * @copydoc(PDMIBASE::pfnQueryInterface)
880 */
881static DECLCALLBACK(void *) devEfiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
882{
883 LogFlowFunc(("ENTER: pIBase=%p pszIID=%p\n", pInterface, pszIID));
884 PDEVEFIR3 pThisCC = RT_FROM_MEMBER(pInterface, DEVEFIR3, Lun0.IBase);
885
886 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->Lun0.IBase);
887 return NULL;
888}
889
890
891/**
892 * Write to CMOS memory.
893 * This is used by the init complete code.
894 */
895static void cmosWrite(PPDMDEVINS pDevIns, unsigned off, uint32_t u32Val)
896{
897 Assert(off < 128);
898 Assert(u32Val < 256);
899
900 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
901 AssertRC(rc);
902}
903
904/**
905 * Init complete notification.
906 *
907 * @returns VBOX status code.
908 * @param pDevIns The device instance.
909 */
910static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
911{
912 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
913
914 uint64_t const cbRamSize = PDMDevHlpMMPhysGetRamSize(pDevIns);
915 uint32_t const cbBelow4GB = PDMDevHlpMMPhysGetRamSizeBelow4GB(pDevIns);
916 uint64_t const cbAbove4GB = PDMDevHlpMMPhysGetRamSizeAbove4GB(pDevIns);
917 NOREF(cbAbove4GB);
918
919 /*
920 * Memory sizes.
921 */
922 uint32_t u32Low = 0;
923 uint32_t u32Chunks = 0;
924 if (cbRamSize > 16 * _1M)
925 {
926 u32Low = RT_MIN(cbBelow4GB, UINT32_C(0xfe000000));
927 u32Chunks = (u32Low - 16U * _1M) / _64K;
928 }
929 cmosWrite(pDevIns, 0x34, RT_BYTE1(u32Chunks));
930 cmosWrite(pDevIns, 0x35, RT_BYTE2(u32Chunks));
931
932 if (u32Low < cbRamSize)
933 {
934 uint64_t u64 = cbRamSize - u32Low;
935 u32Chunks = (uint32_t)(u64 / _64K);
936 cmosWrite(pDevIns, 0x5b, RT_BYTE1(u32Chunks));
937 cmosWrite(pDevIns, 0x5c, RT_BYTE2(u32Chunks));
938 cmosWrite(pDevIns, 0x5d, RT_BYTE3(u32Chunks));
939 cmosWrite(pDevIns, 0x5e, RT_BYTE4(u32Chunks));
940 }
941
942 /*
943 * Number of CPUs.
944 */
945 cmosWrite(pDevIns, 0x60, pThisCC->cCpus & 0xff);
946
947 return VINF_SUCCESS;
948}
949
950
951/**
952 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
953 */
954static DECLCALLBACK(void) efiMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
955{
956 RT_NOREF(enmCtx);
957 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
958
959 /*
960 * Re-shadow the Firmware Volume and make it RAM/RAM.
961 */
962 uint32_t cPages = RT_ALIGN_64(pThisCC->cbEfiRom, GUEST_PAGE_SIZE) >> GUEST_PAGE_SHIFT;
963 RTGCPHYS GCPhys = pThisCC->GCLoadAddress;
964 while (cPages > 0)
965 {
966 uint8_t abPage[GUEST_PAGE_SIZE];
967
968 /* Read the (original) ROM page and write it back to the RAM page. */
969 int rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
970 AssertLogRelRC(rc);
971
972 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
973 AssertLogRelRC(rc);
974 if (RT_FAILURE(rc))
975 memset(abPage, 0xcc, sizeof(abPage));
976
977 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
978 AssertLogRelRC(rc);
979
980 /* Switch to the RAM/RAM mode. */
981 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
982 AssertLogRelRC(rc);
983
984 /* Advance */
985 GCPhys += GUEST_PAGE_SIZE;
986 cPages--;
987 }
988}
989
990
991/**
992 * @interface_method_impl{PDMDEVREG,pfnReset}
993 */
994static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
995{
996 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
997 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
998 LogFlow(("efiReset\n"));
999
1000 pThisCC->iInfoSelector = 0;
1001 pThisCC->offInfo = -1;
1002
1003 pThisCC->iMsg = 0;
1004 pThisCC->szMsg[0] = '\0';
1005 pThisCC->iPanicMsg = 0;
1006 pThisCC->szPanicMsg[0] = '\0';
1007
1008 flashR3Reset(&pThis->Flash);
1009
1010#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
1011 /*
1012 * Zap the debugger script
1013 */
1014 RTFileDelete("./DevEFI.VBoxDbg");
1015#endif
1016}
1017
1018
1019/**
1020 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
1021 */
1022static DECLCALLBACK(void) efiPowerOff(PPDMDEVINS pDevIns)
1023{
1024 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1025 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1026
1027 if (pThisCC->Lun0.pDrvVfs)
1028 {
1029 int rc = flashR3SaveToVfs(&pThis->Flash, pDevIns, pThisCC->Lun0.pDrvVfs,
1030 pDevIns->pReg->szName, "nvram");
1031 if (RT_FAILURE(rc))
1032 LogRel(("EFI: Failed to save flash file to NVRAM store: %Rrc\n", rc));
1033 }
1034 else if (pThisCC->pszNvramFile)
1035 {
1036 int rc = flashR3SaveToFile(&pThis->Flash, pDevIns, pThisCC->pszNvramFile);
1037 if (RT_FAILURE(rc))
1038 LogRel(("EFI: Failed to save flash file to '%s': %Rrc\n", pThisCC->pszNvramFile, rc));
1039 }
1040}
1041
1042
1043
1044/**
1045 * Destruct a device instance.
1046 *
1047 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1048 * resources can be freed correctly.
1049 *
1050 * @param pDevIns The device instance data.
1051 */
1052static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
1053{
1054 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1055 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1056 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1057
1058 flashR3Destruct(&pThis->Flash, pDevIns);
1059
1060 if (pThisCC->pszNvramFile)
1061 {
1062 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszNvramFile);
1063 pThisCC->pszNvramFile = NULL;
1064 }
1065
1066 if (pThisCC->pu8EfiRomFree)
1067 {
1068 RTFileReadAllFree(pThisCC->pu8EfiRomFree, (size_t)pThisCC->cbEfiRom + pThisCC->offEfiRom);
1069 pThisCC->pu8EfiRomFree = NULL;
1070 }
1071
1072 /*
1073 * Free MM heap pointers (waste of time, but whatever).
1074 */
1075 if (pThisCC->pszEfiRomFile)
1076 {
1077 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszEfiRomFile);
1078 pThisCC->pszEfiRomFile = NULL;
1079 }
1080
1081 if (pThisCC->pu8EfiThunk)
1082 {
1083 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pu8EfiThunk);
1084 pThisCC->pu8EfiThunk = NULL;
1085 }
1086
1087 if (pThisCC->pbDeviceProps)
1088 {
1089 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbDeviceProps);
1090 pThisCC->pbDeviceProps = NULL;
1091 pThisCC->cbDeviceProps = 0;
1092 }
1093
1094 return VINF_SUCCESS;
1095}
1096
1097
1098#if 0 /* unused */
1099/**
1100 * Helper that searches for a FFS file of a given type.
1101 *
1102 * @returns Pointer to the FFS file header if found, NULL if not.
1103 *
1104 * @param pFfsFile Pointer to the FFS file header to start searching at.
1105 * @param pbEnd The end of the firmware volume.
1106 * @param FileType The file type to look for.
1107 * @param pcbFfsFile Where to store the FFS file size (includes header).
1108 */
1109DECLINLINE(EFI_FFS_FILE_HEADER const *)
1110efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
1111{
1112# define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
1113 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
1114 {
1115 if (pFfsFile->Type == FileType)
1116 {
1117 *pcbFile = FFS_SIZE(pFfsFile);
1118 LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType));
1119 return pFfsFile;
1120 }
1121 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
1122 }
1123# undef FFS_SIZE
1124 return NULL;
1125}
1126#endif /* unused */
1127
1128
1129/**
1130 * Parse EFI ROM headers and find entry points.
1131 *
1132 * @returns VBox status code.
1133 * @param pDevIns The device instance.
1134 * @param pThis The shared device state.
1135 * @param pThisCC The device state for the current context.
1136 */
1137static int efiParseFirmware(PPDMDEVINS pDevIns, PDEVEFI pThis, PDEVEFIR3 pThisCC)
1138{
1139 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThisCC->pu8EfiRom;
1140
1141 /*
1142 * Validate firmware volume header.
1143 */
1144 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
1145 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
1146 VERR_INVALID_MAGIC);
1147 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
1148 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
1149 VERR_VERSION_MISMATCH);
1150 /** @todo check checksum, see PE spec vol. 3 */
1151 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThisCC->cbEfiRom,
1152 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThisCC->cbEfiRom),
1153 VERR_INVALID_PARAMETER);
1154 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
1155 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
1156 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
1157 VERR_INVALID_PARAMETER);
1158
1159 AssertLogRelMsgReturn(!(pThisCC->cbEfiRom & GUEST_PAGE_OFFSET_MASK), ("%RX64\n", pThisCC->cbEfiRom), VERR_INVALID_PARAMETER);
1160
1161 LogRel(("Found EFI FW Volume, %u bytes (%u %u-byte blocks)\n", pFwVolHdr->FvLength, pFwVolHdr->BlockMap[0].NumBlocks, pFwVolHdr->BlockMap[0].Length));
1162
1163 /** @todo Make this more dynamic, this assumes that the NV storage area comes first (always the case for our builds). */
1164 AssertLogRelMsgReturn(!memcmp(&pFwVolHdr->FileSystemGuid, &g_UuidNvDataFv, sizeof(g_UuidNvDataFv)),
1165 ("Expected EFI_SYSTEM_NV_DATA_FV_GUID as an identifier"),
1166 VERR_INVALID_MAGIC);
1167
1168 /* Found NVRAM storage, configure flash device. */
1169 pThisCC->offEfiRom = pFwVolHdr->FvLength;
1170 pThisCC->cbNvram = pFwVolHdr->FvLength;
1171 pThisCC->GCPhysNvram = UINT32_C(0xfffff000) - pThisCC->cbEfiRom + GUEST_PAGE_SIZE;
1172 pThisCC->cbEfiRom -= pThisCC->cbNvram;
1173
1174 int rc = flashR3Init(&pThis->Flash, pThisCC->pDevIns, 0xA289 /*Intel*/, pThisCC->cbNvram, pFwVolHdr->BlockMap[0].Length);
1175 if (RT_FAILURE(rc))
1176 return rc;
1177
1178 if (pThisCC->Lun0.pDrvVfs)
1179 {
1180 rc = flashR3LoadFromVfs(&pThis->Flash, pDevIns, pThisCC->Lun0.pDrvVfs,
1181 pDevIns->pReg->szName, "nvram");
1182 if (rc == VERR_NOT_FOUND)
1183 {
1184 /* Initialize the NVRAM content from the loaded ROM file as the NVRAM wasn't initialized yet. */
1185 rc = flashR3LoadFromBuf(&pThis->Flash, pThisCC->pu8EfiRom, pThisCC->cbNvram);
1186 }
1187 else if (RT_FAILURE(rc))
1188 return rc;
1189 }
1190 else
1191 {
1192 /* If the file does not exist we initialize the NVRAM from the loaded ROM file. */
1193 if (!pThisCC->pszNvramFile || !RTPathExists(pThisCC->pszNvramFile))
1194 rc = flashR3LoadFromBuf(&pThis->Flash, pThisCC->pu8EfiRom, pThisCC->cbNvram);
1195 else
1196 rc = flashR3LoadFromFile(&pThis->Flash, pDevIns, pThisCC->pszNvramFile);
1197 if (RT_FAILURE(rc))
1198 return rc;
1199 }
1200
1201 pThisCC->GCLoadAddress = pThisCC->GCPhysNvram + pThisCC->cbNvram;
1202
1203 return VINF_SUCCESS;
1204}
1205
1206/**
1207 * Load EFI ROM file into the memory.
1208 *
1209 * @returns VBox status code.
1210 * @param pDevIns The device instance.
1211 * @param pThis The shared Efi state.
1212 * @param pThisCC The device state for the current context.
1213 * @param pCfg Configuration node handle for the device.
1214 */
1215static int efiLoadRom(PPDMDEVINS pDevIns, PDEVEFI pThis, PDEVEFIR3 pThisCC, PCFGMNODE pCfg)
1216{
1217 RT_NOREF(pCfg);
1218
1219 /*
1220 * Read the entire firmware volume into memory.
1221 */
1222 int rc;
1223#ifdef VBOX_WITH_EFI_IN_DD2
1224 if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltin32) == 0)
1225 {
1226 pThisCC->pu8EfiRomFree = NULL;
1227 pThisCC->pu8EfiRom = g_abEfiFirmware32;
1228 pThisCC->cbEfiRom = g_cbEfiFirmware32;
1229 }
1230 else if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltin64) == 0)
1231 {
1232 pThisCC->pu8EfiRomFree = NULL;
1233 pThisCC->pu8EfiRom = g_abEfiFirmware64;
1234 pThisCC->cbEfiRom = g_cbEfiFirmware64;
1235 }
1236 else
1237#endif
1238 {
1239 void *pvFile;
1240 size_t cbFile;
1241 rc = RTFileReadAllEx(pThisCC->pszEfiRomFile,
1242 0 /*off*/,
1243 RTFOFF_MAX /*cbMax*/,
1244 RTFILE_RDALL_O_DENY_WRITE,
1245 &pvFile,
1246 &cbFile);
1247 if (RT_FAILURE(rc))
1248 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1249 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
1250 pThisCC->pszEfiRomFile, rc);
1251 pThisCC->pu8EfiRomFree = (uint8_t *)pvFile;
1252 pThisCC->pu8EfiRom = (uint8_t *)pvFile;
1253 pThisCC->cbEfiRom = cbFile;
1254 }
1255
1256 /*
1257 * Validate firmware volume and figure out the load address as well as the SEC entry point.
1258 */
1259 rc = efiParseFirmware(pDevIns, pThis, pThisCC);
1260 if (RT_FAILURE(rc))
1261 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1262 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
1263 pThisCC->pszEfiRomFile, rc);
1264
1265 /*
1266 * Map the firmware volume into memory as shadowed ROM.
1267 *
1268 * This is a little complicated due to saved state legacy. We used to have a
1269 * 2MB image w/o any flash portion, divided into four 512KB mappings.
1270 *
1271 * We've now increased the size of the firmware to 4MB, but for saved state
1272 * compatibility reasons need to use the same mappings and names (!!) for the
1273 * top 2MB.
1274 */
1275 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
1276#if 1
1277 static const char * const s_apszNames[16] =
1278 {
1279 "EFI Firmware Volume", "EFI Firmware Volume (Part 2)", "EFI Firmware Volume (Part 3)", "EFI Firmware Volume (Part 4)",
1280 "EFI Firmware Volume (Part 5)", "EFI Firmware Volume (Part 6)", "EFI Firmware Volume (Part 7)", "EFI Firmware Volume (Part 8)",
1281 "EFI Firmware Volume (Part 9)", "EFI Firmware Volume (Part 10)", "EFI Firmware Volume (Part 11)", "EFI Firmware Volume (Part 12)",
1282 "EFI Firmware Volume (Part 13)", "EFI Firmware Volume (Part 14)", "EFI Firmware Volume (Part 15)", "EFI Firmware Volume (Part 16)",
1283 };
1284 AssertLogRelMsgReturn(pThisCC->cbEfiRom < RT_ELEMENTS(s_apszNames) * _512K,
1285 ("EFI firmware image too big: %#RX64, max %#zx\n",
1286 pThisCC->cbEfiRom, RT_ELEMENTS(s_apszNames) * _512K),
1287 VERR_IMAGE_TOO_BIG);
1288
1289 uint32_t const cbChunk = pThisCC->cbNvram + pThisCC->cbEfiRom >= _2M ? _512K
1290 : (uint32_t)RT_ALIGN_64((pThisCC->cbNvram + pThisCC->cbEfiRom) / 4, GUEST_PAGE_SIZE);
1291 uint32_t cbLeft = pThisCC->cbEfiRom; /* ASSUMES NVRAM comes first! */
1292 uint32_t off = pThisCC->offEfiRom + cbLeft; /* ASSUMES NVRAM comes first! */
1293 RTGCPHYS64 GCPhys = pThisCC->GCLoadAddress + cbLeft;
1294 AssertLogRelMsg(GCPhys == _4G, ("%RGp\n", GCPhys));
1295
1296 /* Compatibility mappings at the top (note that this isn't entirely the same
1297 algorithm, but it will produce the same results for a power of two sized image): */
1298 unsigned i = 4;
1299 while (i-- > 0)
1300 {
1301 uint32_t const cb = RT_MIN(cbLeft, cbChunk);
1302 cbLeft -= cb;
1303 GCPhys -= cb;
1304 off -= cb;
1305 rc = PDMDevHlpROMRegister(pDevIns, GCPhys, cb, pThisCC->pu8EfiRom + off, cb,
1306 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, s_apszNames[i]);
1307 AssertRCReturn(rc, rc);
1308 }
1309
1310 /* The rest (if any) is mapped in descending order of address and increasing name order: */
1311 if (cbLeft > 0)
1312 {
1313 Assert(cbChunk == _512K);
1314 for (i = 4; cbLeft > 0; i++)
1315 {
1316 uint32_t const cb = RT_MIN(cbLeft, cbChunk);
1317 cbLeft -= cb;
1318 GCPhys -= cb;
1319 off -= cb;
1320 /** @todo Add flag to prevent saved state loading from bitching about these regions. */
1321 rc = PDMDevHlpROMRegister(pDevIns, GCPhys, cb, pThisCC->pu8EfiRom + off, cb,
1322 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY
1323 | PGMPHYS_ROM_FLAGS_MAYBE_MISSING_FROM_STATE, s_apszNames[i]);
1324 AssertRCReturn(rc, rc);
1325 }
1326 Assert(i <= RT_ELEMENTS(s_apszNames));
1327 }
1328
1329 /* Not sure what the purpose of this one is... */
1330 rc = PDMDevHlpROMProtectShadow(pDevIns, pThisCC->GCLoadAddress, (uint32_t)cbChunk, PGMROMPROT_READ_RAM_WRITE_IGNORE);
1331 AssertRCReturn(rc, rc);
1332
1333#else
1334 RTGCPHYS cbQuart = RT_ALIGN_64(pThisCC->cbEfiRom / 4, GUEST_PAGE_SIZE);
1335 rc = PDMDevHlpROMRegister(pDevIns,
1336 pThisCC->GCLoadAddress,
1337 cbQuart,
1338 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs,
1339 cbQuart,
1340 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1341 "EFI Firmware Volume");
1342 AssertRCReturn(rc, rc);
1343 rc = PDMDevHlpROMProtectShadow(pDevIns, pThisCC->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
1344 AssertRCReturn(rc, rc);
1345 rc = PDMDevHlpROMRegister(pDevIns,
1346 pThisCC->GCLoadAddress + cbQuart,
1347 cbQuart,
1348 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart,
1349 cbQuart,
1350 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1351 "EFI Firmware Volume (Part 2)");
1352 if (RT_FAILURE(rc))
1353 return rc;
1354 rc = PDMDevHlpROMRegister(pDevIns,
1355 pThisCC->GCLoadAddress + cbQuart * 2,
1356 cbQuart,
1357 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart * 2,
1358 cbQuart,
1359 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1360 "EFI Firmware Volume (Part 3)");
1361 if (RT_FAILURE(rc))
1362 return rc;
1363 rc = PDMDevHlpROMRegister(pDevIns,
1364 pThisCC->GCLoadAddress + cbQuart * 3,
1365 pThisCC->cbEfiRom - cbQuart * 3,
1366 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart * 3,
1367 pThisCC->cbEfiRom - cbQuart * 3,
1368 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1369 "EFI Firmware Volume (Part 4)");
1370 if (RT_FAILURE(rc))
1371 return rc;
1372#endif
1373
1374 /*
1375 * Register MMIO region for flash device.
1376 */
1377 rc = PDMDevHlpMmioCreateEx(pDevIns, pThisCC->cbNvram, IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
1378 NULL /*pPciDev*/, UINT32_MAX, efiR3NvMmioWrite, efiR3NvMmioRead, NULL, NULL /*pvUser*/,
1379 "Flash Memory", &pThis->hMmioFlash);
1380 AssertRCReturn(rc, rc);
1381 rc = PDMDevHlpMmioMap(pDevIns, pThis->hMmioFlash, pThisCC->GCPhysNvram);
1382 AssertRCReturn(rc, rc);
1383
1384 LogRel(("EFI: Registered %uKB flash at %RGp\n", pThisCC->cbNvram / _1K, pThisCC->GCPhysNvram));
1385 return VINF_SUCCESS;
1386}
1387
1388static uint8_t efiGetHalfByte(char ch)
1389{
1390 uint8_t val;
1391
1392 if (ch >= '0' && ch <= '9')
1393 val = ch - '0';
1394 else if (ch >= 'A' && ch <= 'F')
1395 val = ch - 'A' + 10;
1396 else if(ch >= 'a' && ch <= 'f')
1397 val = ch - 'a' + 10;
1398 else
1399 val = 0xff;
1400
1401 return val;
1402}
1403
1404
1405/**
1406 * Converts a hex string into a binary data blob located at
1407 * pThisCC->pbDeviceProps, size returned as pThisCC->cbDeviceProps.
1408 *
1409 * @returns VERR_NO_MEMORY or VINF_SUCCESS.
1410 * @param pThisCC The device state for the current context.
1411 * @param pszDeviceProps The device property hex string to decode.
1412 */
1413static int efiParseDeviceString(PDEVEFIR3 pThisCC, const char *pszDeviceProps)
1414{
1415 uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
1416 pThisCC->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThisCC->pDevIns, cbOut);
1417 if (!pThisCC->pbDeviceProps)
1418 return VERR_NO_MEMORY;
1419
1420 uint32_t iHex = 0;
1421 bool fUpper = true;
1422 uint8_t u8Value = 0; /* (shut up gcc) */
1423 for (uint32_t iStr = 0; pszDeviceProps[iStr]; iStr++)
1424 {
1425 uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]);
1426 if (u8Hb > 0xf)
1427 continue;
1428
1429 if (fUpper)
1430 u8Value = u8Hb << 4;
1431 else
1432 pThisCC->pbDeviceProps[iHex++] = u8Hb | u8Value;
1433
1434 Assert(iHex < cbOut);
1435 fUpper = !fUpper;
1436 }
1437
1438 Assert(iHex == 0 || fUpper);
1439 pThisCC->cbDeviceProps = iHex;
1440
1441 return VINF_SUCCESS;
1442}
1443
1444
1445/**
1446 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1447 */
1448static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1449{
1450 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1451 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1452 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1453 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1454 int rc;
1455
1456 RT_NOREF(iInstance);
1457 Assert(iInstance == 0);
1458
1459 /*
1460 * Initalize the basic variables so that the destructor always works.
1461 */
1462 pThisCC->pDevIns = pDevIns;
1463 pThisCC->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface;
1464
1465 /*
1466 * Validate and read the configuration.
1467 */
1468 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
1469 "EfiRom|"
1470 "NumCPUs|"
1471 "McfgBase|"
1472 "McfgLength|"
1473 "UUID|"
1474 "UuidLe|"
1475 "IOAPIC|"
1476 "APIC|"
1477 "DmiBIOSFirmwareMajor|"
1478 "DmiBIOSFirmwareMinor|"
1479 "DmiBIOSReleaseDate|"
1480 "DmiBIOSReleaseMajor|"
1481 "DmiBIOSReleaseMinor|"
1482 "DmiBIOSVendor|"
1483 "DmiBIOSVersion|"
1484 "DmiSystemFamily|"
1485 "DmiSystemProduct|"
1486 "DmiSystemSerial|"
1487 "DmiSystemSKU|"
1488 "DmiSystemUuid|"
1489 "DmiSystemVendor|"
1490 "DmiSystemVersion|"
1491 "DmiBoardAssetTag|"
1492 "DmiBoardBoardType|"
1493 "DmiBoardLocInChass|"
1494 "DmiBoardProduct|"
1495 "DmiBoardSerial|"
1496 "DmiBoardVendor|"
1497 "DmiBoardVersion|"
1498 "DmiChassisAssetTag|"
1499 "DmiChassisSerial|"
1500 "DmiChassisType|"
1501 "DmiChassisVendor|"
1502 "DmiChassisVersion|"
1503 "DmiProcManufacturer|"
1504 "DmiProcVersion|"
1505 "DmiOEMVBoxVer|"
1506 "DmiOEMVBoxRev|"
1507 "DmiUseHostInfo|"
1508 "DmiExposeMemoryTable|"
1509 "DmiExposeProcInf|"
1510 "64BitEntry|"
1511 "BootArgs|"
1512 "DeviceProps|"
1513 "GopMode|" // legacy
1514 "GraphicsMode|"
1515 "UgaHorizontalResolution|" // legacy
1516 "UgaVerticalResolution|" // legacy
1517 "GraphicsResolution|"
1518 "NvramFile", "");
1519
1520 /* CPU count (optional). */
1521 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "NumCPUs", &pThisCC->cCpus, 1);
1522 AssertLogRelRCReturn(rc, rc);
1523
1524 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThisCC->u64McfgBase, 0);
1525 if (RT_FAILURE(rc))
1526 return PDMDEV_SET_ERROR(pDevIns, rc,
1527 N_("Configuration error: Querying \"\" as integer failed"));
1528 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThisCC->cbMcfgLength, 0);
1529 if (RT_FAILURE(rc))
1530 return PDMDEV_SET_ERROR(pDevIns, rc,
1531 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1532
1533 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThisCC->u8IOAPIC, 1);
1534 if (RT_FAILURE (rc))
1535 return PDMDEV_SET_ERROR(pDevIns, rc,
1536 N_("Configuration error: Failed to read \"IOAPIC\""));
1537
1538 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThisCC->u8APIC, 1);
1539 if (RT_FAILURE (rc))
1540 return PDMDEV_SET_ERROR(pDevIns, rc,
1541 N_("Configuration error: Failed to read \"APIC\""));
1542
1543 /*
1544 * Query the machine's UUID for SMBIOS/DMI use.
1545 */
1546 RTUUID uuid;
1547 rc = pHlp->pfnCFGMQueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1548 if (RT_FAILURE(rc))
1549 return PDMDEV_SET_ERROR(pDevIns, rc,
1550 N_("Configuration error: Querying \"UUID\" failed"));
1551
1552 bool fUuidLe;
1553 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UuidLe", &fUuidLe, false);
1554 if (RT_FAILURE(rc))
1555 return PDMDEV_SET_ERROR(pDevIns, rc,
1556 N_("Configuration error: Querying \"UuidLe\" failed"));
1557
1558 if (!fUuidLe)
1559 {
1560 /*
1561 * UUIDs are stored little endian actually (see chapter 7.2.1 System — UUID
1562 * of the DMI/SMBIOS spec) but to not force reactivation of existing guests we have
1563 * to carry this bug along... (see also DevPcBios.cpp when changing this)
1564 *
1565 * Convert the UUID to network byte order. Not entirely straightforward as
1566 * parts are MSB already...
1567 */
1568 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1569 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1570 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1571 }
1572 memcpy(&pThisCC->aUuid, &uuid, sizeof pThisCC->aUuid);
1573
1574 /*
1575 * Get the system EFI ROM file name.
1576 */
1577#ifdef VBOX_WITH_EFI_IN_DD2
1578 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "EfiRom", &pThisCC->pszEfiRomFile, g_szEfiBuiltin32);
1579 if (RT_FAILURE(rc))
1580#else
1581 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "EfiRom", &pThisCC->pszEfiRomFile);
1582 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1583 {
1584 pThisCC->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
1585 AssertReturn(pThisCC->pszEfiRomFile, VERR_NO_MEMORY);
1586 rc = RTPathAppPrivateArchTop(pThisCC->pszEfiRomFile, RTPATH_MAX);
1587 AssertRCReturn(rc, rc);
1588 rc = RTPathAppend(pThisCC->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd");
1589 AssertRCReturn(rc, rc);
1590 }
1591 else if (RT_FAILURE(rc))
1592#endif
1593 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1594 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
1595
1596 /*
1597 * Saved State handling.
1598 */
1599 rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThisCC), efiSaveExec, efiLoadExec);
1600 AssertRCReturn(rc, rc);
1601
1602 /*
1603 * NVRAM storage.
1604 */
1605 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->Lun0.IBase, &pThisCC->Lun0.pDrvBase, "NvramStorage");
1606 if (RT_SUCCESS(rc))
1607 {
1608 pThisCC->Lun0.pDrvVfs = PDMIBASE_QUERY_INTERFACE(pThisCC->Lun0.pDrvBase, PDMIVFSCONNECTOR);
1609 if (!pThisCC->Lun0.pDrvVfs)
1610 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("NVRAM storage driver is missing VFS interface below"));
1611 }
1612 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1613 rc = VINF_SUCCESS; /* Missing driver is no error condition. */
1614 else
1615 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
1616
1617 /*
1618 * Get boot args.
1619 */
1620 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "BootArgs", pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs), "");
1621 if (RT_FAILURE(rc))
1622 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1623 N_("Configuration error: Querying \"BootArgs\" as a string failed"));
1624
1625 //strcpy(pThisCC->szBootArgs, "-v keepsyms=1 io=0xf debug=0x2a");
1626 //strcpy(pThisCC->szBootArgs, "-v keepsyms=1 debug=0x2a");
1627 LogRel(("EFI: boot args = %s\n", pThisCC->szBootArgs));
1628
1629 /*
1630 * Get device props.
1631 */
1632 char *pszDeviceProps;
1633 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DeviceProps", &pszDeviceProps, NULL);
1634 if (RT_FAILURE(rc))
1635 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1636 N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
1637 if (pszDeviceProps)
1638 {
1639 LogRel(("EFI: device props = %s\n", pszDeviceProps));
1640 rc = efiParseDeviceString(pThisCC, pszDeviceProps);
1641 PDMDevHlpMMHeapFree(pDevIns, pszDeviceProps);
1642 if (RT_FAILURE(rc))
1643 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1644 N_("Configuration error: Cannot parse device properties"));
1645 }
1646 else
1647 {
1648 pThisCC->pbDeviceProps = NULL;
1649 pThisCC->cbDeviceProps = 0;
1650 }
1651
1652 /*
1653 * CPU frequencies.
1654 */
1655 pThisCC->u64TscFrequency = PDMDevHlpTMCpuTicksPerSecond(pDevIns);
1656 pThisCC->u64CpuFrequency = pThisCC->u64TscFrequency;
1657 pThisCC->u64FsbFrequency = PDMDevHlpCpuGetGuestScalableBusFrequency(pDevIns);
1658
1659 /*
1660 * EFI graphics mode (with new EFI VGA code used only as a fallback, for
1661 * old EFI VGA code the only way to select the GOP mode).
1662 */
1663 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "GraphicsMode", &pThisCC->u32GraphicsMode, UINT32_MAX);
1664 if (RT_FAILURE(rc))
1665 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1666 N_("Configuration error: Querying \"GraphicsMode\" as a 32-bit int failed"));
1667 if (pThisCC->u32GraphicsMode == UINT32_MAX)
1668 {
1669 /* get the legacy value if nothing else was specified */
1670 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "GopMode", &pThisCC->u32GraphicsMode, UINT32_MAX);
1671 if (RT_FAILURE(rc))
1672 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1673 N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed"));
1674 }
1675 if (pThisCC->u32GraphicsMode == UINT32_MAX)
1676 pThisCC->u32GraphicsMode = 2; /* 1024x768, at least typically */
1677
1678 /*
1679 * EFI graphics resolution, defaults to 1024x768 (used to be UGA only, now
1680 * is the main config setting as the mode number is so hard to predict).
1681 */
1682 char szResolution[16];
1683 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "GraphicsResolution", szResolution, sizeof(szResolution), "");
1684 if (RT_FAILURE(rc))
1685 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1686 N_("Configuration error: Querying \"GraphicsResolution\" as a string failed"));
1687 if (szResolution[0])
1688 {
1689 const char *pszX = RTStrStr(szResolution, "x");
1690 if (pszX)
1691 {
1692 pThisCC->u32HorizontalResolution = RTStrToUInt32(szResolution);
1693 pThisCC->u32VerticalResolution = RTStrToUInt32(pszX + 1);
1694 }
1695 }
1696 else
1697 {
1698 /* get the legacy values if nothing else was specified */
1699 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UgaHorizontalResolution", &pThisCC->u32HorizontalResolution, 0);
1700 AssertRCReturn(rc, rc);
1701 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UgaVerticalResolution", &pThisCC->u32VerticalResolution, 0);
1702 AssertRCReturn(rc, rc);
1703 }
1704 if (pThisCC->u32HorizontalResolution == 0 || pThisCC->u32VerticalResolution == 0)
1705 {
1706 pThisCC->u32HorizontalResolution = 1024;
1707 pThisCC->u32VerticalResolution = 768;
1708 }
1709
1710 pThisCC->pszNvramFile = NULL;
1711 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "NvramFile", &pThisCC->pszNvramFile);
1712 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
1713 return PDMDEV_SET_ERROR(pDevIns, rc,
1714 N_("Configuration error: Querying \"NvramFile\" as a string failed"));
1715
1716 /*
1717 * Load firmware volume and thunk ROM.
1718 */
1719 rc = efiLoadRom(pDevIns, pThis, pThisCC, pCfg);
1720 if (RT_FAILURE(rc))
1721 return rc;
1722
1723 /*
1724 * Register our I/O ports.
1725 */
1726 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, IOM_IOPORT_F_ABS,
1727 efiR3IoPortWrite, efiR3IoPortRead,
1728 "EFI communication ports", NULL /*paExtDescs*/, &pThis->hIoPorts);
1729 AssertRCReturn(rc, rc);
1730
1731 /*
1732 * Plant DMI and MPS tables in the ROM region.
1733 */
1734 rc = FwCommonPlantDMITable(pDevIns, pThisCC->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThisCC->aUuid,
1735 pDevIns->pCfg, pThisCC->cCpus, &pThisCC->cbDmiTables, &pThisCC->cNumDmiTables,
1736 true /*fUefi*/);
1737 AssertRCReturn(rc, rc);
1738
1739 /*
1740 * NB: VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.c scans memory for
1741 * the SMBIOS header. The header must be placed in a range that EFI will scan.
1742 */
1743 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThisCC->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1744 pThisCC->cbDmiTables, pThisCC->cNumDmiTables);
1745
1746 if (pThisCC->u8IOAPIC)
1747 {
1748 FwCommonPlantMpsTable(pDevIns,
1749 pThisCC->au8DMIPage /* aka VBOX_DMI_TABLE_BASE */ + VBOX_DMI_TABLE_SIZE + VBOX_DMI_HDR_SIZE,
1750 _4K - VBOX_DMI_TABLE_SIZE - VBOX_DMI_HDR_SIZE, pThisCC->cCpus);
1751 FwCommonPlantMpsFloatPtr(pDevIns, VBOX_DMI_TABLE_BASE + VBOX_DMI_TABLE_SIZE + VBOX_DMI_HDR_SIZE);
1752 }
1753
1754 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThisCC->au8DMIPage, _4K,
1755 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1756
1757 AssertRCReturn(rc, rc);
1758
1759 /*
1760 * Call reset to set things up.
1761 */
1762 efiReset(pDevIns);
1763
1764 return VINF_SUCCESS;
1765}
1766
1767#else /* IN_RING3 */
1768
1769
1770/**
1771 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1772 */
1773static DECLCALLBACK(int) efiRZConstruct(PPDMDEVINS pDevIns)
1774{
1775 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1776 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1777
1778# if 1
1779 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioFlash, efiR3NvMmioWrite, efiR3NvMmioRead, NULL /*pvUser*/);
1780 AssertRCReturn(rc, rc);
1781# else
1782 RT_NOREF(pDevIns, pThis); (void)&efiR3NvMmioRead; (void)&efiR3NvMmioWrite;
1783# endif
1784
1785 return VINF_SUCCESS;
1786}
1787
1788
1789#endif /* IN_RING3 */
1790
1791/**
1792 * The device registration structure.
1793 */
1794const PDMDEVREG g_DeviceEFI =
1795{
1796 /* .u32Version = */ PDM_DEVREG_VERSION,
1797 /* .uReserved0 = */ 0,
1798 /* .szName = */ "efi",
1799 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1800 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
1801 /* .cMaxInstances = */ 1,
1802 /* .uSharedVersion = */ 42,
1803 /* .cbInstanceShared = */ sizeof(DEVEFI),
1804 /* .cbInstanceCC = */ sizeof(DEVEFICC),
1805 /* .cbInstanceRC = */ sizeof(DEVEFIRC),
1806 /* .cMaxPciDevices = */ 0,
1807 /* .cMaxMsixVectors = */ 0,
1808 /* .pszDescription = */ "Extensible Firmware Interface Device.\n"
1809 "LUN#0 - NVRAM port",
1810#if defined(IN_RING3)
1811 /* .pszRCMod = */ "VBoxDDRC.rc",
1812 /* .pszR0Mod = */ "VBoxDDR0.r0",
1813 /* .pfnConstruct = */ efiConstruct,
1814 /* .pfnDestruct = */ efiDestruct,
1815 /* .pfnRelocate = */ NULL,
1816 /* .pfnMemSetup = */ efiMemSetup,
1817 /* .pfnPowerOn = */ NULL,
1818 /* .pfnReset = */ efiReset,
1819 /* .pfnSuspend = */ NULL,
1820 /* .pfnResume = */ NULL,
1821 /* .pfnAttach = */ NULL,
1822 /* .pfnDetach = */ NULL,
1823 /* .pfnQueryInterface = */ NULL,
1824 /* .pfnInitComplete = */ efiInitComplete,
1825 /* .pfnPowerOff = */ efiPowerOff,
1826 /* .pfnSoftReset = */ NULL,
1827 /* .pfnReserved0 = */ NULL,
1828 /* .pfnReserved1 = */ NULL,
1829 /* .pfnReserved2 = */ NULL,
1830 /* .pfnReserved3 = */ NULL,
1831 /* .pfnReserved4 = */ NULL,
1832 /* .pfnReserved5 = */ NULL,
1833 /* .pfnReserved6 = */ NULL,
1834 /* .pfnReserved7 = */ NULL,
1835#elif defined(IN_RING0)
1836 /* .pfnEarlyConstruct = */ NULL,
1837 /* .pfnConstruct = */ efiRZConstruct,
1838 /* .pfnDestruct = */ NULL,
1839 /* .pfnFinalDestruct = */ NULL,
1840 /* .pfnRequest = */ NULL,
1841 /* .pfnReserved0 = */ NULL,
1842 /* .pfnReserved1 = */ NULL,
1843 /* .pfnReserved2 = */ NULL,
1844 /* .pfnReserved3 = */ NULL,
1845 /* .pfnReserved4 = */ NULL,
1846 /* .pfnReserved5 = */ NULL,
1847 /* .pfnReserved6 = */ NULL,
1848 /* .pfnReserved7 = */ NULL,
1849#elif defined(IN_RC)
1850 /* .pfnConstruct = */ efiRZConstruct,
1851 /* .pfnReserved0 = */ NULL,
1852 /* .pfnReserved1 = */ NULL,
1853 /* .pfnReserved2 = */ NULL,
1854 /* .pfnReserved3 = */ NULL,
1855 /* .pfnReserved4 = */ NULL,
1856 /* .pfnReserved5 = */ NULL,
1857 /* .pfnReserved6 = */ NULL,
1858 /* .pfnReserved7 = */ NULL,
1859#else
1860# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1861#endif
1862 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1863};
1864
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