VirtualBox

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

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

DevEFI: Increased the EFI firmware sizes to 4MB so we can make sure the flash/MMIO area does not conflict with old saved. bugref::6940

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