VirtualBox

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

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

DevEFI,VBoxDD2: More preps for baking the EFI firmware images into VBoxDD2.

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