VirtualBox

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

Last change on this file since 75265 was 73142, checked in by vboxsync, 6 years ago

BIOS/EFI: Explicitly specify MP table base address rather than hardcoding.

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