VirtualBox

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

Last change on this file since 82331 was 82214, checked in by vboxsync, 5 years ago

DevEFI: Converted I/O port handler. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.4 KB
Line 
1/* $Id: DevEFI.cpp 82214 2019-11-26 02:51:43Z vboxsync $ */
2/** @file
3 * DevEFI - EFI <-> VirtualBox Integration Framework.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#include <VBox/vmm/pdmnvram.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/ctype.h>
37#include <iprt/file.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40#include <iprt/uuid.h>
41#include <iprt/path.h>
42#include <iprt/string.h>
43#include <iprt/mp.h>
44#include <iprt/list.h>
45#if defined(DEBUG) && defined(IN_RING3)
46# include <iprt/stream.h>
47# define DEVEFI_WITH_VBOXDBG_SCRIPT
48#endif
49#include <iprt/utf16.h>
50
51#include "DevEFI.h"
52#include "FlashCore.h"
53#include "VBoxDD.h"
54#include "VBoxDD2.h"
55#include "../PC/DevFwCommon.h"
56
57/* EFI includes */
58#ifdef IN_RING3
59# ifdef _MSC_VER
60# pragma warning(push)
61# pragma warning(disable:4668)
62# endif
63# include <ProcessorBind.h>
64# ifdef _MSC_VER
65# pragma warning(pop)
66# endif
67# include <Common/UefiBaseTypes.h>
68# include <Common/PiFirmwareVolume.h>
69# include <Common/PiFirmwareFile.h>
70#endif
71
72
73/*********************************************************************************************************************************
74* Structures and Typedefs *
75*********************************************************************************************************************************/
76/**
77 * EFI NVRAM variable.
78 */
79typedef struct EFIVAR
80{
81 /** The list node for the variable. */
82 RTLISTNODE ListNode;
83 /** The unique sequence number of the variable.
84 * This is used to find pCurVar when restoring saved state and therefore only
85 * set when saving. */
86 uint32_t idUniqueSavedState;
87 /** The value attributess. */
88 uint32_t fAttributes;
89 /** The variable name length (not counting the terminator char). */
90 uint32_t cchName;
91 /** The size of the value. This cannot be zero. */
92 uint32_t cbValue;
93 /** The vendor UUID scoping the variable name. */
94 RTUUID uuid;
95 /** The variable name. */
96 char szName[EFI_VARIABLE_NAME_MAX];
97 /** The variable value bytes. */
98 uint8_t abValue[EFI_VARIABLE_VALUE_MAX];
99} EFIVAR;
100/** Pointer to an EFI NVRAM variable. */
101typedef EFIVAR *PEFIVAR;
102/** Pointer to a const EFI NVRAM variable. */
103typedef EFIVAR const *PCEFIVAR;
104/** Pointer to an EFI NVRAM variable pointer. */
105typedef PEFIVAR *PPEFIVAR;
106
107/**
108 * NVRAM state.
109 */
110typedef struct NVRAMDESC
111{
112 /** The current operation. */
113 EFIVAROP enmOp;
114 /** The current status. */
115 uint32_t u32Status;
116 /** The current */
117 uint32_t offOpBuffer;
118 /** The current number of variables. */
119 uint32_t cVariables;
120 /** The list of variables. */
121 RTLISTANCHOR VarList;
122
123 /** The unique variable sequence ID, for the saved state only.
124 * @todo It's part of this structure for hysterical raisins, consider remove it
125 * when changing the saved state format the next time. */
126 uint32_t idUniqueCurVar;
127 /** Variable buffered used both when adding and querying NVRAM variables.
128 * When querying a variable, a copy of it is stored in this buffer and read
129 * from it. When adding, updating or deleting a variable, this buffer is used
130 * to set up the parameters before taking action. */
131 EFIVAR VarOpBuf;
132 /** The current variable. This is only used by EFI_VARIABLE_OP_QUERY_NEXT,
133 * the attribute readers work against the copy in VarOpBuf. */
134 PEFIVAR pCurVar;
135} NVRAMDESC;
136
137
138/**
139 * The EFI device shared state structure.
140 */
141typedef struct DEVEFI
142{
143 /** The flash device containing the NVRAM. */
144 FLASHCORE Flash;
145 /** The 8 I/O ports at 0xEF10 (EFI_PORT_BASE). */
146 IOMIOPORTHANDLE hIoPorts;
147 /** The flash MMIO handle. */
148 IOMMMIOHANDLE hMmioFlash;
149} DEVEFI;
150/** Pointer to the shared EFI state. */
151typedef DEVEFI *PDEVEFI;
152
153/**
154 * The EFI device state structure for ring-3.
155 */
156typedef struct DEVEFIR3
157{
158 /** Pointer back to the device instance. */
159 PPDMDEVINS pDevIns;
160
161 /** EFI message buffer. */
162 char szMsg[VBOX_EFI_DEBUG_BUFFER];
163 /** EFI message buffer index. */
164 uint32_t iMsg;
165
166 /** EFI panic message buffer. */
167 char szPanicMsg[2048];
168 /** EFI panic message buffer index. */
169 uint32_t iPanicMsg;
170
171 struct
172 {
173 /** The current/last image event. */
174 uint8_t uEvt;
175 /** Module path/name offset. */
176 uint8_t offName;
177 /** The offset of the last component in the module path/name. */
178 uint8_t offNameLastComponent;
179 /** Alignment padding. */
180 uint8_t abPadding[5];
181 /** First address associated with the event (image address). */
182 uint64_t uAddr0;
183 /** Second address associated with the event (old image address). */
184 uint64_t uAddr1;
185 /** The size associated with the event (0 if none). */
186 uint64_t cb0;
187 /** The module name. */
188 char szName[256];
189 } ImageEvt;
190
191 /** The system EFI ROM data. */
192 uint8_t const *pu8EfiRom;
193 /** The system EFI ROM data pointer to be passed to RTFileReadAllFree. */
194 uint8_t *pu8EfiRomFree;
195 /** The size of the system EFI ROM. */
196 uint64_t cbEfiRom;
197 /** Offset into the actual ROM within EFI FW volume. */
198 uint64_t offEfiRom;
199 /** The name of the EFI ROM file. */
200 char *pszEfiRomFile;
201 /** Thunk page pointer. */
202 uint8_t *pu8EfiThunk;
203 /** First entry point of the EFI firmware. */
204 RTGCPHYS GCEntryPoint0;
205 /** Second Entry Point (PeiCore)*/
206 RTGCPHYS GCEntryPoint1;
207 /** EFI firmware physical load address. */
208 RTGCPHYS GCLoadAddress;
209 /** Current info selector. */
210 uint32_t iInfoSelector;
211 /** Current info position. */
212 int32_t offInfo;
213
214 /** Number of virtual CPUs. (Config) */
215 uint32_t cCpus;
216
217 /** The size of the DMI tables. */
218 uint16_t cbDmiTables;
219 /** Number of the DMI tables. */
220 uint16_t cNumDmiTables;
221 /** The DMI tables. */
222 uint8_t au8DMIPage[0x1000];
223
224 /** I/O-APIC enabled? */
225 uint8_t u8IOAPIC;
226
227 /** APIC mode to be set up by firmware. */
228 uint8_t u8APIC;
229
230 /** Boot parameters passed to the firmware. */
231 char szBootArgs[256];
232
233 /** Host UUID (for DMI). */
234 RTUUID aUuid;
235
236 /** Device properties buffer. */
237 R3PTRTYPE(uint8_t *) pbDeviceProps;
238 /** Device properties buffer size. */
239 uint32_t cbDeviceProps;
240
241 /** Virtual machine front side bus frequency. */
242 uint64_t u64FsbFrequency;
243 /** Virtual machine time stamp counter frequency. */
244 uint64_t u64TscFrequency;
245 /** Virtual machine CPU frequency. */
246 uint64_t u64CpuFrequency;
247 /** EFI Graphics mode (used as fallback if resolution is not known). */
248 uint32_t u32GraphicsMode;
249 /** EFI Graphics (GOP or UGA) horizontal resolution. */
250 uint32_t u32HorizontalResolution;
251 /** EFI Graphics (GOP or UGA) vertical resolution. */
252 uint32_t u32VerticalResolution;
253 /** Physical address of PCI config space MMIO region */
254 uint64_t u64McfgBase;
255 /** Length of PCI config space MMIO region */
256 uint64_t cbMcfgLength;
257 /** Size of the configured NVRAM device. */
258 uint32_t cbNvram;
259 /** Start address of the NVRAM flash. */
260 RTGCPHYS GCPhysNvram;
261
262 /** NVRAM state variables. */
263 NVRAMDESC NVRAM;
264 /** Filename of the file containing the NVRAM store. */
265 char *pszNvramFile;
266
267 /**
268 * NVRAM port - LUN\#0.
269 */
270 struct
271 {
272 /** The base interface we provide the NVRAM driver. */
273 PDMIBASE IBase;
274 /** The NVRAM driver base interface. */
275 PPDMIBASE pDrvBase;
276 /** The NVRAM interface provided by the driver. */
277 PPDMINVRAMCONNECTOR pNvramDrv;
278 } Lun0;
279} DEVEFIR3;
280/** Pointer to the ring-3 EFI state. */
281typedef DEVEFIR3 *PDEVEFIR3;
282
283
284/**
285 * The EFI device state structure for ring-0.
286 */
287typedef struct DEVEFIR0
288{
289 uint32_t uEmpty;
290} DEVEFIR0;
291/** Pointer to the ring-0 EFI state. */
292typedef DEVEFIR0 *PDEVEFIR0;
293
294
295/**
296 * The EFI device state structure for raw-mode.
297 */
298typedef struct DEVEFIRC
299{
300 uint32_t uEmpty;
301} DEVEFIRC;
302/** Pointer to the raw-mode EFI state. */
303typedef DEVEFIRC *PDEVEFIRC;
304
305
306/** @typedef DEVEFICC
307 * The instance data for the current context. */
308/** @typedef PDEVEFICC
309 * Pointer to the instance data for the current context. */
310#ifdef IN_RING3
311typedef DEVEFIR3 DEVEFICC;
312typedef PDEVEFIR3 PDEVEFICC;
313#elif defined(IN_RING0)
314typedef DEVEFIR0 DEVEFICC;
315typedef PDEVEFIR0 PDEVEFICC;
316#elif defined(IN_RC)
317typedef DEVEFIRC DEVEFICC;
318typedef PDEVEFIRC PDEVEFICC;
319#else
320# error "Not IN_RING3, IN_RING0 or IN_RC"
321#endif
322
323
324/*********************************************************************************************************************************
325* Defined Constants And Macros *
326*********************************************************************************************************************************/
327/** The saved state version. */
328#define EFI_SSM_VERSION 3
329/** The saved state version before working NVRAM support was implemented. */
330#define EFI_SSM_VERSION_PRE_PROPER_NVRAM 2
331/** The saved state version from VBox 4.2. */
332#define EFI_SSM_VERSION_4_2 1
333
334/** Non-volatile EFI variable. */
335#define VBOX_EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
336/** Non-volatile EFI variable. */
337#define VBOX_EFI_VARIABLE_READ_ONLY UINT32_C(0x00000008)
338
339
340/*********************************************************************************************************************************
341* Global Variables *
342*********************************************************************************************************************************/
343#ifdef IN_RING3
344/** Saved state NVRAMDESC field descriptors. */
345static SSMFIELD const g_aEfiNvramDescField[] =
346{
347 SSMFIELD_ENTRY( NVRAMDESC, enmOp),
348 SSMFIELD_ENTRY( NVRAMDESC, u32Status),
349 SSMFIELD_ENTRY( NVRAMDESC, offOpBuffer),
350 SSMFIELD_ENTRY_IGNORE(NVRAMDESC, VarOpBuf),
351 SSMFIELD_ENTRY( NVRAMDESC, cVariables),
352 SSMFIELD_ENTRY_OLD( idUnquireLast, 4),
353 SSMFIELD_ENTRY_IGNORE(NVRAMDESC, VarList),
354 SSMFIELD_ENTRY( NVRAMDESC, idUniqueCurVar),
355 SSMFIELD_ENTRY_IGNORE(NVRAMDESC, pCurVar),
356 SSMFIELD_ENTRY_TERM()
357};
358
359/** Saved state EFIVAR field descriptors. */
360static SSMFIELD const g_aEfiVariableDescFields[] =
361{
362 SSMFIELD_ENTRY_IGNORE(EFIVAR, ListNode),
363 SSMFIELD_ENTRY( EFIVAR, idUniqueSavedState),
364 SSMFIELD_ENTRY( EFIVAR, uuid),
365 SSMFIELD_ENTRY( EFIVAR, szName),
366 SSMFIELD_ENTRY_OLD( cchName, 4),
367 SSMFIELD_ENTRY( EFIVAR, abValue),
368 SSMFIELD_ENTRY( EFIVAR, cbValue),
369 SSMFIELD_ENTRY( EFIVAR, fAttributes),
370 SSMFIELD_ENTRY_TERM()
371};
372
373/** The EfiSystemNvDataFv GUID for NVRAM storage. */
374static const RTUUID g_UuidNvDataFv = { { 0x8d, 0x2b, 0xf1, 0xff, 0x96, 0x76, 0x8b, 0x4c, 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50} };
375
376# ifdef VBOX_WITH_EFI_IN_DD2
377/** Special file name value for indicating the 32-bit built-in EFI firmware. */
378static const char g_szEfiBuiltin32[] = "VBoxEFI32.fd";
379/** Special file name value for indicating the 64-bit built-in EFI firmware. */
380static const char g_szEfiBuiltin64[] = "VBoxEFI64.fd";
381# endif
382#endif /* IN_RING3 */
383
384
385#ifdef IN_RING3
386
387/**
388 * Flushes the variable list.
389 *
390 * @param pThisCC The EFI state for the current context.
391 */
392static void nvramFlushDeviceVariableList(PDEVEFIR3 pThisCC)
393{
394 while (!RTListIsEmpty(&pThisCC->NVRAM.VarList))
395 {
396 PEFIVAR pEfiVar = RTListNodeGetNext(&pThisCC->NVRAM.VarList, EFIVAR, ListNode);
397 RTListNodeRemove(&pEfiVar->ListNode);
398 RTMemFree(pEfiVar);
399 }
400
401 pThisCC->NVRAM.pCurVar = NULL;
402}
403
404/**
405 * This function looks up variable in NVRAM list.
406 */
407static int nvramLookupVariableByUuidAndName(PDEVEFIR3 pThisCC, char *pszVariableName, PCRTUUID pUuid, PPEFIVAR ppEfiVar)
408{
409 LogFlowFunc(("%RTuuid::'%s'\n", pUuid, pszVariableName));
410 size_t const cchVariableName = strlen(pszVariableName);
411 int rc = VERR_NOT_FOUND;
412
413 /*
414 * Start by checking the last variable queried.
415 */
416 if ( pThisCC->NVRAM.pCurVar
417 && pThisCC->NVRAM.pCurVar->cchName == cchVariableName
418 && memcmp(pThisCC->NVRAM.pCurVar->szName, pszVariableName, cchVariableName + 1) == 0
419 && RTUuidCompare(&pThisCC->NVRAM.pCurVar->uuid, pUuid) == 0
420 )
421 {
422 *ppEfiVar = pThisCC->NVRAM.pCurVar;
423 rc = VINF_SUCCESS;
424 }
425 else
426 {
427 /*
428 * Linear list search.
429 */
430 PEFIVAR pEfiVar;
431 RTListForEach(&pThisCC->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
432 {
433 Assert(strlen(pEfiVar->szName) == pEfiVar->cchName);
434 if ( pEfiVar->cchName == cchVariableName
435 && memcmp(pEfiVar->szName, pszVariableName, cchVariableName + 1) == 0
436 && RTUuidCompare(&pEfiVar->uuid, pUuid) == 0)
437 {
438 *ppEfiVar = pEfiVar;
439 rc = VINF_SUCCESS;
440 break;
441 }
442 }
443 }
444
445 LogFlowFunc(("rc=%Rrc pEfiVar=%p\n", rc, *ppEfiVar));
446 return rc;
447}
448
449
450/**
451 * Inserts the EFI variable into the list.
452 *
453 * This enforces the desired list ordering and/or insertion policy.
454 *
455 * @param pThisCC The EFI state for the current context.
456 * @param pEfiVar The variable to insert.
457 */
458static void nvramInsertVariable(PDEVEFIR3 pThisCC, PEFIVAR pEfiVar)
459{
460#if 1
461 /*
462 * Sorted by UUID and name.
463 */
464 PEFIVAR pCurVar;
465 RTListForEach(&pThisCC->NVRAM.VarList, pCurVar, EFIVAR, ListNode)
466 {
467 int iDiff = RTUuidCompare(&pEfiVar->uuid, &pCurVar->uuid);
468 if (!iDiff)
469 iDiff = strcmp(pEfiVar->szName, pCurVar->szName);
470 if (iDiff < 0)
471 {
472 RTListNodeInsertBefore(&pCurVar->ListNode, &pEfiVar->ListNode);
473 return;
474 }
475 }
476#endif
477
478 /*
479 * Add it at the end.
480 */
481 RTListAppend(&pThisCC->NVRAM.VarList, &pEfiVar->ListNode);
482}
483
484
485/**
486 * Creates an device internal list of variables.
487 *
488 * @returns VBox status code.
489 * @param pThisCC The EFI state for the current context.
490 */
491static int nvramLoad(PDEVEFIR3 pThisCC)
492{
493 int rc;
494 for (uint32_t iVar = 0; iVar < EFI_VARIABLE_MAX; iVar++)
495 {
496 PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
497 AssertReturn(pEfiVar, VERR_NO_MEMORY);
498
499 pEfiVar->cchName = sizeof(pEfiVar->szName);
500 pEfiVar->cbValue = sizeof(pEfiVar->abValue);
501 rc = pThisCC->Lun0.pNvramDrv->pfnVarQueryByIndex(pThisCC->Lun0.pNvramDrv, iVar,
502 &pEfiVar->uuid, &pEfiVar->szName[0], &pEfiVar->cchName,
503 &pEfiVar->fAttributes, &pEfiVar->abValue[0], &pEfiVar->cbValue);
504 if (RT_SUCCESS(rc))
505 {
506 /* Some validations. */
507 rc = RTStrValidateEncoding(pEfiVar->szName);
508 size_t cchName = RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
509 if (cchName != pEfiVar->cchName)
510 rc = VERR_INVALID_PARAMETER;
511 if (pEfiVar->cbValue == 0)
512 rc = VERR_NO_DATA;
513 if (RT_FAILURE(rc))
514 LogRel(("EFI/nvramLoad: Bad variable #%u: cbValue=%#x cchName=%#x (strlen=%#x) szName=%.*Rhxs\n",
515 iVar, pEfiVar->cbValue, pEfiVar->cchName, cchName, pEfiVar->cchName + 1, pEfiVar->szName));
516 }
517 if (RT_FAILURE(rc))
518 {
519 RTMemFree(pEfiVar);
520 if (rc == VERR_NOT_FOUND)
521 rc = VINF_SUCCESS;
522 AssertRC(rc);
523 return rc;
524 }
525
526 /* Append it. */
527 nvramInsertVariable(pThisCC, pEfiVar);
528 pThisCC->NVRAM.cVariables++;
529 }
530
531 AssertLogRelMsgFailed(("EFI: Too many variables.\n"));
532 return VERR_TOO_MUCH_DATA;
533}
534
535
536/**
537 * Let the NVRAM driver store the internal NVRAM variable list.
538 *
539 * @returns VBox status code.
540 * @param pThisCC The EFI state for the current context.
541 */
542static int nvramStore(PDEVEFIR3 pThisCC)
543{
544 /*
545 * Count the non-volatile variables and issue the begin call.
546 */
547 PEFIVAR pEfiVar;
548 uint32_t cNonVolatile = 0;
549 RTListForEach(&pThisCC->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
550 if (pEfiVar->fAttributes & VBOX_EFI_VARIABLE_NON_VOLATILE)
551 cNonVolatile++;
552 int rc = pThisCC->Lun0.pNvramDrv->pfnVarStoreSeqBegin(pThisCC->Lun0.pNvramDrv, cNonVolatile);
553 if (RT_SUCCESS(rc))
554 {
555 /*
556 * Store each non-volatile variable.
557 */
558 uint32_t idxVar = 0;
559 RTListForEach(&pThisCC->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
560 {
561 /* Skip volatile variables. */
562 if (!(pEfiVar->fAttributes & VBOX_EFI_VARIABLE_NON_VOLATILE))
563 continue;
564
565 int rc2 = pThisCC->Lun0.pNvramDrv->pfnVarStoreSeqPut(pThisCC->Lun0.pNvramDrv, idxVar,
566 &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cchName,
567 pEfiVar->fAttributes, pEfiVar->abValue, pEfiVar->cbValue);
568 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rc))
569 {
570 LogRel(("EFI: pfnVarStoreVarByIndex failed: %Rrc\n", rc));
571 rc = rc2;
572 }
573 idxVar++;
574 }
575 Assert(idxVar == cNonVolatile);
576
577 /*
578 * Done.
579 */
580 rc = pThisCC->Lun0.pNvramDrv->pfnVarStoreSeqEnd(pThisCC->Lun0.pNvramDrv, rc);
581 }
582 else
583 LogRel(("EFI: pfnVarStoreBegin failed: %Rrc\n", rc));
584 return rc;
585}
586
587/**
588 * EFI_VARIABLE_OP_QUERY and EFI_VARIABLE_OP_QUERY_NEXT worker that copies the
589 * variable into the VarOpBuf, set pCurVar and u32Status.
590 *
591 * @param pThisCC The EFI state for the current context.
592 * @param pEfiVar The resulting variable. NULL if not found / end.
593 * @param fEnumQuery Set if enumeration query, clear if specific.
594 */
595static void nvramWriteVariableOpQueryCopyResult(PDEVEFIR3 pThisCC, PEFIVAR pEfiVar, bool fEnumQuery)
596{
597 RT_ZERO(pThisCC->NVRAM.VarOpBuf.abValue);
598 if (pEfiVar)
599 {
600 RT_ZERO(pThisCC->NVRAM.VarOpBuf.szName);
601 pThisCC->NVRAM.VarOpBuf.uuid = pEfiVar->uuid;
602 pThisCC->NVRAM.VarOpBuf.cchName = pEfiVar->cchName;
603 memcpy(pThisCC->NVRAM.VarOpBuf.szName, pEfiVar->szName, pEfiVar->cchName); /* no need for + 1. */
604 pThisCC->NVRAM.VarOpBuf.fAttributes = pEfiVar->fAttributes;
605 pThisCC->NVRAM.VarOpBuf.cbValue = pEfiVar->cbValue;
606 memcpy(pThisCC->NVRAM.VarOpBuf.abValue, pEfiVar->abValue, pEfiVar->cbValue);
607 pThisCC->NVRAM.pCurVar = pEfiVar;
608 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
609 LogFlow(("EFI: Variable query -> %RTuuid::'%s' (%d) abValue=%.*Rhxs\n", &pThisCC->NVRAM.VarOpBuf.uuid,
610 pThisCC->NVRAM.VarOpBuf.szName, pThisCC->NVRAM.VarOpBuf.cchName,
611 pThisCC->NVRAM.VarOpBuf.cbValue, pThisCC->NVRAM.VarOpBuf.abValue));
612 }
613 else
614 {
615 if (fEnumQuery)
616 LogFlow(("EFI: Variable query -> NOT_FOUND \n"));
617 else
618 LogFlow(("EFI: Variable query %RTuuid::'%s' -> NOT_FOUND \n",
619 &pThisCC->NVRAM.VarOpBuf.uuid, pThisCC->NVRAM.VarOpBuf.szName));
620 RT_ZERO(pThisCC->NVRAM.VarOpBuf.szName);
621 pThisCC->NVRAM.VarOpBuf.fAttributes = 0;
622 pThisCC->NVRAM.VarOpBuf.cbValue = 0;
623 pThisCC->NVRAM.VarOpBuf.cchName = 0;
624 pThisCC->NVRAM.pCurVar = NULL;
625 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_NOT_FOUND;
626 }
627}
628
629/**
630 * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_QUERY.
631 *
632 * @returns IOM strict status code.
633 * @param pThisCC The EFI state for the current context.
634 */
635static int nvramWriteVariableOpQuery(PDEVEFIR3 pThisCC)
636{
637 Log(("EFI_VARIABLE_OP_QUERY: %RTuuid::'%s'\n", &pThisCC->NVRAM.VarOpBuf.uuid, pThisCC->NVRAM.VarOpBuf.szName));
638
639 PEFIVAR pEfiVar;
640 int rc = nvramLookupVariableByUuidAndName(pThisCC,
641 pThisCC->NVRAM.VarOpBuf.szName,
642 &pThisCC->NVRAM.VarOpBuf.uuid,
643 &pEfiVar);
644 nvramWriteVariableOpQueryCopyResult(pThisCC, RT_SUCCESS(rc) ? pEfiVar : NULL, false /*fEnumQuery*/);
645 return VINF_SUCCESS;
646}
647
648/**
649 * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_QUERY_NEXT.
650 *
651 * This simply walks the list.
652 *
653 * @returns IOM strict status code.
654 * @param pThisCC The EFI state for the current context.
655 */
656static int nvramWriteVariableOpQueryNext(PDEVEFIR3 pThisCC)
657{
658 Log(("EFI_VARIABLE_OP_QUERY_NEXT: pCurVar=%p\n", pThisCC->NVRAM.pCurVar));
659 PEFIVAR pEfiVar = pThisCC->NVRAM.pCurVar;
660 if (pEfiVar)
661 pEfiVar = RTListGetNext(&pThisCC->NVRAM.VarList, pEfiVar, EFIVAR, ListNode);
662 else
663 pEfiVar = RTListGetFirst(&pThisCC->NVRAM.VarList, EFIVAR, ListNode);
664 nvramWriteVariableOpQueryCopyResult(pThisCC, pEfiVar, true /* fEnumQuery */);
665 return VINF_SUCCESS;
666}
667
668/**
669 * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_ADD.
670 *
671 * @returns IOM strict status code.
672 * @param pThisCC The EFI state for the current context.
673 */
674static int nvramWriteVariableOpAdd(PDEVEFIR3 pThisCC)
675{
676 Log(("EFI_VARIABLE_OP_ADD: %RTuuid::'%s' fAttributes=%#x abValue=%.*Rhxs\n",
677 &pThisCC->NVRAM.VarOpBuf.uuid, pThisCC->NVRAM.VarOpBuf.szName, pThisCC->NVRAM.VarOpBuf.fAttributes,
678 pThisCC->NVRAM.VarOpBuf.cbValue, pThisCC->NVRAM.VarOpBuf.abValue));
679
680 /*
681 * Validate and adjust the input a little before we start.
682 */
683 int rc = RTStrValidateEncoding(pThisCC->NVRAM.VarOpBuf.szName);
684 if (RT_FAILURE(rc))
685 LogRel(("EFI: Badly encoded variable name: %.*Rhxs\n", pThisCC->NVRAM.VarOpBuf.cchName + 1, pThisCC->NVRAM.VarOpBuf.szName));
686 if (RT_FAILURE(rc))
687 {
688 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
689 return VINF_SUCCESS;
690 }
691 pThisCC->NVRAM.VarOpBuf.cchName = (uint32_t)RTStrNLen(pThisCC->NVRAM.VarOpBuf.szName, sizeof(pThisCC->NVRAM.VarOpBuf.szName));
692
693 /*
694 * Look it up and see what to do.
695 */
696 PEFIVAR pEfiVar;
697 rc = nvramLookupVariableByUuidAndName(pThisCC,
698 pThisCC->NVRAM.VarOpBuf.szName,
699 &pThisCC->NVRAM.VarOpBuf.uuid,
700 &pEfiVar);
701 if (RT_SUCCESS(rc))
702 {
703 LogFlowFunc(("Old abValue=%.*Rhxs\n", pEfiVar->cbValue, pEfiVar->abValue));
704#if 0 /** @todo Implement read-only EFI variables. */
705 if (pEfiVar->fAttributes & EFI_VARIABLE_XXXXXXX)
706 {
707 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_RO;
708 break;
709 }
710#endif
711
712 if (pThisCC->NVRAM.VarOpBuf.cbValue == 0)
713 {
714 /*
715 * Delete it.
716 */
717 LogRel(("EFI: Deleting variable %RTuuid::'%s'\n", &pThisCC->NVRAM.VarOpBuf.uuid, pThisCC->NVRAM.VarOpBuf.szName));
718 RTListNodeRemove(&pEfiVar->ListNode);
719 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
720 pThisCC->NVRAM.cVariables--;
721
722 if (pThisCC->NVRAM.pCurVar == pEfiVar)
723 pThisCC->NVRAM.pCurVar = NULL;
724 RTMemFree(pEfiVar);
725 pEfiVar = NULL;
726 }
727 else
728 {
729 /*
730 * Update/replace it. (The name and UUID are unchanged, of course.)
731 */
732 LogRel(("EFI: Replacing variable %RTuuid::'%s' fAttrib=%#x cbValue=%#x\n", &pThisCC->NVRAM.VarOpBuf.uuid,
733 pThisCC->NVRAM.VarOpBuf.szName, pThisCC->NVRAM.VarOpBuf.fAttributes, pThisCC->NVRAM.VarOpBuf.cbValue));
734 pEfiVar->fAttributes = pThisCC->NVRAM.VarOpBuf.fAttributes;
735 pEfiVar->cbValue = pThisCC->NVRAM.VarOpBuf.cbValue;
736 memcpy(pEfiVar->abValue, pThisCC->NVRAM.VarOpBuf.abValue, pEfiVar->cbValue);
737 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
738 }
739 }
740 else if (pThisCC->NVRAM.VarOpBuf.cbValue == 0)
741 {
742 /* delete operation, but nothing to delete. */
743 LogFlow(("nvramWriteVariableOpAdd: Delete (not found)\n"));
744 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
745 }
746 else if (pThisCC->NVRAM.cVariables < EFI_VARIABLE_MAX)
747 {
748 /*
749 * Add a new variable.
750 */
751 LogRel(("EFI: Adding variable %RTuuid::'%s' fAttrib=%#x cbValue=%#x\n", &pThisCC->NVRAM.VarOpBuf.uuid,
752 pThisCC->NVRAM.VarOpBuf.szName, pThisCC->NVRAM.VarOpBuf.fAttributes, pThisCC->NVRAM.VarOpBuf.cbValue));
753 pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
754 if (pEfiVar)
755 {
756 pEfiVar->uuid = pThisCC->NVRAM.VarOpBuf.uuid;
757 pEfiVar->cchName = pThisCC->NVRAM.VarOpBuf.cchName;
758 memcpy(pEfiVar->szName, pThisCC->NVRAM.VarOpBuf.szName, pEfiVar->cchName); /* The buffer is zeroed, so skip '\0'. */
759 pEfiVar->fAttributes = pThisCC->NVRAM.VarOpBuf.fAttributes;
760 pEfiVar->cbValue = pThisCC->NVRAM.VarOpBuf.cbValue;
761 memcpy(pEfiVar->abValue, pThisCC->NVRAM.VarOpBuf.abValue, pEfiVar->cbValue);
762
763 nvramInsertVariable(pThisCC, pEfiVar);
764 pThisCC->NVRAM.cVariables++;
765 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
766 }
767 else
768 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
769 }
770 else
771 {
772 /*
773 * Too many variables.
774 */
775 LogRelMax(5, ("EFI: Too many variables (%RTuuid::'%s' fAttrib=%#x cbValue=%#x)\n", &pThisCC->NVRAM.VarOpBuf.uuid,
776 pThisCC->NVRAM.VarOpBuf.szName, pThisCC->NVRAM.VarOpBuf.fAttributes, pThisCC->NVRAM.VarOpBuf.cbValue));
777 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
778 Log(("nvramWriteVariableOpAdd: Too many variabled.\n"));
779 }
780
781 /*
782 * Log the value of bugcheck variables.
783 */
784 if ( ( pThisCC->NVRAM.VarOpBuf.cbValue == 4
785 || pThisCC->NVRAM.VarOpBuf.cbValue == 8)
786 && ( strcmp(pThisCC->NVRAM.VarOpBuf.szName, "BugCheckCode") == 0
787 || strcmp(pThisCC->NVRAM.VarOpBuf.szName, "BugCheckParameter0") == 0
788 || strcmp(pThisCC->NVRAM.VarOpBuf.szName, "BugCheckParameter1") == 0
789 || strcmp(pThisCC->NVRAM.VarOpBuf.szName, "BugCheckParameter2") == 0
790 || strcmp(pThisCC->NVRAM.VarOpBuf.szName, "BugCheckParameter3") == 0
791 || strcmp(pThisCC->NVRAM.VarOpBuf.szName, "BugCheckProgress") == 0 ) )
792 {
793 if (pThisCC->NVRAM.VarOpBuf.cbValue == 4)
794 LogRel(("EFI: %RTuuid::'%s' = %#010RX32\n", &pThisCC->NVRAM.VarOpBuf.uuid, pThisCC->NVRAM.VarOpBuf.szName,
795 RT_MAKE_U32_FROM_U8(pThisCC->NVRAM.VarOpBuf.abValue[0], pThisCC->NVRAM.VarOpBuf.abValue[1],
796 pThisCC->NVRAM.VarOpBuf.abValue[2], pThisCC->NVRAM.VarOpBuf.abValue[3])));
797 else
798 LogRel(("EFI: %RTuuid::'%s' = %#018RX64\n", &pThisCC->NVRAM.VarOpBuf.uuid, pThisCC->NVRAM.VarOpBuf.szName,
799 RT_MAKE_U64_FROM_U8(pThisCC->NVRAM.VarOpBuf.abValue[0], pThisCC->NVRAM.VarOpBuf.abValue[1],
800 pThisCC->NVRAM.VarOpBuf.abValue[2], pThisCC->NVRAM.VarOpBuf.abValue[3],
801 pThisCC->NVRAM.VarOpBuf.abValue[4], pThisCC->NVRAM.VarOpBuf.abValue[5],
802 pThisCC->NVRAM.VarOpBuf.abValue[6], pThisCC->NVRAM.VarOpBuf.abValue[7])));
803 }
804
805
806 LogFunc(("cVariables=%u u32Status=%#x\n", pThisCC->NVRAM.cVariables, pThisCC->NVRAM.u32Status));
807 return VINF_SUCCESS;
808}
809
810/**
811 * Implements EFI_VARIABLE_PARAM writes.
812 *
813 * @returns IOM strict status code.
814 * @param pThisCC The EFI state for the current context.
815 * @param u32Value The value being written.
816 */
817static int nvramWriteVariableParam(PDEVEFIR3 pThisCC, uint32_t u32Value)
818{
819 int rc = VINF_SUCCESS;
820 switch (pThisCC->NVRAM.enmOp)
821 {
822 case EFI_VM_VARIABLE_OP_START:
823 switch (u32Value)
824 {
825 case EFI_VARIABLE_OP_QUERY:
826 rc = nvramWriteVariableOpQuery(pThisCC);
827 break;
828
829 case EFI_VARIABLE_OP_QUERY_NEXT:
830 rc = nvramWriteVariableOpQueryNext(pThisCC);
831 break;
832
833 case EFI_VARIABLE_OP_QUERY_REWIND:
834 Log2(("EFI_VARIABLE_OP_QUERY_REWIND\n"));
835 pThisCC->NVRAM.pCurVar = NULL;
836 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
837 break;
838
839 case EFI_VARIABLE_OP_ADD:
840 rc = nvramWriteVariableOpAdd(pThisCC);
841 break;
842
843 default:
844 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
845 LogRel(("EFI: Unknown EFI_VM_VARIABLE_OP_START value %#x\n", u32Value));
846 break;
847 }
848 break;
849
850 case EFI_VM_VARIABLE_OP_GUID:
851 Log2(("EFI_VM_VARIABLE_OP_GUID[%#x]=%#x\n", pThisCC->NVRAM.offOpBuffer, u32Value));
852 if (pThisCC->NVRAM.offOpBuffer < sizeof(pThisCC->NVRAM.VarOpBuf.uuid))
853 pThisCC->NVRAM.VarOpBuf.uuid.au8[pThisCC->NVRAM.offOpBuffer++] = (uint8_t)u32Value;
854 else
855 {
856 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_GUID write (%#x).\n", u32Value));
857 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
858 }
859 break;
860
861 case EFI_VM_VARIABLE_OP_ATTRIBUTE:
862 Log2(("EFI_VM_VARIABLE_OP_ATTRIBUTE=%#x\n", u32Value));
863 pThisCC->NVRAM.VarOpBuf.fAttributes = u32Value;
864 break;
865
866 case EFI_VM_VARIABLE_OP_NAME:
867 Log2(("EFI_VM_VARIABLE_OP_NAME[%#x]=%#x\n", pThisCC->NVRAM.offOpBuffer, u32Value));
868 if (pThisCC->NVRAM.offOpBuffer < pThisCC->NVRAM.VarOpBuf.cchName)
869 pThisCC->NVRAM.VarOpBuf.szName[pThisCC->NVRAM.offOpBuffer++] = (uint8_t)u32Value;
870 else if (u32Value == 0)
871 Assert(pThisCC->NVRAM.VarOpBuf.szName[sizeof(pThisCC->NVRAM.VarOpBuf.szName) - 1] == 0);
872 else
873 {
874 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME write (%#x).\n", u32Value));
875 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
876 }
877 break;
878
879 case EFI_VM_VARIABLE_OP_NAME_LENGTH:
880 Log2(("EFI_VM_VARIABLE_OP_NAME_LENGTH=%#x\n", u32Value));
881 RT_ZERO(pThisCC->NVRAM.VarOpBuf.szName);
882 if (u32Value < sizeof(pThisCC->NVRAM.VarOpBuf.szName))
883 pThisCC->NVRAM.VarOpBuf.cchName = u32Value;
884 else
885 {
886 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME_LENGTH write (%#x, max %#x).\n",
887 u32Value, sizeof(pThisCC->NVRAM.VarOpBuf.szName) - 1));
888 pThisCC->NVRAM.VarOpBuf.cchName = sizeof(pThisCC->NVRAM.VarOpBuf.szName) - 1;
889 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
890 }
891 Assert(pThisCC->NVRAM.offOpBuffer == 0);
892 break;
893
894 case EFI_VM_VARIABLE_OP_NAME_UTF16:
895 {
896 Log2(("EFI_VM_VARIABLE_OP_NAME_UTF16[%#x]=%#x\n", pThisCC->NVRAM.offOpBuffer, u32Value));
897 /* Currently simplifying this to UCS2, i.e. no surrogates. */
898 if (pThisCC->NVRAM.offOpBuffer == 0)
899 RT_ZERO(pThisCC->NVRAM.VarOpBuf.szName);
900 uint32_t cbUtf8 = (uint32_t)RTStrCpSize(u32Value);
901 if (pThisCC->NVRAM.offOpBuffer + cbUtf8 < sizeof(pThisCC->NVRAM.VarOpBuf.szName))
902 {
903 RTStrPutCp(&pThisCC->NVRAM.VarOpBuf.szName[pThisCC->NVRAM.offOpBuffer], u32Value);
904 pThisCC->NVRAM.offOpBuffer += cbUtf8;
905 }
906 else if (u32Value == 0)
907 Assert(pThisCC->NVRAM.VarOpBuf.szName[sizeof(pThisCC->NVRAM.VarOpBuf.szName) - 1] == 0);
908 else
909 {
910 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME_UTF16 write (%#x).\n", u32Value));
911 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
912 }
913 break;
914 }
915
916 case EFI_VM_VARIABLE_OP_VALUE:
917 Log2(("EFI_VM_VARIABLE_OP_VALUE[%#x]=%#x\n", pThisCC->NVRAM.offOpBuffer, u32Value));
918 if (pThisCC->NVRAM.offOpBuffer < pThisCC->NVRAM.VarOpBuf.cbValue)
919 pThisCC->NVRAM.VarOpBuf.abValue[pThisCC->NVRAM.offOpBuffer++] = (uint8_t)u32Value;
920 else
921 {
922 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_VALUE write (%#x).\n", u32Value));
923 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
924 }
925 break;
926
927 case EFI_VM_VARIABLE_OP_VALUE_LENGTH:
928 Log2(("EFI_VM_VARIABLE_OP_VALUE_LENGTH=%#x\n", u32Value));
929 RT_ZERO(pThisCC->NVRAM.VarOpBuf.abValue);
930 if (u32Value <= sizeof(pThisCC->NVRAM.VarOpBuf.abValue))
931 pThisCC->NVRAM.VarOpBuf.cbValue = u32Value;
932 else
933 {
934 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_VALUE_LENGTH write (%#x, max %#x).\n",
935 u32Value, sizeof(pThisCC->NVRAM.VarOpBuf.abValue)));
936 pThisCC->NVRAM.VarOpBuf.cbValue = sizeof(pThisCC->NVRAM.VarOpBuf.abValue);
937 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
938 }
939 Assert(pThisCC->NVRAM.offOpBuffer == 0);
940 break;
941
942 default:
943 pThisCC->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
944 LogRel(("EFI: Unexpected variable operation %#x\n", pThisCC->NVRAM.enmOp));
945 break;
946 }
947 return VINF_SUCCESS;
948}
949
950/**
951 * Implements EFI_VARIABLE_OP reads.
952 *
953 * @returns IOM strict status code.
954 * @param pThisCC The EFI state for the current context.
955 * @param u32Value The value being written.
956 */
957static int nvramReadVariableOp(PDEVEFIR3 pThisCC, uint32_t *pu32, unsigned cb)
958{
959 switch (pThisCC->NVRAM.enmOp)
960 {
961 case EFI_VM_VARIABLE_OP_START:
962 *pu32 = pThisCC->NVRAM.u32Status;
963 break;
964
965 case EFI_VM_VARIABLE_OP_GUID:
966 if (pThisCC->NVRAM.offOpBuffer < sizeof(pThisCC->NVRAM.VarOpBuf.uuid) && cb == 1)
967 *pu32 = pThisCC->NVRAM.VarOpBuf.uuid.au8[pThisCC->NVRAM.offOpBuffer++];
968 else
969 {
970 if (cb == 1)
971 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_GUID read.\n"));
972 else
973 LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_GUID read size (%d).\n", cb));
974 *pu32 = UINT32_MAX;
975 }
976 break;
977
978 case EFI_VM_VARIABLE_OP_ATTRIBUTE:
979 *pu32 = pThisCC->NVRAM.VarOpBuf.fAttributes;
980 break;
981
982 case EFI_VM_VARIABLE_OP_NAME:
983 /* allow reading terminator char */
984 if (pThisCC->NVRAM.offOpBuffer <= pThisCC->NVRAM.VarOpBuf.cchName && cb == 1)
985 *pu32 = pThisCC->NVRAM.VarOpBuf.szName[pThisCC->NVRAM.offOpBuffer++];
986 else
987 {
988 if (cb == 1)
989 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME read.\n"));
990 else
991 LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_NAME read size (%d).\n", cb));
992 *pu32 = UINT32_MAX;
993 }
994 break;
995
996 case EFI_VM_VARIABLE_OP_NAME_LENGTH:
997 *pu32 = pThisCC->NVRAM.VarOpBuf.cchName;
998 break;
999
1000 case EFI_VM_VARIABLE_OP_NAME_UTF16:
1001 /* Lazy bird: ASSUME no surrogate pairs. */
1002 if (pThisCC->NVRAM.offOpBuffer <= pThisCC->NVRAM.VarOpBuf.cchName && cb == 2)
1003 {
1004 char const *psz1 = &pThisCC->NVRAM.VarOpBuf.szName[pThisCC->NVRAM.offOpBuffer];
1005 char const *psz2 = psz1;
1006 RTUNICP Cp;
1007 RTStrGetCpEx(&psz2, &Cp);
1008 *pu32 = Cp;
1009 Log2(("EFI_VM_VARIABLE_OP_NAME_UTF16[%u] => %#x (+%d)\n", pThisCC->NVRAM.offOpBuffer, *pu32, psz2 - psz1));
1010 pThisCC->NVRAM.offOpBuffer += psz2 - psz1;
1011 }
1012 else
1013 {
1014 if (cb == 2)
1015 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME_UTF16 read.\n"));
1016 else
1017 LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_NAME_UTF16 read size (%d).\n", cb));
1018 *pu32 = UINT32_MAX;
1019 }
1020 break;
1021
1022 case EFI_VM_VARIABLE_OP_NAME_LENGTH_UTF16:
1023 /* Lazy bird: ASSUME no surrogate pairs. */
1024 *pu32 = (uint32_t)RTStrUniLen(pThisCC->NVRAM.VarOpBuf.szName);
1025 break;
1026
1027 case EFI_VM_VARIABLE_OP_VALUE:
1028 if (pThisCC->NVRAM.offOpBuffer < pThisCC->NVRAM.VarOpBuf.cbValue && cb == 1)
1029 *pu32 = pThisCC->NVRAM.VarOpBuf.abValue[pThisCC->NVRAM.offOpBuffer++];
1030 else
1031 {
1032 if (cb == 1)
1033 LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_VALUE read.\n"));
1034 else
1035 LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_VALUE read size (%d).\n", cb));
1036 *pu32 = UINT32_MAX;
1037 }
1038 break;
1039
1040 case EFI_VM_VARIABLE_OP_VALUE_LENGTH:
1041 *pu32 = pThisCC->NVRAM.VarOpBuf.cbValue;
1042 break;
1043
1044 default:
1045 *pu32 = UINT32_MAX;
1046 break;
1047 }
1048 return VINF_SUCCESS;
1049}
1050
1051
1052/**
1053 * Checks if the EFI variable value looks like a printable UTF-8 string.
1054 *
1055 * @returns true if it is, false if not.
1056 * @param pEfiVar The variable.
1057 * @param pfZeroTerm Where to return whether the string is zero
1058 * terminated.
1059 */
1060static bool efiInfoNvramIsUtf8(PCEFIVAR pEfiVar, bool *pfZeroTerm)
1061{
1062 if (pEfiVar->cbValue < 2)
1063 return false;
1064 const char *pachValue = (const char *)&pEfiVar->abValue[0];
1065 *pfZeroTerm = pachValue[pEfiVar->cbValue - 1] == 0;
1066
1067 /* Check the length. */
1068 size_t cchValue = RTStrNLen((const char *)pEfiVar->abValue, pEfiVar->cbValue);
1069 if (cchValue != pEfiVar->cbValue - *pfZeroTerm)
1070 return false; /* stray zeros in the value, forget it. */
1071
1072 /* Check that the string is valid UTF-8 and printable. */
1073 const char *pchCur = pachValue;
1074 while ((uintptr_t)(pchCur - pachValue) < cchValue)
1075 {
1076 RTUNICP uc;
1077 int rc = RTStrGetCpEx(&pachValue, &uc);
1078 if (RT_FAILURE(rc))
1079 return false;
1080 /** @todo Missing RTUniCpIsPrintable. */
1081 if (uc < 128 && !RT_C_IS_PRINT(uc))
1082 return false;
1083 }
1084
1085 return true;
1086}
1087
1088
1089/**
1090 * Checks if the EFI variable value looks like a printable UTF-16 string.
1091 *
1092 * @returns true if it is, false if not.
1093 * @param pEfiVar The variable.
1094 * @param pfZeroTerm Where to return whether the string is zero
1095 * terminated.
1096 */
1097static bool efiInfoNvramIsUtf16(PCEFIVAR pEfiVar, bool *pfZeroTerm)
1098{
1099 if (pEfiVar->cbValue < 4 || (pEfiVar->cbValue & 1))
1100 return false;
1101
1102 PCRTUTF16 pwcValue = (PCRTUTF16)&pEfiVar->abValue[0];
1103 size_t cwcValue = pEfiVar->cbValue / sizeof(RTUTF16);
1104 *pfZeroTerm = pwcValue[cwcValue - 1] == 0;
1105 if (!*pfZeroTerm && RTUtf16IsHighSurrogate(pwcValue[cwcValue - 1]))
1106 return false; /* Catch bad string early, before reading a char too many. */
1107 cwcValue -= *pfZeroTerm;
1108 if (cwcValue < 2)
1109 return false;
1110
1111 /* Check that the string is valid UTF-16, printable and spans the whole
1112 value length. */
1113 size_t cAscii = 0;
1114 PCRTUTF16 pwcCur = pwcValue;
1115 while ((uintptr_t)(pwcCur - pwcValue) < cwcValue)
1116 {
1117 RTUNICP uc;
1118 int rc = RTUtf16GetCpEx(&pwcCur, &uc);
1119 if (RT_FAILURE(rc))
1120 return false;
1121 /** @todo Missing RTUniCpIsPrintable. */
1122 if (uc < 128 && !RT_C_IS_PRINT(uc))
1123 return false;
1124 cAscii += uc < 128;
1125 }
1126 if (cAscii < 2)
1127 return false;
1128
1129 return true;
1130}
1131
1132
1133/**
1134 * @implement_callback_method{FNDBGFHANDLERDEV}
1135 */
1136static DECLCALLBACK(void) efiInfoNvram(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1137{
1138 RT_NOREF(pszArgs);
1139 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1140 PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1141
1142 pHlp->pfnPrintf(pHlp, "NVRAM variables: %u\n", pThisCC->NVRAM.cVariables);
1143 PCEFIVAR pEfiVar;
1144 RTListForEach(&pThisCC->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
1145 {
1146 /* Detect UTF-8 and UTF-16 strings. */
1147 bool fZeroTerm = false;
1148 if (efiInfoNvramIsUtf8(pEfiVar, &fZeroTerm))
1149 pHlp->pfnPrintf(pHlp,
1150 "Variable - fAttr=%#04x - '%RTuuid:%s' - cb=%#04x\n"
1151 "String value (UTF-8%s): \"%.*s\"\n",
1152 pEfiVar->fAttributes, &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cbValue,
1153 fZeroTerm ? "" : ",nz", pEfiVar->cbValue, pEfiVar->abValue);
1154 else if (efiInfoNvramIsUtf16(pEfiVar, &fZeroTerm))
1155 pHlp->pfnPrintf(pHlp,
1156 "Variable - fAttr=%#04x - '%RTuuid:%s' - cb=%#04x\n"
1157 "String value (UTF-16%s): \"%.*ls\"\n",
1158 pEfiVar->fAttributes, &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cbValue,
1159 fZeroTerm ? "" : ",nz", pEfiVar->cbValue, pEfiVar->abValue);
1160 else
1161 pHlp->pfnPrintf(pHlp,
1162 "Variable - fAttr=%#04x - '%RTuuid:%s' - cb=%#04x\n"
1163 "%.*Rhxd\n",
1164 pEfiVar->fAttributes, &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cbValue,
1165 pEfiVar->cbValue, pEfiVar->abValue);
1166
1167 }
1168
1169 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1170}
1171
1172
1173
1174/**
1175 * Gets the info item size.
1176 *
1177 * @returns Size in bytes, UINT32_MAX on error.
1178 * @param pThisCC The EFI state for the current context.
1179 */
1180static uint32_t efiInfoSize(PDEVEFIR3 pThisCC)
1181{
1182 switch (pThisCC->iInfoSelector)
1183 {
1184 case EFI_INFO_INDEX_VOLUME_BASE:
1185 case EFI_INFO_INDEX_VOLUME_SIZE:
1186 case EFI_INFO_INDEX_TEMPMEM_BASE:
1187 case EFI_INFO_INDEX_TEMPMEM_SIZE:
1188 case EFI_INFO_INDEX_STACK_BASE:
1189 case EFI_INFO_INDEX_STACK_SIZE:
1190 case EFI_INFO_INDEX_GRAPHICS_MODE:
1191 case EFI_INFO_INDEX_VERTICAL_RESOLUTION:
1192 case EFI_INFO_INDEX_HORIZONTAL_RESOLUTION:
1193 return 4;
1194 case EFI_INFO_INDEX_BOOT_ARGS:
1195 return (uint32_t)RTStrNLen(pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs)) + 1;
1196 case EFI_INFO_INDEX_DEVICE_PROPS:
1197 return pThisCC->cbDeviceProps;
1198 case EFI_INFO_INDEX_FSB_FREQUENCY:
1199 case EFI_INFO_INDEX_CPU_FREQUENCY:
1200 case EFI_INFO_INDEX_TSC_FREQUENCY:
1201 case EFI_INFO_INDEX_MCFG_BASE:
1202 case EFI_INFO_INDEX_MCFG_SIZE:
1203 return 8;
1204 }
1205 return UINT32_MAX;
1206}
1207
1208
1209/**
1210 * efiInfoNextByte for a uint64_t value.
1211 *
1212 * @returns Next (current) byte.
1213 * @param pThisCC The EFI state for the current context.
1214 * @param u64 The value.
1215 */
1216static uint8_t efiInfoNextByteU64(PDEVEFIR3 pThisCC, uint64_t u64)
1217{
1218 uint64_t off = pThisCC->offInfo;
1219 if (off >= 8)
1220 return 0;
1221 return (uint8_t)(u64 >> (off * 8));
1222}
1223
1224/**
1225 * efiInfoNextByte for a uint32_t value.
1226 *
1227 * @returns Next (current) byte.
1228 * @param pThisCC The EFI state for the current context.
1229 * @param u32 The value.
1230 */
1231static uint8_t efiInfoNextByteU32(PDEVEFIR3 pThisCC, uint32_t u32)
1232{
1233 uint32_t off = pThisCC->offInfo;
1234 if (off >= 4)
1235 return 0;
1236 return (uint8_t)(u32 >> (off * 8));
1237}
1238
1239/**
1240 * efiInfoNextByte for a buffer.
1241 *
1242 * @returns Next (current) byte.
1243 * @param pThisCC The EFI state for the current context.
1244 * @param pvBuf The buffer.
1245 * @param cbBuf The buffer size.
1246 */
1247static uint8_t efiInfoNextByteBuf(PDEVEFIR3 pThisCC, void const *pvBuf, size_t cbBuf)
1248{
1249 uint32_t off = pThisCC->offInfo;
1250 if (off >= cbBuf)
1251 return 0;
1252 return ((uint8_t const *)pvBuf)[off];
1253}
1254
1255/**
1256 * Gets the next info byte.
1257 *
1258 * @returns Next (current) byte.
1259 * @param pThisCC The EFI state for the current context.
1260 */
1261static uint8_t efiInfoNextByte(PDEVEFIR3 pThisCC)
1262{
1263 switch (pThisCC->iInfoSelector)
1264 {
1265
1266 case EFI_INFO_INDEX_VOLUME_BASE: return efiInfoNextByteU64(pThisCC, pThisCC->GCLoadAddress);
1267 case EFI_INFO_INDEX_VOLUME_SIZE: return efiInfoNextByteU64(pThisCC, pThisCC->cbEfiRom);
1268 case EFI_INFO_INDEX_TEMPMEM_BASE: return efiInfoNextByteU32(pThisCC, VBOX_EFI_TOP_OF_STACK); /* just after stack */
1269 case EFI_INFO_INDEX_TEMPMEM_SIZE: return efiInfoNextByteU32(pThisCC, _512K);
1270 case EFI_INFO_INDEX_FSB_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64FsbFrequency);
1271 case EFI_INFO_INDEX_TSC_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64TscFrequency);
1272 case EFI_INFO_INDEX_CPU_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64CpuFrequency);
1273 case EFI_INFO_INDEX_BOOT_ARGS: return efiInfoNextByteBuf(pThisCC, pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs));
1274 case EFI_INFO_INDEX_DEVICE_PROPS: return efiInfoNextByteBuf(pThisCC, pThisCC->pbDeviceProps, pThisCC->cbDeviceProps);
1275 case EFI_INFO_INDEX_GRAPHICS_MODE: return efiInfoNextByteU32(pThisCC, pThisCC->u32GraphicsMode);
1276 case EFI_INFO_INDEX_HORIZONTAL_RESOLUTION: return efiInfoNextByteU32(pThisCC, pThisCC->u32HorizontalResolution);
1277 case EFI_INFO_INDEX_VERTICAL_RESOLUTION: return efiInfoNextByteU32(pThisCC, pThisCC->u32VerticalResolution);
1278
1279 /* Keep in sync with value in EfiThunk.asm */
1280 case EFI_INFO_INDEX_STACK_BASE: return efiInfoNextByteU32(pThisCC, VBOX_EFI_TOP_OF_STACK - _128K); /* 2M - 128 K */
1281 case EFI_INFO_INDEX_STACK_SIZE: return efiInfoNextByteU32(pThisCC, _128K);
1282 case EFI_INFO_INDEX_MCFG_BASE: return efiInfoNextByteU64(pThisCC, pThisCC->u64McfgBase);
1283 case EFI_INFO_INDEX_MCFG_SIZE: return efiInfoNextByteU64(pThisCC, pThisCC->cbMcfgLength);
1284
1285 default:
1286 PDMDevHlpDBGFStop(pThisCC->pDevIns, RT_SRC_POS, "%#x", pThisCC->iInfoSelector);
1287 return 0;
1288 }
1289}
1290
1291
1292#ifdef IN_RING3
1293static void efiVBoxDbgScript(const char *pszFormat, ...)
1294{
1295# ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
1296 PRTSTREAM pStrm;
1297 int rc2 = RTStrmOpen("./DevEFI.VBoxDbg", "a", &pStrm);
1298 if (RT_SUCCESS(rc2))
1299 {
1300 va_list va;
1301 va_start(va, pszFormat);
1302 RTStrmPrintfV(pStrm, pszFormat, va);
1303 va_end(va);
1304 RTStrmClose(pStrm);
1305 }
1306# else
1307 RT_NOREF(pszFormat);
1308# endif
1309}
1310#endif /* IN_RING3 */
1311
1312
1313/**
1314 * Handles writes to the image event port.
1315 *
1316 * @returns VBox status suitable for I/O port write handler.
1317 *
1318 * @param pThisCC The EFI state for the current context.
1319 * @param u32 The value being written.
1320 * @param cb The size of the value.
1321 */
1322static int efiPortImageEventWrite(PDEVEFIR3 pThisCC, uint32_t u32, unsigned cb)
1323{
1324 RT_NOREF(cb);
1325 switch (u32 & EFI_IMAGE_EVT_CMD_MASK)
1326 {
1327 case EFI_IMAGE_EVT_CMD_START_LOAD32:
1328 case EFI_IMAGE_EVT_CMD_START_LOAD64:
1329 case EFI_IMAGE_EVT_CMD_START_UNLOAD32:
1330 case EFI_IMAGE_EVT_CMD_START_UNLOAD64:
1331 case EFI_IMAGE_EVT_CMD_START_RELOC32:
1332 case EFI_IMAGE_EVT_CMD_START_RELOC64:
1333 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
1334
1335 /* Reset the state. */
1336 RT_ZERO(pThisCC->ImageEvt);
1337 pThisCC->ImageEvt.uEvt = (uint8_t)u32; Assert(pThisCC->ImageEvt.uEvt == u32);
1338 return VINF_SUCCESS;
1339
1340 case EFI_IMAGE_EVT_CMD_COMPLETE:
1341 {
1342# ifdef IN_RING3
1343 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
1344
1345 /* For now, just log it. */
1346 static uint64_t s_cImageEvtLogged = 0;
1347 if (s_cImageEvtLogged < 2048)
1348 {
1349 s_cImageEvtLogged++;
1350 switch (pThisCC->ImageEvt.uEvt)
1351 {
1352 /* ASSUMES the name ends with .pdb and the image file ends with .efi! */
1353 case EFI_IMAGE_EVT_CMD_START_LOAD32:
1354 LogRel(("EFI: VBoxDbg> loadimage32 '%.*s.efi' %#llx LB %#llx\n",
1355 pThisCC->ImageEvt.offName - 4, pThisCC->ImageEvt.szName, pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0));
1356 if (pThisCC->ImageEvt.offName > 4)
1357 efiVBoxDbgScript("loadimage32 '%.*s.efi' %#llx\n",
1358 pThisCC->ImageEvt.offName - 4, pThisCC->ImageEvt.szName, pThisCC->ImageEvt.uAddr0);
1359 break;
1360 case EFI_IMAGE_EVT_CMD_START_LOAD64:
1361 LogRel(("EFI: VBoxDbg> loadimage64 '%.*s.efi' %#llx LB %#llx\n",
1362 pThisCC->ImageEvt.offName - 4, pThisCC->ImageEvt.szName, pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0));
1363 if (pThisCC->ImageEvt.offName > 4)
1364 efiVBoxDbgScript("loadimage64 '%.*s.efi' %#llx\n",
1365 pThisCC->ImageEvt.offName - 4, pThisCC->ImageEvt.szName, pThisCC->ImageEvt.uAddr0);
1366 break;
1367 case EFI_IMAGE_EVT_CMD_START_UNLOAD32:
1368 case EFI_IMAGE_EVT_CMD_START_UNLOAD64:
1369 {
1370 LogRel(("EFI: VBoxDbg> unload '%.*s.efi' # %#llx LB %#llx\n",
1371 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
1372 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent],
1373 pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0));
1374 if (pThisCC->ImageEvt.offName > 4)
1375 efiVBoxDbgScript("unload '%.*s.efi'\n",
1376 pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent,
1377 &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent]);
1378 break;
1379 }
1380 case EFI_IMAGE_EVT_CMD_START_RELOC32:
1381 case EFI_IMAGE_EVT_CMD_START_RELOC64:
1382 {
1383 LogRel(("EFI: relocate module to %#llx from %#llx\n",
1384 pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.uAddr1));
1385 break;
1386 }
1387 }
1388 }
1389 return VINF_SUCCESS;
1390# else
1391 return VINF_IOM_R3_IOPORT_WRITE;
1392# endif
1393 }
1394
1395 case EFI_IMAGE_EVT_CMD_ADDR0:
1396 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
1397 pThisCC->ImageEvt.uAddr0 <<= 16;
1398 pThisCC->ImageEvt.uAddr0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
1399 return VINF_SUCCESS;
1400
1401 case EFI_IMAGE_EVT_CMD_ADDR1:
1402 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
1403 pThisCC->ImageEvt.uAddr1 <<= 16;
1404 pThisCC->ImageEvt.uAddr1 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
1405 return VINF_SUCCESS;
1406
1407 case EFI_IMAGE_EVT_CMD_SIZE0:
1408 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
1409 pThisCC->ImageEvt.cb0 <<= 16;
1410 pThisCC->ImageEvt.cb0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
1411 return VINF_SUCCESS;
1412
1413 case EFI_IMAGE_EVT_CMD_NAME:
1414 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= 0x7f);
1415 if (pThisCC->ImageEvt.offName < sizeof(pThisCC->ImageEvt.szName) - 1)
1416 {
1417 char ch = EFI_IMAGE_EVT_GET_PAYLOAD_U8(u32);
1418 if (ch == '\\')
1419 ch = '/';
1420 pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offName++] = ch;
1421 if (ch == '/' || ch == ':')
1422 pThisCC->ImageEvt.offNameLastComponent = pThisCC->ImageEvt.offName;
1423 }
1424 else
1425 Log(("EFI: Image name overflow\n"));
1426 return VINF_SUCCESS;
1427 }
1428
1429 Log(("EFI: Unknown image event: %#x (cb=%d)\n", u32, cb));
1430 return VINF_SUCCESS;
1431}
1432
1433
1434/**
1435 * @callback_method_impl{FNIOMIOPORTNEWIN}
1436 *
1437 * @note The @a offPort parameter is absolute!
1438 */
1439static DECLCALLBACK(VBOXSTRICTRC) efiR3IoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1440{
1441 RT_NOREF(pvUser);
1442 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1443 Log4(("EFI in: %x %x\n", offPort, cb));
1444
1445 switch (offPort)
1446 {
1447 case EFI_INFO_PORT:
1448 if (pThisCC->offInfo == -1 && cb == 4)
1449 {
1450 pThisCC->offInfo = 0;
1451 uint32_t cbInfo = *pu32 = efiInfoSize(pThisCC);
1452 if (cbInfo == UINT32_MAX)
1453 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "iInfoSelector=%#x (%d)\n",
1454 pThisCC->iInfoSelector, pThisCC->iInfoSelector);
1455 }
1456 else
1457 {
1458 if (cb != 1)
1459 return VERR_IOM_IOPORT_UNUSED;
1460 *pu32 = efiInfoNextByte(pThisCC);
1461 pThisCC->offInfo++;
1462 }
1463 return VINF_SUCCESS;
1464
1465 case EFI_PANIC_PORT:
1466# ifdef IN_RING3
1467 LogRel(("EFI panic port read!\n"));
1468 /* Insert special code here on panic reads */
1469 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: panic port read!\n");
1470# else
1471 /* Reschedule to R3 */
1472 return VINF_IOM_R3_IOPORT_READ;
1473# endif
1474
1475 case EFI_PORT_VARIABLE_OP:
1476 return nvramReadVariableOp(pThisCC, pu32, cb);
1477
1478 case EFI_PORT_VARIABLE_PARAM:
1479 case EFI_PORT_DEBUG_POINT:
1480 case EFI_PORT_IMAGE_EVENT:
1481 *pu32 = UINT32_MAX;
1482 return VINF_SUCCESS;
1483 }
1484
1485 return VERR_IOM_IOPORT_UNUSED;
1486}
1487
1488
1489/**
1490 * Translates a debug point value into a string for logging.
1491 *
1492 * @returns read-only string
1493 * @param enmDbgPoint Valid debug point value.
1494 */
1495static const char *efiDbgPointName(EFIDBGPOINT enmDbgPoint)
1496{
1497 switch (enmDbgPoint)
1498 {
1499 case EFIDBGPOINT_SEC_PREMEM: return "SEC_PREMEM";
1500 case EFIDBGPOINT_SEC_POSTMEM: return "SEC_POSTMEM";
1501 case EFIDBGPOINT_DXE_CORE: return "DXE_CORE";
1502 case EFIDBGPOINT_SMM: return "SMM";
1503 case EFIDBGPOINT_SMI_ENTER: return "SMI_ENTER";
1504 case EFIDBGPOINT_SMI_EXIT: return "SMI_EXIT";
1505 case EFIDBGPOINT_GRAPHICS: return "GRAPHICS";
1506 case EFIDBGPOINT_DXE_AP: return "DXE_AP";
1507 default:
1508 AssertFailed();
1509 return "Unknown";
1510 }
1511}
1512
1513
1514/**
1515 * @callback_method_impl{FNIOMIOPORTNEWOUT}
1516 *
1517 * @note The @a offPort parameter is absolute!
1518 */
1519static DECLCALLBACK(VBOXSTRICTRC) efiR3IoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
1520{
1521 RT_NOREF(pvUser);
1522 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1523 VBOXSTRICTRC rc = VINF_SUCCESS;
1524 Log4(("efi: out %x %x %d\n", offPort, u32, cb));
1525
1526 switch (offPort)
1527 {
1528 case EFI_INFO_PORT:
1529 Log2(("EFI_INFO_PORT: iInfoSelector=%#x\n", u32));
1530 pThisCC->iInfoSelector = u32;
1531 pThisCC->offInfo = -1;
1532 break;
1533
1534 case EFI_DEBUG_PORT:
1535 {
1536 /* The raw version. */
1537 switch (u32)
1538 {
1539 case '\r': Log3(("efi: <return>\n")); break;
1540 case '\n': Log3(("efi: <newline>\n")); break;
1541 case '\t': Log3(("efi: <tab>\n")); break;
1542 default: Log3(("efi: %c (%02x)\n", u32, u32)); break;
1543 }
1544 /* The readable, buffered version. */
1545 if (u32 == '\n' || u32 == '\r')
1546 {
1547 Assert(pThisCC->iMsg < sizeof(pThisCC->szMsg));
1548 pThisCC->szMsg[pThisCC->iMsg] = '\0';
1549 if (pThisCC->iMsg)
1550 LogRel2(("efi: %s\n", pThisCC->szMsg));
1551 pThisCC->iMsg = 0;
1552 }
1553 else
1554 {
1555 if (pThisCC->iMsg >= sizeof(pThisCC->szMsg) - 1)
1556 {
1557 pThisCC->szMsg[pThisCC->iMsg] = '\0';
1558 LogRel2(("efi: %s\n", pThisCC->szMsg));
1559 pThisCC->iMsg = 0;
1560 }
1561 pThisCC->szMsg[pThisCC->iMsg] = (char)u32;
1562 pThisCC->szMsg[++pThisCC->iMsg] = '\0';
1563 }
1564 break;
1565 }
1566
1567 case EFI_PANIC_PORT:
1568 {
1569 switch (u32)
1570 {
1571 case EFI_PANIC_CMD_BAD_ORG: /* Legacy */
1572 case EFI_PANIC_CMD_THUNK_TRAP:
1573#ifdef IN_RING3
1574 LogRel(("EFI: Panic! Unexpected trap!!\n"));
1575# ifdef VBOX_STRICT
1576 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n");
1577# else
1578 AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
1579# endif
1580 break;
1581#else
1582 return VINF_IOM_R3_IOPORT_WRITE;
1583#endif
1584
1585 case EFI_PANIC_CMD_START_MSG:
1586 LogRel(("Receiving EFI panic...\n"));
1587 pThisCC->iPanicMsg = 0;
1588 pThisCC->szPanicMsg[0] = '\0';
1589 break;
1590
1591 case EFI_PANIC_CMD_END_MSG:
1592#ifdef IN_RING3
1593 LogRel(("EFI: Panic! %s\n", pThisCC->szPanicMsg));
1594# ifdef VBOX_STRICT
1595 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThisCC->szPanicMsg);
1596# else
1597 return VERR_INTERNAL_ERROR;
1598# endif
1599#else
1600 return VINF_IOM_R3_IOPORT_WRITE;
1601#endif
1602
1603
1604 default:
1605 if ( u32 >= EFI_PANIC_CMD_MSG_FIRST
1606 && u32 <= EFI_PANIC_CMD_MSG_LAST)
1607 {
1608 /* Add the message char to the buffer. */
1609 uint32_t i = pThisCC->iPanicMsg;
1610 if (i + 1 < sizeof(pThisCC->szPanicMsg))
1611 {
1612 char ch = EFI_PANIC_CMD_MSG_GET_CHAR(u32);
1613 if ( ch == '\n'
1614 && i > 0
1615 && pThisCC->szPanicMsg[i - 1] == '\r')
1616 i--;
1617 pThisCC->szPanicMsg[i] = ch;
1618 pThisCC->szPanicMsg[i + 1] = '\0';
1619 pThisCC->iPanicMsg = i + 1;
1620 }
1621 }
1622 else
1623 Log(("EFI: Unknown panic command: %#x (cb=%d)\n", u32, cb));
1624 break;
1625 }
1626 break;
1627 }
1628
1629 case EFI_PORT_VARIABLE_OP:
1630 {
1631 /* clear buffer index */
1632 if (u32 >= (uint32_t)EFI_VM_VARIABLE_OP_MAX)
1633 {
1634 Log(("EFI: Invalid variable op %#x\n", u32));
1635 u32 = EFI_VM_VARIABLE_OP_ERROR;
1636 }
1637 pThisCC->NVRAM.offOpBuffer = 0;
1638 pThisCC->NVRAM.enmOp = (EFIVAROP)u32;
1639 Log2(("EFI_VARIABLE_OP: enmOp=%#x (%d)\n", u32, u32));
1640 break;
1641 }
1642
1643 case EFI_PORT_VARIABLE_PARAM:
1644 rc = nvramWriteVariableParam(pThisCC, u32);
1645 break;
1646
1647 case EFI_PORT_DEBUG_POINT:
1648# ifdef IN_RING3
1649 if (u32 > EFIDBGPOINT_INVALID && u32 < EFIDBGPOINT_END)
1650 {
1651 /* For now, just log it. */
1652 LogRelMax(1024, ("EFI: debug point %s\n", efiDbgPointName((EFIDBGPOINT)u32)));
1653 rc = VINF_SUCCESS;
1654 }
1655 else
1656 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Invalid debug point %#x\n", u32);
1657 break;
1658# else
1659 return VINF_IOM_R3_IOPORT_WRITE;
1660# endif
1661
1662 case EFI_PORT_IMAGE_EVENT:
1663 rc = efiPortImageEventWrite(pThisCC, u32, cb);
1664 break;
1665
1666 default:
1667 Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", offPort, u32, cb));
1668 break;
1669 }
1670 return rc;
1671}
1672
1673#endif /* IN_RING3 */
1674
1675/**
1676 * @callback_method_impl{FNIOMMMIONEWWRITE, Flash memory write}
1677 */
1678static DECLCALLBACK(VBOXSTRICTRC) efiR3NvMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1679{
1680 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1681 RT_NOREF(pvUser);
1682
1683 return flashWrite(&pThis->Flash, off, pv, cb);
1684}
1685
1686
1687/**
1688 * @callback_method_impl{FNIOMMMIONEWREAD, Flash memory read}
1689 */
1690static DECLCALLBACK(VBOXSTRICTRC) efiR3NvMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1691{
1692 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1693 RT_NOREF(pvUser);
1694
1695 return flashRead(&pThis->Flash, off, pv, cb);
1696}
1697
1698#ifdef IN_RING3
1699
1700static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1701{
1702 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1703 LogFlow(("efiSaveExec:\n"));
1704
1705 return flashR3SaveExec(&pThis->Flash, pDevIns, pSSM);
1706}
1707
1708static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1709{
1710 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1711 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1712 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1713 LogFlow(("efiLoadExec: uVersion=%d uPass=%d\n", uVersion, uPass));
1714
1715 /*
1716 * Validate input.
1717 */
1718 if (uPass != SSM_PASS_FINAL)
1719 return VERR_SSM_UNEXPECTED_PASS;
1720 if ( uVersion != EFI_SSM_VERSION
1721 && uVersion != EFI_SSM_VERSION_PRE_PROPER_NVRAM
1722 && uVersion != EFI_SSM_VERSION_4_2
1723 )
1724 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1725
1726 int rc;
1727 if (uVersion > EFI_SSM_VERSION_PRE_PROPER_NVRAM)
1728 rc = flashR3LoadExec(&pThis->Flash, pDevIns, pSSM);
1729 else
1730 {
1731 /*
1732 * Kill the current variables before loading anything.
1733 */
1734 nvramFlushDeviceVariableList(pThisCC);
1735
1736 /*
1737 * Load the NVRAM state.
1738 */
1739 rc = pHlp->pfnSSMGetStructEx(pSSM, &pThisCC->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
1740 AssertRCReturn(rc, rc);
1741 pThisCC->NVRAM.pCurVar = NULL;
1742
1743 rc = pHlp->pfnSSMGetStructEx(pSSM, &pThisCC->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1744 AssertRCReturn(rc, rc);
1745
1746 /*
1747 * Load variables.
1748 */
1749 pThisCC->NVRAM.pCurVar = NULL;
1750 Assert(RTListIsEmpty(&pThisCC->NVRAM.VarList));
1751 RTListInit(&pThisCC->NVRAM.VarList);
1752 for (uint32_t i = 0; i < pThisCC->NVRAM.cVariables; i++)
1753 {
1754 PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
1755 AssertReturn(pEfiVar, VERR_NO_MEMORY);
1756
1757 rc = pHlp->pfnSSMGetStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1758 if (RT_SUCCESS(rc))
1759 {
1760 if ( pEfiVar->cbValue > sizeof(pEfiVar->abValue)
1761 || pEfiVar->cbValue == 0)
1762 {
1763 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1764 LogRel(("EFI: Loaded invalid variable value length %#x\n", pEfiVar->cbValue));
1765 }
1766 uint32_t cchVarName = (uint32_t)RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
1767 if (cchVarName >= sizeof(pEfiVar->szName))
1768 {
1769 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1770 LogRel(("EFI: Loaded variable name is unterminated.\n"));
1771 }
1772 if (pEfiVar->cchName > cchVarName) /* No check for 0 here, busted load code in 4.2, so now storing 0 here. */
1773 {
1774 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1775 LogRel(("EFI: Loaded invalid variable name length %#x (cchVarName=%#x)\n", pEfiVar->cchName, cchVarName));
1776 }
1777 if (RT_SUCCESS(rc))
1778 pEfiVar->cchName = cchVarName;
1779 }
1780 AssertRCReturnStmt(rc, RTMemFree(pEfiVar), rc);
1781
1782 /* Add it (not using nvramInsertVariable to preserve saved order),
1783 updating the current variable pointer while we're here. */
1784#if 1
1785 RTListAppend(&pThisCC->NVRAM.VarList, &pEfiVar->ListNode);
1786#else
1787 nvramInsertVariable(pThisCC, pEfiVar);
1788#endif
1789 if (pThisCC->NVRAM.idUniqueCurVar == pEfiVar->idUniqueSavedState)
1790 pThisCC->NVRAM.pCurVar = pEfiVar;
1791 }
1792 }
1793
1794 return rc;
1795}
1796
1797
1798/**
1799 * @copydoc(PDMIBASE::pfnQueryInterface)
1800 */
1801static DECLCALLBACK(void *) devEfiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1802{
1803 LogFlowFunc(("ENTER: pIBase=%p pszIID=%p\n", pInterface, pszIID));
1804 PDEVEFIR3 pThisCC = RT_FROM_MEMBER(pInterface, DEVEFIR3, Lun0.IBase);
1805
1806 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->Lun0.IBase);
1807 return NULL;
1808}
1809
1810
1811/**
1812 * Write to CMOS memory.
1813 * This is used by the init complete code.
1814 */
1815static void cmosWrite(PPDMDEVINS pDevIns, unsigned off, uint32_t u32Val)
1816{
1817 Assert(off < 128);
1818 Assert(u32Val < 256);
1819
1820 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
1821 AssertRC(rc);
1822}
1823
1824/**
1825 * Init complete notification.
1826 *
1827 * @returns VBOX status code.
1828 * @param pDevIns The device instance.
1829 */
1830static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
1831{
1832 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1833
1834 PVM pVM = PDMDevHlpGetVM(pDevIns);
1835 uint64_t const cbRamSize = MMR3PhysGetRamSize(pVM);
1836 uint32_t const cbBelow4GB = MMR3PhysGetRamSizeBelow4GB(pVM);
1837 uint64_t const cbAbove4GB = MMR3PhysGetRamSizeAbove4GB(pVM);
1838 NOREF(cbAbove4GB);
1839
1840 /*
1841 * Memory sizes.
1842 */
1843 uint32_t u32Low = 0;
1844 uint32_t u32Chunks = 0;
1845 if (cbRamSize > 16 * _1M)
1846 {
1847 u32Low = RT_MIN(cbBelow4GB, UINT32_C(0xfe000000));
1848 u32Chunks = (u32Low - 16U * _1M) / _64K;
1849 }
1850 cmosWrite(pDevIns, 0x34, RT_BYTE1(u32Chunks));
1851 cmosWrite(pDevIns, 0x35, RT_BYTE2(u32Chunks));
1852
1853 if (u32Low < cbRamSize)
1854 {
1855 uint64_t u64 = cbRamSize - u32Low;
1856 u32Chunks = (uint32_t)(u64 / _64K);
1857 cmosWrite(pDevIns, 0x5b, RT_BYTE1(u32Chunks));
1858 cmosWrite(pDevIns, 0x5c, RT_BYTE2(u32Chunks));
1859 cmosWrite(pDevIns, 0x5d, RT_BYTE3(u32Chunks));
1860 cmosWrite(pDevIns, 0x5e, RT_BYTE4(u32Chunks));
1861 }
1862
1863 /*
1864 * Number of CPUs.
1865 */
1866 cmosWrite(pDevIns, 0x60, pThisCC->cCpus & 0xff);
1867
1868 return VINF_SUCCESS;
1869}
1870
1871
1872/**
1873 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1874 */
1875static DECLCALLBACK(void) efiMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1876{
1877 RT_NOREF(enmCtx);
1878 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1879
1880 /*
1881 * Re-shadow the Firmware Volume and make it RAM/RAM.
1882 */
1883 uint32_t cPages = RT_ALIGN_64(pThisCC->cbEfiRom, PAGE_SIZE) >> PAGE_SHIFT;
1884 RTGCPHYS GCPhys = pThisCC->GCLoadAddress;
1885 while (cPages > 0)
1886 {
1887 uint8_t abPage[PAGE_SIZE];
1888
1889 /* Read the (original) ROM page and write it back to the RAM page. */
1890 int rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1891 AssertLogRelRC(rc);
1892
1893 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
1894 AssertLogRelRC(rc);
1895 if (RT_FAILURE(rc))
1896 memset(abPage, 0xcc, sizeof(abPage));
1897
1898 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
1899 AssertLogRelRC(rc);
1900
1901 /* Switch to the RAM/RAM mode. */
1902 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1903 AssertLogRelRC(rc);
1904
1905 /* Advance */
1906 GCPhys += PAGE_SIZE;
1907 cPages--;
1908 }
1909}
1910
1911
1912/**
1913 * @interface_method_impl{PDMDEVREG,pfnReset}
1914 */
1915static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
1916{
1917 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1918 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1919 LogFlow(("efiReset\n"));
1920
1921 pThisCC->iInfoSelector = 0;
1922 pThisCC->offInfo = -1;
1923
1924 pThisCC->iMsg = 0;
1925 pThisCC->szMsg[0] = '\0';
1926 pThisCC->iPanicMsg = 0;
1927 pThisCC->szPanicMsg[0] = '\0';
1928
1929 flashR3Reset(&pThis->Flash);
1930
1931#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
1932 /*
1933 * Zap the debugger script
1934 */
1935 RTFileDelete("./DevEFI.VBoxDbg");
1936#endif
1937}
1938
1939
1940/**
1941 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
1942 */
1943static DECLCALLBACK(void) efiPowerOff(PPDMDEVINS pDevIns)
1944{
1945 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1946
1947 if (pThisCC->Lun0.pNvramDrv)
1948 nvramStore(pThisCC);
1949}
1950
1951
1952
1953/**
1954 * Destruct a device instance.
1955 *
1956 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1957 * resources can be freed correctly.
1958 *
1959 * @param pDevIns The device instance data.
1960 */
1961static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
1962{
1963 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1964 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
1965 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
1966
1967 nvramFlushDeviceVariableList(pThisCC);
1968
1969 if (pThisCC->pszNvramFile)
1970 {
1971 int rc = flashR3SaveToFile(&pThis->Flash, pDevIns, pThisCC->pszNvramFile);
1972 if (RT_FAILURE(rc))
1973 LogRel(("EFI: Failed to save flash file to '%s': %Rrc\n", pThisCC->pszNvramFile, rc));
1974 }
1975
1976 flashR3Destruct(&pThis->Flash, pDevIns);
1977
1978 if (pThisCC->pszNvramFile)
1979 {
1980 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszNvramFile);
1981 pThisCC->pszNvramFile = NULL;
1982 }
1983
1984 if (pThisCC->pu8EfiRomFree)
1985 {
1986 RTFileReadAllFree(pThisCC->pu8EfiRomFree, (size_t)pThisCC->cbEfiRom + pThisCC->offEfiRom);
1987 pThisCC->pu8EfiRomFree = NULL;
1988 }
1989
1990 /*
1991 * Free MM heap pointers (waste of time, but whatever).
1992 */
1993 if (pThisCC->pszEfiRomFile)
1994 {
1995 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszEfiRomFile);
1996 pThisCC->pszEfiRomFile = NULL;
1997 }
1998
1999 if (pThisCC->pu8EfiThunk)
2000 {
2001 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pu8EfiThunk);
2002 pThisCC->pu8EfiThunk = NULL;
2003 }
2004
2005 if (pThisCC->pbDeviceProps)
2006 {
2007 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbDeviceProps);
2008 pThisCC->pbDeviceProps = NULL;
2009 pThisCC->cbDeviceProps = 0;
2010 }
2011
2012 return VINF_SUCCESS;
2013}
2014
2015
2016#if 0 /* unused */
2017/**
2018 * Helper that searches for a FFS file of a given type.
2019 *
2020 * @returns Pointer to the FFS file header if found, NULL if not.
2021 *
2022 * @param pFfsFile Pointer to the FFS file header to start searching at.
2023 * @param pbEnd The end of the firmware volume.
2024 * @param FileType The file type to look for.
2025 * @param pcbFfsFile Where to store the FFS file size (includes header).
2026 */
2027DECLINLINE(EFI_FFS_FILE_HEADER const *)
2028efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
2029{
2030# define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
2031 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
2032 {
2033 if (pFfsFile->Type == FileType)
2034 {
2035 *pcbFile = FFS_SIZE(pFfsFile);
2036 LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType));
2037 return pFfsFile;
2038 }
2039 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
2040 }
2041# undef FFS_SIZE
2042 return NULL;
2043}
2044#endif /* unused */
2045
2046
2047/**
2048 * Parse EFI ROM headers and find entry points.
2049 *
2050 * @returns VBox status code.
2051 * @param pDevIns The device instance.
2052 * @param pThis The shared device state.
2053 * @param pThisCC The device state for the current context.
2054 */
2055static int efiParseFirmware(PPDMDEVINS pDevIns, PDEVEFI pThis, PDEVEFIR3 pThisCC)
2056{
2057 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThisCC->pu8EfiRom;
2058
2059 /*
2060 * Validate firmware volume header.
2061 */
2062 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
2063 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
2064 VERR_INVALID_MAGIC);
2065 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
2066 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
2067 VERR_VERSION_MISMATCH);
2068 /** @todo check checksum, see PE spec vol. 3 */
2069 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThisCC->cbEfiRom,
2070 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThisCC->cbEfiRom),
2071 VERR_INVALID_PARAMETER);
2072 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
2073 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
2074 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
2075 VERR_INVALID_PARAMETER);
2076
2077 AssertLogRelMsgReturn(!(pThisCC->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThisCC->cbEfiRom), VERR_INVALID_PARAMETER);
2078
2079 LogRel(("Found EFI FW Volume, %u bytes (%u %u-byte blocks)\n", pFwVolHdr->FvLength, pFwVolHdr->BlockMap[0].NumBlocks, pFwVolHdr->BlockMap[0].Length));
2080
2081 /** @todo Make this more dynamic, this assumes that the NV storage area comes first (always the case for our builds). */
2082 AssertLogRelMsgReturn(!memcmp(&pFwVolHdr->FileSystemGuid, &g_UuidNvDataFv, sizeof(g_UuidNvDataFv)),
2083 ("Expected EFI_SYSTEM_NV_DATA_FV_GUID as an identifier"),
2084 VERR_INVALID_MAGIC);
2085
2086 /* Found NVRAM storage, configure flash device. */
2087 pThisCC->offEfiRom = pFwVolHdr->FvLength;
2088 pThisCC->cbNvram = pFwVolHdr->FvLength;
2089 pThisCC->GCPhysNvram = UINT32_C(0xfffff000) - pThisCC->cbEfiRom + PAGE_SIZE;
2090 pThisCC->cbEfiRom -= pThisCC->cbNvram;
2091
2092 int rc = flashR3Init(&pThis->Flash, pThisCC->pDevIns, 0xA289 /*Intel*/, pThisCC->cbNvram, pFwVolHdr->BlockMap[0].Length);
2093 if (RT_FAILURE(rc))
2094 return rc;
2095
2096 /* If the file does not exist we initialize the NVRAM from the loaded ROM file. */
2097 if (!pThisCC->pszNvramFile || !RTPathExists(pThisCC->pszNvramFile))
2098 rc = flashR3LoadFromBuf(&pThis->Flash, pThisCC->pu8EfiRom, pThisCC->cbNvram);
2099 else
2100 rc = flashR3LoadFromFile(&pThis->Flash, pDevIns, pThisCC->pszNvramFile);
2101 if (RT_FAILURE(rc))
2102 return rc;
2103
2104 pThisCC->GCLoadAddress = pThisCC->GCPhysNvram + pThisCC->cbNvram;
2105
2106 return VINF_SUCCESS;
2107}
2108
2109/**
2110 * Load EFI ROM file into the memory.
2111 *
2112 * @returns VBox status code.
2113 * @param pDevIns The device instance.
2114 * @param pThis The shared Efi state.
2115 * @param pThisCC The device state for the current context.
2116 * @param pCfg Configuration node handle for the device.
2117 */
2118static int efiLoadRom(PPDMDEVINS pDevIns, PDEVEFI pThis, PDEVEFIR3 pThisCC, PCFGMNODE pCfg)
2119{
2120 RT_NOREF(pCfg);
2121
2122 /*
2123 * Read the entire firmware volume into memory.
2124 */
2125 int rc;
2126#ifdef VBOX_WITH_EFI_IN_DD2
2127 if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltin32) == 0)
2128 {
2129 pThisCC->pu8EfiRomFree = NULL;
2130 pThisCC->pu8EfiRom = g_abEfiFirmware32;
2131 pThisCC->cbEfiRom = g_cbEfiFirmware32;
2132 }
2133 else if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltin64) == 0)
2134 {
2135 pThisCC->pu8EfiRomFree = NULL;
2136 pThisCC->pu8EfiRom = g_abEfiFirmware64;
2137 pThisCC->cbEfiRom = g_cbEfiFirmware64;
2138 }
2139 else
2140#endif
2141 {
2142 void *pvFile;
2143 size_t cbFile;
2144 rc = RTFileReadAllEx(pThisCC->pszEfiRomFile,
2145 0 /*off*/,
2146 RTFOFF_MAX /*cbMax*/,
2147 RTFILE_RDALL_O_DENY_WRITE,
2148 &pvFile,
2149 &cbFile);
2150 if (RT_FAILURE(rc))
2151 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2152 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
2153 pThisCC->pszEfiRomFile, rc);
2154 pThisCC->pu8EfiRomFree = (uint8_t *)pvFile;
2155 pThisCC->pu8EfiRom = (uint8_t *)pvFile;
2156 pThisCC->cbEfiRom = cbFile;
2157 }
2158
2159 /*
2160 * Validate firmware volume and figure out the load address as well as the SEC entry point.
2161 */
2162 rc = efiParseFirmware(pDevIns, pThis, pThisCC);
2163 if (RT_FAILURE(rc))
2164 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2165 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
2166 pThisCC->pszEfiRomFile, rc);
2167
2168 /*
2169 * Map the firmware volume into memory as shadowed ROM.
2170 *
2171 * This is a little complicated due to saved state legacy. We used to have a
2172 * 2MB image w/o any flash portion, divided into four 512KB mappings.
2173 *
2174 * We've now increased the size of the firmware to 4MB, but for saved state
2175 * compatibility reasons need to use the same mappings and names (!!) for the
2176 * top 2MB.
2177 */
2178 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
2179#if 1
2180 static const char * const s_apszNames[16] =
2181 {
2182 "EFI Firmware Volume", "EFI Firmware Volume (Part 2)", "EFI Firmware Volume (Part 3)", "EFI Firmware Volume (Part 4)",
2183 "EFI Firmware Volume (Part 5)", "EFI Firmware Volume (Part 6)", "EFI Firmware Volume (Part 7)", "EFI Firmware Volume (Part 8)",
2184 "EFI Firmware Volume (Part 9)", "EFI Firmware Volume (Part 10)", "EFI Firmware Volume (Part 11)", "EFI Firmware Volume (Part 12)",
2185 "EFI Firmware Volume (Part 13)", "EFI Firmware Volume (Part 14)", "EFI Firmware Volume (Part 15)", "EFI Firmware Volume (Part 16)",
2186 };
2187 AssertLogRelMsgReturn(pThisCC->cbEfiRom < RT_ELEMENTS(s_apszNames) * _512K,
2188 ("EFI firmware image too big: %#RX64, max %#zx\n",
2189 pThisCC->cbEfiRom, RT_ELEMENTS(s_apszNames) * _512K),
2190 VERR_IMAGE_TOO_BIG);
2191
2192 uint32_t const cbChunk = pThisCC->cbNvram + pThisCC->cbEfiRom >= _2M ? _512K
2193 : (uint32_t)RT_ALIGN_64((pThisCC->cbNvram + pThisCC->cbEfiRom) / 4, PAGE_SIZE);
2194 uint32_t cbLeft = pThisCC->cbEfiRom; /* ASSUMES NVRAM comes first! */
2195 uint32_t off = pThisCC->offEfiRom + cbLeft; /* ASSUMES NVRAM comes first! */
2196 RTGCPHYS64 GCPhys = pThisCC->GCLoadAddress + cbLeft;
2197 AssertLogRelMsg(GCPhys == _4G, ("%RGp\n", GCPhys));
2198
2199 /* Compatibility mappings at the top (note that this isn't entirely the same
2200 algorithm, but it will produce the same results for a power of two sized image): */
2201 unsigned i = 4;
2202 while (i-- > 0)
2203 {
2204 uint32_t const cb = RT_MIN(cbLeft, cbChunk);
2205 cbLeft -= cb;
2206 GCPhys -= cb;
2207 off -= cb;
2208 rc = PDMDevHlpROMRegister(pDevIns, GCPhys, cb, pThisCC->pu8EfiRom + off, cb,
2209 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, s_apszNames[i]);
2210 AssertRCReturn(rc, rc);
2211 }
2212
2213 /* The rest (if any) is mapped in descending order of address and increasing name order: */
2214 if (cbLeft > 0)
2215 {
2216 Assert(cbChunk == _512K);
2217 for (i = 4; cbLeft > 0; i++)
2218 {
2219 uint32_t const cb = RT_MIN(cbLeft, cbChunk);
2220 cbLeft -= cb;
2221 GCPhys -= cb;
2222 off -= cb;
2223 /** @todo Add flag to prevent saved state loading from bitching about these regions. */
2224 rc = PDMDevHlpROMRegister(pDevIns, GCPhys, cb, pThisCC->pu8EfiRom + off, cb,
2225 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY
2226 | PGMPHYS_ROM_FLAGS_MAYBE_MISSING_FROM_STATE, s_apszNames[i]);
2227 AssertRCReturn(rc, rc);
2228 }
2229 Assert(i <= RT_ELEMENTS(s_apszNames));
2230 }
2231
2232 /* Not sure what the purpose of this one is... */
2233 rc = PDMDevHlpROMProtectShadow(pDevIns, pThisCC->GCLoadAddress, (uint32_t)cbChunk, PGMROMPROT_READ_RAM_WRITE_IGNORE);
2234 AssertRCReturn(rc, rc);
2235
2236#else
2237 RTGCPHYS cbQuart = RT_ALIGN_64(pThisCC->cbEfiRom / 4, PAGE_SIZE);
2238 rc = PDMDevHlpROMRegister(pDevIns,
2239 pThisCC->GCLoadAddress,
2240 cbQuart,
2241 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs,
2242 cbQuart,
2243 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2244 "EFI Firmware Volume");
2245 AssertRCReturn(rc, rc);
2246 rc = PDMDevHlpROMProtectShadow(pDevIns, pThisCC->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
2247 AssertRCReturn(rc, rc);
2248 rc = PDMDevHlpROMRegister(pDevIns,
2249 pThisCC->GCLoadAddress + cbQuart,
2250 cbQuart,
2251 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart,
2252 cbQuart,
2253 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2254 "EFI Firmware Volume (Part 2)");
2255 if (RT_FAILURE(rc))
2256 return rc;
2257 rc = PDMDevHlpROMRegister(pDevIns,
2258 pThisCC->GCLoadAddress + cbQuart * 2,
2259 cbQuart,
2260 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart * 2,
2261 cbQuart,
2262 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2263 "EFI Firmware Volume (Part 3)");
2264 if (RT_FAILURE(rc))
2265 return rc;
2266 rc = PDMDevHlpROMRegister(pDevIns,
2267 pThisCC->GCLoadAddress + cbQuart * 3,
2268 pThisCC->cbEfiRom - cbQuart * 3,
2269 pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart * 3,
2270 pThisCC->cbEfiRom - cbQuart * 3,
2271 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2272 "EFI Firmware Volume (Part 4)");
2273 if (RT_FAILURE(rc))
2274 return rc;
2275#endif
2276
2277 /*
2278 * Register MMIO region for flash device.
2279 */
2280 rc = PDMDevHlpMmioCreateEx(pDevIns, pThisCC->cbNvram, IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
2281 NULL /*pPciDev*/, UINT32_MAX, efiR3NvMmioWrite, efiR3NvMmioRead, NULL, NULL /*pvUser*/,
2282 "Flash Memory", &pThis->hMmioFlash);
2283 AssertRCReturn(rc, rc);
2284 rc = PDMDevHlpMmioMap(pDevIns, pThis->hMmioFlash, pThisCC->GCPhysNvram);
2285 AssertRCReturn(rc, rc);
2286
2287 LogRel(("EFI: Registered %uKB flash at %RGp\n", pThisCC->cbNvram / _1K, pThisCC->GCPhysNvram));
2288 return VINF_SUCCESS;
2289}
2290
2291static uint8_t efiGetHalfByte(char ch)
2292{
2293 uint8_t val;
2294
2295 if (ch >= '0' && ch <= '9')
2296 val = ch - '0';
2297 else if (ch >= 'A' && ch <= 'F')
2298 val = ch - 'A' + 10;
2299 else if(ch >= 'a' && ch <= 'f')
2300 val = ch - 'a' + 10;
2301 else
2302 val = 0xff;
2303
2304 return val;
2305}
2306
2307
2308/**
2309 * Converts a hex string into a binary data blob located at
2310 * pThisCC->pbDeviceProps, size returned as pThisCC->cbDeviceProps.
2311 *
2312 * @returns VERR_NO_MEMORY or VINF_SUCCESS.
2313 * @param pThisCC The device state for the current context.
2314 * @param pszDeviceProps The device property hex string to decode.
2315 */
2316static int efiParseDeviceString(PDEVEFIR3 pThisCC, const char *pszDeviceProps)
2317{
2318 uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
2319 pThisCC->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThisCC->pDevIns, cbOut);
2320 if (!pThisCC->pbDeviceProps)
2321 return VERR_NO_MEMORY;
2322
2323 uint32_t iHex = 0;
2324 bool fUpper = true;
2325 uint8_t u8Value = 0; /* (shut up gcc) */
2326 for (uint32_t iStr = 0; pszDeviceProps[iStr]; iStr++)
2327 {
2328 uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]);
2329 if (u8Hb > 0xf)
2330 continue;
2331
2332 if (fUpper)
2333 u8Value = u8Hb << 4;
2334 else
2335 pThisCC->pbDeviceProps[iHex++] = u8Hb | u8Value;
2336
2337 Assert(iHex < cbOut);
2338 fUpper = !fUpper;
2339 }
2340
2341 Assert(iHex == 0 || fUpper);
2342 pThisCC->cbDeviceProps = iHex;
2343
2344 return VINF_SUCCESS;
2345}
2346
2347
2348/**
2349 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2350 */
2351static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2352{
2353 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2354 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
2355 PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3);
2356 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2357 int rc;
2358
2359 RT_NOREF(iInstance);
2360 Assert(iInstance == 0);
2361
2362 /*
2363 * Initalize the basic variables so that the destructor always works.
2364 */
2365 pThisCC->pDevIns = pDevIns;
2366 RTListInit(&pThisCC->NVRAM.VarList);
2367 pThisCC->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface;
2368
2369 /*
2370 * Validate and read the configuration.
2371 */
2372 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
2373 "EfiRom|"
2374 "NumCPUs|"
2375 "McfgBase|"
2376 "McfgLength|"
2377 "UUID|"
2378 "UuidLe|"
2379 "IOAPIC|"
2380 "APIC|"
2381 "DmiBIOSFirmwareMajor|"
2382 "DmiBIOSFirmwareMinor|"
2383 "DmiBIOSReleaseDate|"
2384 "DmiBIOSReleaseMajor|"
2385 "DmiBIOSReleaseMinor|"
2386 "DmiBIOSVendor|"
2387 "DmiBIOSVersion|"
2388 "DmiSystemFamily|"
2389 "DmiSystemProduct|"
2390 "DmiSystemSerial|"
2391 "DmiSystemSKU|"
2392 "DmiSystemUuid|"
2393 "DmiSystemVendor|"
2394 "DmiSystemVersion|"
2395 "DmiBoardAssetTag|"
2396 "DmiBoardBoardType|"
2397 "DmiBoardLocInChass|"
2398 "DmiBoardProduct|"
2399 "DmiBoardSerial|"
2400 "DmiBoardVendor|"
2401 "DmiBoardVersion|"
2402 "DmiChassisAssetTag|"
2403 "DmiChassisSerial|"
2404 "DmiChassisType|"
2405 "DmiChassisVendor|"
2406 "DmiChassisVersion|"
2407 "DmiProcManufacturer|"
2408 "DmiProcVersion|"
2409 "DmiOEMVBoxVer|"
2410 "DmiOEMVBoxRev|"
2411 "DmiUseHostInfo|"
2412 "DmiExposeMemoryTable|"
2413 "DmiExposeProcInf|"
2414 "64BitEntry|"
2415 "BootArgs|"
2416 "DeviceProps|"
2417 "GopMode|" // legacy
2418 "GraphicsMode|"
2419 "UgaHorizontalResolution|" // legacy
2420 "UgaVerticalResolution|" // legacy
2421 "GraphicsResolution|"
2422 "NvramFile", "");
2423
2424 /* CPU count (optional). */
2425 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "NumCPUs", &pThisCC->cCpus, 1);
2426 AssertLogRelRCReturn(rc, rc);
2427
2428 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThisCC->u64McfgBase, 0);
2429 if (RT_FAILURE(rc))
2430 return PDMDEV_SET_ERROR(pDevIns, rc,
2431 N_("Configuration error: Querying \"\" as integer failed"));
2432 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThisCC->cbMcfgLength, 0);
2433 if (RT_FAILURE(rc))
2434 return PDMDEV_SET_ERROR(pDevIns, rc,
2435 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
2436
2437 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThisCC->u8IOAPIC, 1);
2438 if (RT_FAILURE (rc))
2439 return PDMDEV_SET_ERROR(pDevIns, rc,
2440 N_("Configuration error: Failed to read \"IOAPIC\""));
2441
2442 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThisCC->u8APIC, 1);
2443 if (RT_FAILURE (rc))
2444 return PDMDEV_SET_ERROR(pDevIns, rc,
2445 N_("Configuration error: Failed to read \"APIC\""));
2446
2447 /*
2448 * Query the machine's UUID for SMBIOS/DMI use.
2449 */
2450 RTUUID uuid;
2451 rc = pHlp->pfnCFGMQueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
2452 if (RT_FAILURE(rc))
2453 return PDMDEV_SET_ERROR(pDevIns, rc,
2454 N_("Configuration error: Querying \"UUID\" failed"));
2455
2456 bool fUuidLe;
2457 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UuidLe", &fUuidLe, false);
2458 if (RT_FAILURE(rc))
2459 return PDMDEV_SET_ERROR(pDevIns, rc,
2460 N_("Configuration error: Querying \"UuidLe\" failed"));
2461
2462 if (!fUuidLe)
2463 {
2464 /*
2465 * UUIDs are stored little endian actually (see chapter 7.2.1 System — UUID
2466 * of the DMI/SMBIOS spec) but to not force reactivation of existing guests we have
2467 * to carry this bug along... (see also DevPcBios.cpp when changing this)
2468 *
2469 * Convert the UUID to network byte order. Not entirely straightforward as
2470 * parts are MSB already...
2471 */
2472 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
2473 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
2474 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
2475 }
2476 memcpy(&pThisCC->aUuid, &uuid, sizeof pThisCC->aUuid);
2477
2478 /*
2479 * Get the system EFI ROM file name.
2480 */
2481#ifdef VBOX_WITH_EFI_IN_DD2
2482 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "EfiRom", &pThisCC->pszEfiRomFile, g_szEfiBuiltin32);
2483 if (RT_FAILURE(rc))
2484#else
2485 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "EfiRom", &pThisCC->pszEfiRomFile);
2486 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2487 {
2488 pThisCC->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
2489 AssertReturn(pThisCC->pszEfiRomFile, VERR_NO_MEMORY);
2490 rc = RTPathAppPrivateArchTop(pThisCC->pszEfiRomFile, RTPATH_MAX);
2491 AssertRCReturn(rc, rc);
2492 rc = RTPathAppend(pThisCC->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd");
2493 AssertRCReturn(rc, rc);
2494 }
2495 else if (RT_FAILURE(rc))
2496#endif
2497 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2498 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
2499
2500 /*
2501 * NVRAM processing.
2502 */
2503 rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThisCC), efiSaveExec, efiLoadExec);
2504 AssertRCReturn(rc, rc);
2505
2506 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->Lun0.IBase, &pThisCC->Lun0.pDrvBase, "NvramStorage");
2507 if (RT_SUCCESS(rc))
2508 {
2509 pThisCC->Lun0.pNvramDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->Lun0.pDrvBase, PDMINVRAMCONNECTOR);
2510 AssertPtrReturn(pThisCC->Lun0.pNvramDrv, VERR_PDM_MISSING_INTERFACE_BELOW);
2511
2512 rc = nvramLoad(pThisCC);
2513 AssertRCReturn(rc, rc);
2514 }
2515 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2516 {
2517 pThisCC->Lun0.pNvramDrv = NULL;
2518 rc = VINF_SUCCESS; /* Missing driver is no error condition. */
2519 }
2520 else
2521 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
2522
2523 /*
2524 * Get boot args.
2525 */
2526 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "BootArgs", pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs), "");
2527 if (RT_FAILURE(rc))
2528 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2529 N_("Configuration error: Querying \"BootArgs\" as a string failed"));
2530
2531 //strcpy(pThisCC->szBootArgs, "-v keepsyms=1 io=0xf debug=0x2a");
2532 //strcpy(pThisCC->szBootArgs, "-v keepsyms=1 debug=0x2a");
2533 LogRel(("EFI: boot args = %s\n", pThisCC->szBootArgs));
2534
2535 /*
2536 * Get device props.
2537 */
2538 char *pszDeviceProps;
2539 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DeviceProps", &pszDeviceProps, NULL);
2540 if (RT_FAILURE(rc))
2541 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2542 N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
2543 if (pszDeviceProps)
2544 {
2545 LogRel(("EFI: device props = %s\n", pszDeviceProps));
2546 rc = efiParseDeviceString(pThisCC, pszDeviceProps);
2547 PDMDevHlpMMHeapFree(pDevIns, pszDeviceProps);
2548 if (RT_FAILURE(rc))
2549 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2550 N_("Configuration error: Cannot parse device properties"));
2551 }
2552 else
2553 {
2554 pThisCC->pbDeviceProps = NULL;
2555 pThisCC->cbDeviceProps = 0;
2556 }
2557
2558 /*
2559 * CPU frequencies.
2560 */
2561 pThisCC->u64TscFrequency = TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
2562 pThisCC->u64CpuFrequency = pThisCC->u64TscFrequency;
2563 pThisCC->u64FsbFrequency = CPUMGetGuestScalableBusFrequency(PDMDevHlpGetVM(pDevIns));
2564
2565 /*
2566 * EFI graphics mode (with new EFI VGA code used only as a fallback, for
2567 * old EFI VGA code the only way to select the GOP mode).
2568 */
2569 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "GraphicsMode", &pThisCC->u32GraphicsMode, UINT32_MAX);
2570 if (RT_FAILURE(rc))
2571 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2572 N_("Configuration error: Querying \"GraphicsMode\" as a 32-bit int failed"));
2573 if (pThisCC->u32GraphicsMode == UINT32_MAX)
2574 {
2575 /* get the legacy value if nothing else was specified */
2576 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "GopMode", &pThisCC->u32GraphicsMode, UINT32_MAX);
2577 if (RT_FAILURE(rc))
2578 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2579 N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed"));
2580 }
2581 if (pThisCC->u32GraphicsMode == UINT32_MAX)
2582 pThisCC->u32GraphicsMode = 2; /* 1024x768, at least typically */
2583
2584 /*
2585 * EFI graphics resolution, defaults to 1024x768 (used to be UGA only, now
2586 * is the main config setting as the mode number is so hard to predict).
2587 */
2588 char szResolution[16];
2589 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "GraphicsResolution", szResolution, sizeof(szResolution), "");
2590 if (RT_FAILURE(rc))
2591 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2592 N_("Configuration error: Querying \"GraphicsResolution\" as a string failed"));
2593 if (szResolution[0])
2594 {
2595 const char *pszX = RTStrStr(szResolution, "x");
2596 if (pszX)
2597 {
2598 pThisCC->u32HorizontalResolution = RTStrToUInt32(szResolution);
2599 pThisCC->u32VerticalResolution = RTStrToUInt32(pszX + 1);
2600 }
2601 }
2602 else
2603 {
2604 /* get the legacy values if nothing else was specified */
2605 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UgaHorizontalResolution", &pThisCC->u32HorizontalResolution, 0);
2606 AssertRCReturn(rc, rc);
2607 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UgaVerticalResolution", &pThisCC->u32VerticalResolution, 0);
2608 AssertRCReturn(rc, rc);
2609 }
2610 if (pThisCC->u32HorizontalResolution == 0 || pThisCC->u32VerticalResolution == 0)
2611 {
2612 pThisCC->u32HorizontalResolution = 1024;
2613 pThisCC->u32VerticalResolution = 768;
2614 }
2615
2616 pThisCC->pszNvramFile = NULL;
2617 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "NvramFile", &pThisCC->pszNvramFile);
2618 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2619 return PDMDEV_SET_ERROR(pDevIns, rc,
2620 N_("Configuration error: Querying \"NvramFile\" as a string failed"));
2621
2622 /*
2623 * Load firmware volume and thunk ROM.
2624 */
2625 rc = efiLoadRom(pDevIns, pThis, pThisCC, pCfg);
2626 if (RT_FAILURE(rc))
2627 return rc;
2628
2629 /*
2630 * Register our I/O ports.
2631 */
2632 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, IOM_IOPORT_F_ABS,
2633 efiR3IoPortWrite, efiR3IoPortRead,
2634 "EFI communication ports", NULL /*paExtDescs*/, &pThis->hIoPorts);
2635 AssertRCReturn(rc, rc);
2636
2637 /*
2638 * Plant DMI and MPS tables in the ROM region.
2639 */
2640 rc = FwCommonPlantDMITable(pDevIns, pThisCC->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThisCC->aUuid,
2641 pDevIns->pCfg, pThisCC->cCpus, &pThisCC->cbDmiTables, &pThisCC->cNumDmiTables);
2642 AssertRCReturn(rc, rc);
2643
2644 /*
2645 * NB: VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.c scans memory for
2646 * the SMBIOS header. The header must be placed in a range that EFI will scan.
2647 */
2648 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThisCC->au8DMIPage + VBOX_DMI_TABLE_SIZE,
2649 pThisCC->cbDmiTables, pThisCC->cNumDmiTables);
2650
2651 if (pThisCC->u8IOAPIC)
2652 {
2653 FwCommonPlantMpsTable(pDevIns,
2654 pThisCC->au8DMIPage /* aka VBOX_DMI_TABLE_BASE */ + VBOX_DMI_TABLE_SIZE + VBOX_DMI_HDR_SIZE,
2655 _4K - VBOX_DMI_TABLE_SIZE - VBOX_DMI_HDR_SIZE, pThisCC->cCpus);
2656 FwCommonPlantMpsFloatPtr(pDevIns, VBOX_DMI_TABLE_BASE + VBOX_DMI_TABLE_SIZE + VBOX_DMI_HDR_SIZE);
2657 }
2658
2659 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThisCC->au8DMIPage, _4K,
2660 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
2661
2662 AssertRCReturn(rc, rc);
2663
2664 /*
2665 * Register info handlers.
2666 */
2667 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "nvram", "Dumps the NVRAM variables.\n", efiInfoNvram);
2668 AssertRCReturn(rc, rc);
2669
2670 /*
2671 * Call reset to set things up.
2672 */
2673 efiReset(pDevIns);
2674
2675 return VINF_SUCCESS;
2676}
2677
2678#else /* IN_RING3 */
2679
2680
2681/**
2682 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
2683 */
2684static DECLCALLBACK(int) efiRZConstruct(PPDMDEVINS pDevIns)
2685{
2686 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2687 PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI);
2688
2689# if 1
2690 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioFlash, efiR3NvMmioWrite, efiR3NvMmioRead, NULL /*pvUser*/);
2691 AssertRCReturn(rc, rc);
2692# else
2693 RT_NOREF(pDevIns, pThis); (void)&efiR3NvMmioRead; (void)&efiR3NvMmioWrite;
2694# endif
2695
2696 return VINF_SUCCESS;
2697}
2698
2699
2700#endif /* IN_RING3 */
2701
2702/**
2703 * The device registration structure.
2704 */
2705const PDMDEVREG g_DeviceEFI =
2706{
2707 /* .u32Version = */ PDM_DEVREG_VERSION,
2708 /* .uReserved0 = */ 0,
2709 /* .szName = */ "efi",
2710 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
2711 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
2712 /* .cMaxInstances = */ 1,
2713 /* .uSharedVersion = */ 42,
2714 /* .cbInstanceShared = */ sizeof(DEVEFI),
2715 /* .cbInstanceCC = */ sizeof(DEVEFICC),
2716 /* .cbInstanceRC = */ sizeof(DEVEFIRC),
2717 /* .cMaxPciDevices = */ 0,
2718 /* .cMaxMsixVectors = */ 0,
2719 /* .pszDescription = */ "Extensible Firmware Interface Device.\n"
2720 "LUN#0 - NVRAM port",
2721#if defined(IN_RING3)
2722 /* .pszRCMod = */ "VBoxDDRC.rc",
2723 /* .pszR0Mod = */ "VBoxDDR0.r0",
2724 /* .pfnConstruct = */ efiConstruct,
2725 /* .pfnDestruct = */ efiDestruct,
2726 /* .pfnRelocate = */ NULL,
2727 /* .pfnMemSetup = */ efiMemSetup,
2728 /* .pfnPowerOn = */ NULL,
2729 /* .pfnReset = */ efiReset,
2730 /* .pfnSuspend = */ NULL,
2731 /* .pfnResume = */ NULL,
2732 /* .pfnAttach = */ NULL,
2733 /* .pfnDetach = */ NULL,
2734 /* .pfnQueryInterface = */ NULL,
2735 /* .pfnInitComplete = */ efiInitComplete,
2736 /* .pfnPowerOff = */ efiPowerOff,
2737 /* .pfnSoftReset = */ NULL,
2738 /* .pfnReserved0 = */ NULL,
2739 /* .pfnReserved1 = */ NULL,
2740 /* .pfnReserved2 = */ NULL,
2741 /* .pfnReserved3 = */ NULL,
2742 /* .pfnReserved4 = */ NULL,
2743 /* .pfnReserved5 = */ NULL,
2744 /* .pfnReserved6 = */ NULL,
2745 /* .pfnReserved7 = */ NULL,
2746#elif defined(IN_RING0)
2747 /* .pfnEarlyConstruct = */ NULL,
2748 /* .pfnConstruct = */ efiRZConstruct,
2749 /* .pfnDestruct = */ NULL,
2750 /* .pfnFinalDestruct = */ NULL,
2751 /* .pfnRequest = */ NULL,
2752 /* .pfnReserved0 = */ NULL,
2753 /* .pfnReserved1 = */ NULL,
2754 /* .pfnReserved2 = */ NULL,
2755 /* .pfnReserved3 = */ NULL,
2756 /* .pfnReserved4 = */ NULL,
2757 /* .pfnReserved5 = */ NULL,
2758 /* .pfnReserved6 = */ NULL,
2759 /* .pfnReserved7 = */ NULL,
2760#elif defined(IN_RC)
2761 /* .pfnConstruct = */ efiRZConstruct,
2762 /* .pfnReserved0 = */ NULL,
2763 /* .pfnReserved1 = */ NULL,
2764 /* .pfnReserved2 = */ NULL,
2765 /* .pfnReserved3 = */ NULL,
2766 /* .pfnReserved4 = */ NULL,
2767 /* .pfnReserved5 = */ NULL,
2768 /* .pfnReserved6 = */ NULL,
2769 /* .pfnReserved7 = */ NULL,
2770#else
2771# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2772#endif
2773 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2774};
2775
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