VirtualBox

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

Last change on this file since 72500 was 72500, checked in by vboxsync, 7 years ago

EFI: Log EFI module relocations, such as when an OS switches to its own virtual memory map. Needed to know where run-time EFI modules end up.

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