VirtualBox

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

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

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.2 KB
Line 
1/* $Id: DevEFI.cpp 69500 2017-10-28 15:14:05Z 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 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
1249
1250 /* Reset the state. */
1251 RT_ZERO(pThis->ImageEvt);
1252 pThis->ImageEvt.uEvt = (uint8_t)u32; Assert(pThis->ImageEvt.uEvt == u32);
1253 return VINF_SUCCESS;
1254
1255 case EFI_IMAGE_EVT_CMD_COMPLETE:
1256 {
1257#ifdef IN_RING3
1258 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
1259
1260 /* For now, just log it. */
1261 static uint64_t s_cImageEvtLogged = 0;
1262 if (s_cImageEvtLogged < 2048)
1263 {
1264 s_cImageEvtLogged++;
1265 switch (pThis->ImageEvt.uEvt)
1266 {
1267 /* ASSUMES the name ends with .pdb and the image file ends with .efi! */
1268 case EFI_IMAGE_EVT_CMD_START_LOAD32:
1269 LogRel(("EFI: VBoxDbg> loadimage32 '%.*s.efi' %#llx LB %#llx\n",
1270 pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->ImageEvt.uAddr0, pThis->ImageEvt.cb0));
1271 if (pThis->ImageEvt.offName > 4)
1272 efiVBoxDbgScript("loadimage32 '%.*s.efi' %#llx\n",
1273 pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->ImageEvt.uAddr0);
1274 break;
1275 case EFI_IMAGE_EVT_CMD_START_LOAD64:
1276 LogRel(("EFI: VBoxDbg> loadimage64 '%.*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("loadimage64 '%.*s.efi' %#llx\n",
1280 pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->ImageEvt.uAddr0);
1281 break;
1282 case EFI_IMAGE_EVT_CMD_START_UNLOAD32:
1283 case EFI_IMAGE_EVT_CMD_START_UNLOAD64:
1284 {
1285 LogRel(("EFI: VBoxDbg> unload '%.*s.efi' # %#llx LB %#llx\n",
1286 pThis->ImageEvt.offName - 4 - pThis->ImageEvt.offNameLastComponent,
1287 &pThis->ImageEvt.szName[pThis->ImageEvt.offNameLastComponent],
1288 pThis->ImageEvt.uAddr0, pThis->ImageEvt.cb0));
1289 if (pThis->ImageEvt.offName > 4)
1290 efiVBoxDbgScript("unload '%.*s.efi'\n",
1291 pThis->ImageEvt.offName - 4 - pThis->ImageEvt.offNameLastComponent,
1292 &pThis->ImageEvt.szName[pThis->ImageEvt.offNameLastComponent]);
1293 break;
1294 }
1295 }
1296 }
1297 return VINF_SUCCESS;
1298#else
1299 return VINF_IOM_R3_IOPORT_WRITE;
1300#endif
1301 }
1302
1303 case EFI_IMAGE_EVT_CMD_ADDR0:
1304 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
1305 pThis->ImageEvt.uAddr0 <<= 16;
1306 pThis->ImageEvt.uAddr0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
1307 return VINF_SUCCESS;
1308
1309 case EFI_IMAGE_EVT_CMD_ADDR1:
1310 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
1311 pThis->ImageEvt.uAddr0 <<= 16;
1312 pThis->ImageEvt.uAddr0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
1313 return VINF_SUCCESS;
1314
1315 case EFI_IMAGE_EVT_CMD_SIZE0:
1316 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX);
1317 pThis->ImageEvt.cb0 <<= 16;
1318 pThis->ImageEvt.cb0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32);
1319 return VINF_SUCCESS;
1320
1321 case EFI_IMAGE_EVT_CMD_NAME:
1322 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= 0x7f);
1323 if (pThis->ImageEvt.offName < sizeof(pThis->ImageEvt.szName) - 1)
1324 {
1325 char ch = EFI_IMAGE_EVT_GET_PAYLOAD_U8(u32);
1326 if (ch == '\\')
1327 ch = '/';
1328 pThis->ImageEvt.szName[pThis->ImageEvt.offName++] = ch;
1329 if (ch == '/' || ch == ':')
1330 pThis->ImageEvt.offNameLastComponent = pThis->ImageEvt.offName;
1331 }
1332 else
1333 Log(("EFI: Image name overflow\n"));
1334 return VINF_SUCCESS;
1335 }
1336
1337 Log(("EFI: Unknown image event: %#x (cb=%d)\n", u32, cb));
1338 return VINF_SUCCESS;
1339}
1340
1341
1342/**
1343 * Port I/O Handler for IN operations.
1344 *
1345 * @returns VBox status code.
1346 *
1347 * @param pDevIns The device instance.
1348 * @param pvUser User argument - ignored.
1349 * @param Port Port number used for the IN operation.
1350 * @param pu32 Where to store the result.
1351 * @param cb Number of bytes read.
1352 */
1353static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1354{
1355 RT_NOREF(pvUser);
1356 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1357 Log4(("EFI in: %x %x\n", Port, cb));
1358
1359 switch (Port)
1360 {
1361 case EFI_INFO_PORT:
1362 if (pThis->offInfo == -1 && cb == 4)
1363 {
1364 pThis->offInfo = 0;
1365 uint32_t cbInfo = *pu32 = efiInfoSize(pThis);
1366 if (cbInfo == UINT32_MAX)
1367 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "iInfoSelector=%#x (%d)\n",
1368 pThis->iInfoSelector, pThis->iInfoSelector);
1369 }
1370 else
1371 {
1372 if (cb != 1)
1373 return VERR_IOM_IOPORT_UNUSED;
1374 *pu32 = efiInfoNextByte(pThis);
1375 pThis->offInfo++;
1376 }
1377 return VINF_SUCCESS;
1378
1379 case EFI_PANIC_PORT:
1380#ifdef IN_RING3
1381 LogRel(("EFI panic port read!\n"));
1382 /* Insert special code here on panic reads */
1383 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: panic port read!\n");
1384#else
1385 /* Reschedule to R3 */
1386 return VINF_IOM_R3_IOPORT_READ;
1387#endif
1388
1389 case EFI_PORT_VARIABLE_OP:
1390 return nvramReadVariableOp(pThis, pu32, cb);
1391
1392 case EFI_PORT_VARIABLE_PARAM:
1393 case EFI_PORT_DEBUG_POINT:
1394 case EFI_PORT_IMAGE_EVENT:
1395 *pu32 = UINT32_MAX;
1396 return VINF_SUCCESS;
1397 }
1398
1399 return VERR_IOM_IOPORT_UNUSED;
1400}
1401
1402
1403/**
1404 * Translates a debug point value into a string for logging.
1405 *
1406 * @returns read-only string
1407 * @param enmDbgPoint Valid debug point value.
1408 */
1409static const char *efiDbgPointName(EFIDBGPOINT enmDbgPoint)
1410{
1411 switch (enmDbgPoint)
1412 {
1413 case EFIDBGPOINT_SEC_PREMEM: return "SEC_PREMEM";
1414 case EFIDBGPOINT_SEC_POSTMEM: return "SEC_POSTMEM";
1415 case EFIDBGPOINT_DXE_CORE: return "DXE_CORE";
1416 case EFIDBGPOINT_SMM: return "SMM";
1417 case EFIDBGPOINT_SMI_ENTER: return "SMI_ENTER";
1418 case EFIDBGPOINT_SMI_EXIT: return "SMI_EXIT";
1419 case EFIDBGPOINT_GRAPHICS: return "GRAPHICS";
1420 case EFIDBGPOINT_DXE_AP: return "DXE_AP";
1421 default:
1422 AssertFailed();
1423 return "Unknown";
1424 }
1425}
1426
1427
1428/**
1429 * Port I/O Handler for OUT operations.
1430 *
1431 * @returns VBox status code.
1432 *
1433 * @param pDevIns The device instance.
1434 * @param pvUser User argument - ignored.
1435 * @param Port Port number used for the IN operation.
1436 * @param u32 The value to output.
1437 * @param cb The value size in bytes.
1438 */
1439static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1440{
1441 RT_NOREF(pvUser);
1442 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1443 int rc = VINF_SUCCESS;
1444 Log4(("efi: out %x %x %d\n", Port, u32, cb));
1445
1446 switch (Port)
1447 {
1448 case EFI_INFO_PORT:
1449 Log2(("EFI_INFO_PORT: iInfoSelector=%#x\n", u32));
1450 pThis->iInfoSelector = u32;
1451 pThis->offInfo = -1;
1452 break;
1453
1454 case EFI_DEBUG_PORT:
1455 {
1456 /* The raw version. */
1457 switch (u32)
1458 {
1459 case '\r': Log3(("efi: <return>\n")); break;
1460 case '\n': Log3(("efi: <newline>\n")); break;
1461 case '\t': Log3(("efi: <tab>\n")); break;
1462 default: Log3(("efi: %c (%02x)\n", u32, u32)); break;
1463 }
1464 /* The readable, buffered version. */
1465 if (u32 == '\n' || u32 == '\r')
1466 {
1467 pThis->szMsg[pThis->iMsg] = '\0';
1468 if (pThis->iMsg)
1469 LogRel2(("efi: %s\n", pThis->szMsg));
1470 pThis->iMsg = 0;
1471 }
1472 else
1473 {
1474 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
1475 {
1476 pThis->szMsg[pThis->iMsg] = '\0';
1477 LogRel2(("efi: %s\n", pThis->szMsg));
1478 pThis->iMsg = 0;
1479 }
1480 pThis->szMsg[pThis->iMsg] = (char )u32;
1481 pThis->szMsg[++pThis->iMsg] = '\0';
1482 }
1483 break;
1484 }
1485
1486 case EFI_PANIC_PORT:
1487 {
1488 switch (u32)
1489 {
1490 case EFI_PANIC_CMD_BAD_ORG: /* Legacy */
1491 case EFI_PANIC_CMD_THUNK_TRAP:
1492#ifdef IN_RING3
1493 LogRel(("EFI: Panic! Unexpected trap!!\n"));
1494# ifdef VBOX_STRICT
1495 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n");
1496# else
1497 AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
1498# endif
1499 break;
1500#else
1501 return VINF_IOM_R3_IOPORT_WRITE;
1502#endif
1503
1504 case EFI_PANIC_CMD_START_MSG:
1505 LogRel(("Receiving EFI panic...\n"));
1506 pThis->iPanicMsg = 0;
1507 pThis->szPanicMsg[0] = '\0';
1508 break;
1509
1510 case EFI_PANIC_CMD_END_MSG:
1511#ifdef IN_RING3
1512 LogRel(("EFI: Panic! %s\n", pThis->szPanicMsg));
1513# ifdef VBOX_STRICT
1514 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThis->szPanicMsg);
1515# else
1516 return VERR_INTERNAL_ERROR;
1517# endif
1518#else
1519 return VINF_IOM_R3_IOPORT_WRITE;
1520#endif
1521
1522
1523 default:
1524 if ( u32 >= EFI_PANIC_CMD_MSG_FIRST
1525 && u32 <= EFI_PANIC_CMD_MSG_LAST)
1526 {
1527 /* Add the message char to the buffer. */
1528 uint32_t i = pThis->iPanicMsg;
1529 if (i + 1 < sizeof(pThis->szPanicMsg))
1530 {
1531 char ch = EFI_PANIC_CMD_MSG_GET_CHAR(u32);
1532 if ( ch == '\n'
1533 && i > 0
1534 && pThis->szPanicMsg[i - 1] == '\r')
1535 i--;
1536 pThis->szPanicMsg[i] = ch;
1537 pThis->szPanicMsg[i + 1] = '\0';
1538 pThis->iPanicMsg = i + 1;
1539 }
1540 }
1541 else
1542 Log(("EFI: Unknown panic command: %#x (cb=%d)\n", u32, cb));
1543 break;
1544 }
1545 break;
1546 }
1547
1548 case EFI_PORT_VARIABLE_OP:
1549 {
1550 /* clear buffer index */
1551 if (u32 >= (uint32_t)EFI_VM_VARIABLE_OP_MAX)
1552 {
1553 Log(("EFI: Invalid variable op %#x\n", u32));
1554 u32 = EFI_VM_VARIABLE_OP_ERROR;
1555 }
1556 pThis->NVRAM.offOpBuffer = 0;
1557 pThis->NVRAM.enmOp = (EFIVAROP)u32;
1558 Log2(("EFI_VARIABLE_OP: enmOp=%#x (%d)\n", u32, u32));
1559 break;
1560 }
1561
1562 case EFI_PORT_VARIABLE_PARAM:
1563 rc = nvramWriteVariableParam(pThis, u32);
1564 break;
1565
1566 case EFI_PORT_DEBUG_POINT:
1567#ifdef IN_RING3
1568 if (u32 > EFIDBGPOINT_INVALID && u32 < EFIDBGPOINT_END)
1569 {
1570 /* For now, just log it. */
1571 LogRelMax(1024, ("EFI: debug point %s\n", efiDbgPointName((EFIDBGPOINT)u32)));
1572 rc = VINF_SUCCESS;
1573 }
1574 else
1575 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Invalid debug point %#x\n", u32);
1576 break;
1577#else
1578 return VINF_IOM_R3_IOPORT_WRITE;
1579#endif
1580
1581 case EFI_PORT_IMAGE_EVENT:
1582 rc = efiPortImageEventWrite(pThis, u32, cb);
1583 break;
1584
1585 default:
1586 Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", Port, u32, cb));
1587 break;
1588 }
1589 return rc;
1590}
1591
1592static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1593{
1594 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1595 LogFlow(("efiSaveExec:\n"));
1596
1597 /*
1598 * Set variables only used when saving state.
1599 */
1600 uint32_t idUniqueSavedState = 0;
1601 PEFIVAR pEfiVar;
1602 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
1603 {
1604 pEfiVar->idUniqueSavedState = idUniqueSavedState++;
1605 }
1606 Assert(idUniqueSavedState == pThis->NVRAM.cVariables);
1607
1608 pThis->NVRAM.idUniqueCurVar = pThis->NVRAM.pCurVar
1609 ? pThis->NVRAM.pCurVar->idUniqueSavedState
1610 : UINT32_MAX;
1611
1612 /*
1613 * Save the NVRAM state.
1614 */
1615 SSMR3PutStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
1616 SSMR3PutStructEx(pSSM, &pThis->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1617
1618 /*
1619 * Save the list variables (we saved the length above).
1620 */
1621 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
1622 {
1623 SSMR3PutStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1624 }
1625
1626 return VINF_SUCCESS; /* SSM knows */
1627}
1628
1629static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1630{
1631 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1632 LogFlow(("efiLoadExec: uVersion=%d uPass=%d\n", uVersion, uPass));
1633
1634 /*
1635 * Validate input.
1636 */
1637 if (uPass != SSM_PASS_FINAL)
1638 return VERR_SSM_UNEXPECTED_PASS;
1639 if ( uVersion != EFI_SSM_VERSION
1640 && uVersion != EFI_SSM_VERSION_4_2
1641 )
1642 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1643
1644 /*
1645 * Kill the current variables before loading anything.
1646 */
1647 nvramFlushDeviceVariableList(pThis);
1648
1649 /*
1650 * Load the NVRAM state.
1651 */
1652 int rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
1653 AssertRCReturn(rc, rc);
1654 pThis->NVRAM.pCurVar = NULL;
1655
1656 rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1657 AssertRCReturn(rc, rc);
1658
1659 /*
1660 * Load variables.
1661 */
1662 pThis->NVRAM.pCurVar = NULL;
1663 Assert(RTListIsEmpty(&pThis->NVRAM.VarList));
1664 RTListInit(&pThis->NVRAM.VarList);
1665 for (uint32_t i = 0; i < pThis->NVRAM.cVariables; i++)
1666 {
1667 PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
1668 AssertReturn(pEfiVar, VERR_NO_MEMORY);
1669
1670 rc = SSMR3GetStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1671 if (RT_SUCCESS(rc))
1672 {
1673 if ( pEfiVar->cbValue > sizeof(pEfiVar->abValue)
1674 || pEfiVar->cbValue == 0)
1675 {
1676 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1677 LogRel(("EFI: Loaded invalid variable value length %#x\n", pEfiVar->cbValue));
1678 }
1679 uint32_t cchVarName = (uint32_t)RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
1680 if (cchVarName >= sizeof(pEfiVar->szName))
1681 {
1682 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1683 LogRel(("EFI: Loaded variable name is unterminated.\n"));
1684 }
1685 if (pEfiVar->cchName > cchVarName) /* No check for 0 here, busted load code in 4.2, so now storing 0 here. */
1686 {
1687 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1688 LogRel(("EFI: Loaded invalid variable name length %#x (cchVarName=%#x)\n", pEfiVar->cchName, cchVarName));
1689 }
1690 if (RT_SUCCESS(rc))
1691 pEfiVar->cchName = cchVarName;
1692 }
1693 AssertRCReturnStmt(rc, RTMemFree(pEfiVar), rc);
1694
1695 /* Add it (not using nvramInsertVariable to preserve saved order),
1696 updating the current variable pointer while we're here. */
1697#if 1
1698 RTListAppend(&pThis->NVRAM.VarList, &pEfiVar->ListNode);
1699#else
1700 nvramInsertVariable(pThis, pEfiVar);
1701#endif
1702 if (pThis->NVRAM.idUniqueCurVar == pEfiVar->idUniqueSavedState)
1703 pThis->NVRAM.pCurVar = pEfiVar;
1704 }
1705
1706 return VINF_SUCCESS;
1707}
1708
1709
1710/**
1711 * @copydoc(PDMIBASE::pfnQueryInterface)
1712 */
1713static DECLCALLBACK(void *) devEfiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1714{
1715 LogFlowFunc(("ENTER: pIBase=%p pszIID=%p\n", pInterface, pszIID));
1716 PDEVEFI pThis = RT_FROM_MEMBER(pInterface, DEVEFI, Lun0.IBase);
1717
1718 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase);
1719 return NULL;
1720}
1721
1722
1723/**
1724 * Write to CMOS memory.
1725 * This is used by the init complete code.
1726 */
1727static void cmosWrite(PPDMDEVINS pDevIns, unsigned off, uint32_t u32Val)
1728{
1729 Assert(off < 128);
1730 Assert(u32Val < 256);
1731
1732 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
1733 AssertRC(rc);
1734}
1735
1736/**
1737 * Init complete notification.
1738 *
1739 * @returns VBOX status code.
1740 * @param pDevIns The device instance.
1741 */
1742static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
1743{
1744 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1745
1746 PVM pVM = PDMDevHlpGetVM(pDevIns);
1747 uint64_t const cbRamSize = MMR3PhysGetRamSize(pVM);
1748 uint32_t const cbBelow4GB = MMR3PhysGetRamSizeBelow4GB(pVM);
1749 uint64_t const cbAbove4GB = MMR3PhysGetRamSizeAbove4GB(pVM);
1750 NOREF(cbAbove4GB);
1751
1752 /*
1753 * Memory sizes.
1754 */
1755 uint32_t u32Low = 0;
1756 uint32_t u32Chunks = 0;
1757 if (cbRamSize > 16 * _1M)
1758 {
1759 u32Low = RT_MIN(cbBelow4GB, UINT32_C(0xfe000000));
1760 u32Chunks = (u32Low - 16U * _1M) / _64K;
1761 }
1762 cmosWrite(pDevIns, 0x34, RT_BYTE1(u32Chunks));
1763 cmosWrite(pDevIns, 0x35, RT_BYTE2(u32Chunks));
1764
1765 if (u32Low < cbRamSize)
1766 {
1767 uint64_t u64 = cbRamSize - u32Low;
1768 u32Chunks = (uint32_t)(u64 / _64K);
1769 cmosWrite(pDevIns, 0x5b, RT_BYTE1(u32Chunks));
1770 cmosWrite(pDevIns, 0x5c, RT_BYTE2(u32Chunks));
1771 cmosWrite(pDevIns, 0x5d, RT_BYTE3(u32Chunks));
1772 cmosWrite(pDevIns, 0x5e, RT_BYTE4(u32Chunks));
1773 }
1774
1775 /*
1776 * Number of CPUs.
1777 */
1778 cmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
1779
1780 return VINF_SUCCESS;
1781}
1782
1783
1784/**
1785 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1786 */
1787static DECLCALLBACK(void) efiMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1788{
1789 RT_NOREF(enmCtx);
1790 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1791
1792 /*
1793 * Plant some structures in RAM.
1794 */
1795 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->cbDmiTables, pThis->cNumDmiTables);
1796 if (pThis->u8IOAPIC)
1797 FwCommonPlantMpsFloatPtr(pDevIns);
1798
1799 /*
1800 * Re-shadow the Firmware Volume and make it RAM/RAM.
1801 */
1802 uint32_t cPages = RT_ALIGN_64(pThis->cbEfiRom, PAGE_SIZE) >> PAGE_SHIFT;
1803 RTGCPHYS GCPhys = pThis->GCLoadAddress;
1804 while (cPages > 0)
1805 {
1806 uint8_t abPage[PAGE_SIZE];
1807
1808 /* Read the (original) ROM page and write it back to the RAM page. */
1809 int rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1810 AssertLogRelRC(rc);
1811
1812 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
1813 AssertLogRelRC(rc);
1814 if (RT_FAILURE(rc))
1815 memset(abPage, 0xcc, sizeof(abPage));
1816
1817 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
1818 AssertLogRelRC(rc);
1819
1820 /* Switch to the RAM/RAM mode. */
1821 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1822 AssertLogRelRC(rc);
1823
1824 /* Advance */
1825 GCPhys += PAGE_SIZE;
1826 cPages--;
1827 }
1828}
1829
1830
1831/**
1832 * @interface_method_impl{PDMDEVREG,pfnReset}
1833 */
1834static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
1835{
1836 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1837
1838 LogFlow(("efiReset\n"));
1839
1840 pThis->iInfoSelector = 0;
1841 pThis->offInfo = -1;
1842
1843 pThis->iMsg = 0;
1844 pThis->szMsg[0] = '\0';
1845 pThis->iPanicMsg = 0;
1846 pThis->szPanicMsg[0] = '\0';
1847
1848#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
1849 /*
1850 * Zap the debugger script
1851 */
1852 RTFileDelete("./DevEFI.VBoxDbg");
1853#endif
1854}
1855
1856
1857/**
1858 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
1859 */
1860static DECLCALLBACK(void) efiPowerOff(PPDMDEVINS pDevIns)
1861{
1862 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1863
1864 if (pThis->Lun0.pNvramDrv)
1865 nvramStore(pThis);
1866}
1867
1868
1869
1870/**
1871 * Destruct a device instance.
1872 *
1873 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1874 * resources can be freed correctly.
1875 *
1876 * @param pDevIns The device instance data.
1877 */
1878static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
1879{
1880 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1881 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1882
1883 nvramFlushDeviceVariableList(pThis);
1884
1885 if (pThis->pu8EfiRom)
1886 {
1887 RTFileReadAllFree(pThis->pu8EfiRom, (size_t)pThis->cbEfiRom);
1888 pThis->pu8EfiRom = NULL;
1889 }
1890
1891 /*
1892 * Free MM heap pointers (waste of time, but whatever).
1893 */
1894 if (pThis->pszEfiRomFile)
1895 {
1896 MMR3HeapFree(pThis->pszEfiRomFile);
1897 pThis->pszEfiRomFile = NULL;
1898 }
1899
1900 if (pThis->pu8EfiThunk)
1901 {
1902 MMR3HeapFree(pThis->pu8EfiThunk);
1903 pThis->pu8EfiThunk = NULL;
1904 }
1905
1906 if (pThis->pbDeviceProps)
1907 {
1908 PDMDevHlpMMHeapFree(pDevIns, pThis->pbDeviceProps);
1909 pThis->pbDeviceProps = NULL;
1910 pThis->cbDeviceProps = 0;
1911 }
1912
1913 return VINF_SUCCESS;
1914}
1915
1916
1917#if 0 /* unused */
1918/**
1919 * Helper that searches for a FFS file of a given type.
1920 *
1921 * @returns Pointer to the FFS file header if found, NULL if not.
1922 *
1923 * @param pFfsFile Pointer to the FFS file header to start searching at.
1924 * @param pbEnd The end of the firmware volume.
1925 * @param FileType The file type to look for.
1926 * @param pcbFfsFile Where to store the FFS file size (includes header).
1927 */
1928DECLINLINE(EFI_FFS_FILE_HEADER const *)
1929efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
1930{
1931# define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
1932 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
1933 {
1934 if (pFfsFile->Type == FileType)
1935 {
1936 *pcbFile = FFS_SIZE(pFfsFile);
1937 LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType));
1938 return pFfsFile;
1939 }
1940 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
1941 }
1942# undef FFS_SIZE
1943 return NULL;
1944}
1945#endif /* unused */
1946
1947
1948/**
1949 * Parse EFI ROM headers and find entry points.
1950 *
1951 * @returns VBox status code.
1952 * @param pThis The device instance data.
1953 */
1954static int efiParseFirmware(PDEVEFI pThis)
1955{
1956 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThis->pu8EfiRom;
1957
1958 /*
1959 * Validate firmware volume header.
1960 */
1961 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
1962 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
1963 VERR_INVALID_MAGIC);
1964 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
1965 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
1966 VERR_VERSION_MISMATCH);
1967 /** @todo check checksum, see PE spec vol. 3 */
1968 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThis->cbEfiRom,
1969 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThis->cbEfiRom),
1970 VERR_INVALID_PARAMETER);
1971 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
1972 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
1973 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
1974 VERR_INVALID_PARAMETER);
1975
1976 AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
1977
1978 pThis->GCLoadAddress = UINT32_C(0xfffff000) - pThis->cbEfiRom + PAGE_SIZE;
1979
1980 return VINF_SUCCESS;
1981}
1982
1983/**
1984 * Load EFI ROM file into the memory.
1985 *
1986 * @returns VBox status code.
1987 * @param pThis The device instance data.
1988 * @param pCfg Configuration node handle for the device.
1989 */
1990static int efiLoadRom(PDEVEFI pThis, PCFGMNODE pCfg)
1991{
1992 RT_NOREF(pCfg);
1993
1994 /*
1995 * Read the entire firmware volume into memory.
1996 */
1997 void *pvFile;
1998 size_t cbFile;
1999 int rc = RTFileReadAllEx(pThis->pszEfiRomFile,
2000 0 /*off*/,
2001 RTFOFF_MAX /*cbMax*/,
2002 RTFILE_RDALL_O_DENY_WRITE,
2003 &pvFile,
2004 &cbFile);
2005 if (RT_FAILURE(rc))
2006 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
2007 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
2008 pThis->pszEfiRomFile, rc);
2009 pThis->pu8EfiRom = (uint8_t *)pvFile;
2010 pThis->cbEfiRom = cbFile;
2011
2012 /*
2013 * Validate firmware volume and figure out the load address as well as the SEC entry point.
2014 */
2015 rc = efiParseFirmware(pThis);
2016 if (RT_FAILURE(rc))
2017 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
2018 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
2019 pThis->pszEfiRomFile, rc);
2020
2021 /*
2022 * Map the firmware volume into memory as shadowed ROM.
2023 */
2024 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
2025 RTGCPHYS cbQuart = RT_ALIGN_64(pThis->cbEfiRom / 4, PAGE_SIZE);
2026 rc = PDMDevHlpROMRegister(pThis->pDevIns,
2027 pThis->GCLoadAddress,
2028 cbQuart,
2029 pThis->pu8EfiRom,
2030 cbQuart,
2031 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2032 "EFI Firmware Volume");
2033 AssertRCReturn(rc, rc);
2034 rc = PDMDevHlpROMProtectShadow(pThis->pDevIns, pThis->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
2035 AssertRCReturn(rc, rc);
2036 rc = PDMDevHlpROMRegister(pThis->pDevIns,
2037 pThis->GCLoadAddress + cbQuart,
2038 cbQuart,
2039 pThis->pu8EfiRom + cbQuart,
2040 cbQuart,
2041 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2042 "EFI Firmware Volume (Part 2)");
2043 if (RT_FAILURE(rc))
2044 return rc;
2045 rc = PDMDevHlpROMRegister(pThis->pDevIns,
2046 pThis->GCLoadAddress + cbQuart * 2,
2047 cbQuart,
2048 pThis->pu8EfiRom + cbQuart * 2,
2049 cbQuart,
2050 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2051 "EFI Firmware Volume (Part 3)");
2052 if (RT_FAILURE(rc))
2053 return rc;
2054 rc = PDMDevHlpROMRegister(pThis->pDevIns,
2055 pThis->GCLoadAddress + cbQuart * 3,
2056 pThis->cbEfiRom - cbQuart * 3,
2057 pThis->pu8EfiRom + cbQuart * 3,
2058 pThis->cbEfiRom - cbQuart * 3,
2059 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2060 "EFI Firmware Volume (Part 4)");
2061 if (RT_FAILURE(rc))
2062 return rc;
2063 return VINF_SUCCESS;
2064}
2065
2066static uint8_t efiGetHalfByte(char ch)
2067{
2068 uint8_t val;
2069
2070 if (ch >= '0' && ch <= '9')
2071 val = ch - '0';
2072 else if (ch >= 'A' && ch <= 'F')
2073 val = ch - 'A' + 10;
2074 else if(ch >= 'a' && ch <= 'f')
2075 val = ch - 'a' + 10;
2076 else
2077 val = 0xff;
2078
2079 return val;
2080
2081}
2082
2083
2084/**
2085 * Converts a hex string into a binary data blob located at
2086 * pThis->pbDeviceProps, size returned as pThis->cbDeviceProps.
2087 *
2088 * @returns VERR_NO_MEMORY or VINF_SUCCESS.
2089 * @param pThis The EFI instance data.
2090 * @param pszDeviceProps The device property hex string to decode.
2091 */
2092static int efiParseDeviceString(PDEVEFI pThis, const char *pszDeviceProps)
2093{
2094 uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
2095 pThis->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThis->pDevIns, cbOut);
2096 if (!pThis->pbDeviceProps)
2097 return VERR_NO_MEMORY;
2098
2099 uint32_t iHex = 0;
2100 bool fUpper = true;
2101 uint8_t u8Value = 0; /* (shut up gcc) */
2102 for (uint32_t iStr = 0; pszDeviceProps[iStr]; iStr++)
2103 {
2104 uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]);
2105 if (u8Hb > 0xf)
2106 continue;
2107
2108 if (fUpper)
2109 u8Value = u8Hb << 4;
2110 else
2111 pThis->pbDeviceProps[iHex++] = u8Hb | u8Value;
2112
2113 Assert(iHex < cbOut);
2114 fUpper = !fUpper;
2115 }
2116
2117 Assert(iHex == 0 || fUpper);
2118 pThis->cbDeviceProps = iHex;
2119
2120 return VINF_SUCCESS;
2121}
2122
2123
2124/**
2125 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2126 */
2127static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2128{
2129 RT_NOREF(iInstance);
2130 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2131 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
2132 int rc;
2133
2134 Assert(iInstance == 0);
2135
2136 /*
2137 * Initalize the basic variables so that the destructor always works.
2138 */
2139 pThis->pDevIns = pDevIns;
2140 RTListInit(&pThis->NVRAM.VarList);
2141 pThis->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface;
2142
2143
2144 /*
2145 * Validate and read the configuration.
2146 */
2147 if (!CFGMR3AreValuesValid(pCfg,
2148 "EfiRom\0"
2149 "NumCPUs\0"
2150 "McfgBase\0"
2151 "McfgLength\0"
2152 "UUID\0"
2153 "IOAPIC\0"
2154 "APIC\0"
2155 "DmiBIOSFirmwareMajor\0"
2156 "DmiBIOSFirmwareMinor\0"
2157 "DmiBIOSReleaseDate\0"
2158 "DmiBIOSReleaseMajor\0"
2159 "DmiBIOSReleaseMinor\0"
2160 "DmiBIOSVendor\0"
2161 "DmiBIOSVersion\0"
2162 "DmiSystemFamily\0"
2163 "DmiSystemProduct\0"
2164 "DmiSystemSerial\0"
2165 "DmiSystemSKU\0"
2166 "DmiSystemUuid\0"
2167 "DmiSystemVendor\0"
2168 "DmiSystemVersion\0"
2169 "DmiBoardAssetTag\0"
2170 "DmiBoardBoardType\0"
2171 "DmiBoardLocInChass\0"
2172 "DmiBoardProduct\0"
2173 "DmiBoardSerial\0"
2174 "DmiBoardVendor\0"
2175 "DmiBoardVersion\0"
2176 "DmiChassisAssetTag\0"
2177 "DmiChassisSerial\0"
2178 "DmiChassisType\0"
2179 "DmiChassisVendor\0"
2180 "DmiChassisVersion\0"
2181 "DmiProcManufacturer\0"
2182 "DmiProcVersion\0"
2183 "DmiOEMVBoxVer\0"
2184 "DmiOEMVBoxRev\0"
2185 "DmiUseHostInfo\0"
2186 "DmiExposeMemoryTable\0"
2187 "DmiExposeProcInf\0"
2188 "64BitEntry\0"
2189 "BootArgs\0"
2190 "DeviceProps\0"
2191 "GopMode\0" // legacy
2192 "GraphicsMode\0"
2193 "UgaHorizontalResolution\0" // legacy
2194 "UgaVerticalResolution\0" // legacy
2195 "GraphicsResolution\0"))
2196 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2197 N_("Configuration error: Invalid config value(s) for the EFI device"));
2198
2199 /* CPU count (optional). */
2200 rc = CFGMR3QueryU32Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
2201 AssertLogRelRCReturn(rc, rc);
2202
2203 rc = CFGMR3QueryU64Def(pCfg, "McfgBase", &pThis->u64McfgBase, 0);
2204 if (RT_FAILURE(rc))
2205 return PDMDEV_SET_ERROR(pDevIns, rc,
2206 N_("Configuration error: Querying \"\" as integer failed"));
2207 rc = CFGMR3QueryU64Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
2208 if (RT_FAILURE(rc))
2209 return PDMDEV_SET_ERROR(pDevIns, rc,
2210 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
2211
2212 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
2213 if (RT_FAILURE (rc))
2214 return PDMDEV_SET_ERROR(pDevIns, rc,
2215 N_("Configuration error: Failed to read \"IOAPIC\""));
2216
2217 rc = CFGMR3QueryU8Def(pCfg, "APIC", &pThis->u8APIC, 1);
2218 if (RT_FAILURE (rc))
2219 return PDMDEV_SET_ERROR(pDevIns, rc,
2220 N_("Configuration error: Failed to read \"APIC\""));
2221
2222 /*
2223 * Query the machine's UUID for SMBIOS/DMI use.
2224 */
2225 RTUUID uuid;
2226 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
2227 if (RT_FAILURE(rc))
2228 return PDMDEV_SET_ERROR(pDevIns, rc,
2229 N_("Configuration error: Querying \"UUID\" failed"));
2230
2231 /*
2232 * Convert the UUID to network byte order. Not entirely straightforward as
2233 * parts are MSB already...
2234 */
2235 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
2236 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
2237 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
2238 memcpy(&pThis->aUuid, &uuid, sizeof pThis->aUuid);
2239
2240 /*
2241 * Get the system EFI ROM file name.
2242 */
2243 rc = CFGMR3QueryStringAlloc(pCfg, "EfiRom", &pThis->pszEfiRomFile);
2244 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2245 {
2246 pThis->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
2247 if (!pThis->pszEfiRomFile)
2248 return VERR_NO_MEMORY;
2249
2250 rc = RTPathAppPrivateArchTop(pThis->pszEfiRomFile, RTPATH_MAX);
2251 AssertRCReturn(rc, rc);
2252 rc = RTPathAppend(pThis->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd");
2253 AssertRCReturn(rc, rc);
2254 }
2255 else if (RT_FAILURE(rc))
2256 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2257 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
2258 else if (!*pThis->pszEfiRomFile)
2259 {
2260 MMR3HeapFree(pThis->pszEfiRomFile);
2261 pThis->pszEfiRomFile = NULL;
2262 }
2263
2264 /*
2265 * NVRAM processing.
2266 */
2267 rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThis), efiSaveExec, efiLoadExec);
2268 AssertRCReturn(rc, rc);
2269
2270 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "NvramStorage");
2271 if (RT_FAILURE(rc))
2272 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
2273
2274 pThis->Lun0.pNvramDrv = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pDrvBase, PDMINVRAMCONNECTOR);
2275 AssertPtrReturn(pThis->Lun0.pNvramDrv, VERR_PDM_MISSING_INTERFACE_BELOW);
2276
2277 rc = nvramLoad(pThis);
2278 AssertRCReturn(rc, rc);
2279
2280 /*
2281 * Get boot args.
2282 */
2283 rc = CFGMR3QueryStringDef(pCfg, "BootArgs", pThis->szBootArgs, sizeof(pThis->szBootArgs), "");
2284 if (RT_FAILURE(rc))
2285 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2286 N_("Configuration error: Querying \"BootArgs\" as a string failed"));
2287
2288 //strcpy(pThis->szBootArgs, "-v keepsyms=1 io=0xf debug=0x2a");
2289 //strcpy(pThis->szBootArgs, "-v keepsyms=1 debug=0x2a");
2290 LogRel(("EFI: boot args = %s\n", pThis->szBootArgs));
2291
2292 /*
2293 * Get device props.
2294 */
2295 char *pszDeviceProps;
2296 rc = CFGMR3QueryStringAllocDef(pCfg, "DeviceProps", &pszDeviceProps, NULL);
2297 if (RT_FAILURE(rc))
2298 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2299 N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
2300 if (pszDeviceProps)
2301 {
2302 LogRel(("EFI: device props = %s\n", pszDeviceProps));
2303 rc = efiParseDeviceString(pThis, pszDeviceProps);
2304 MMR3HeapFree(pszDeviceProps);
2305 if (RT_FAILURE(rc))
2306 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2307 N_("Configuration error: Cannot parse device properties"));
2308 }
2309 else
2310 {
2311 pThis->pbDeviceProps = NULL;
2312 pThis->cbDeviceProps = 0;
2313 }
2314
2315 /*
2316 * CPU frequencies.
2317 */
2318 pThis->u64TscFrequency = TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
2319 pThis->u64CpuFrequency = pThis->u64TscFrequency;
2320 pThis->u64FsbFrequency = CPUMGetGuestScalableBusFrequency(PDMDevHlpGetVM(pDevIns));
2321
2322 /*
2323 * EFI graphics mode (with new EFI VGA code used only as a fallback, for
2324 * old EFI VGA code the only way to select the GOP mode).
2325 */
2326 rc = CFGMR3QueryU32Def(pCfg, "GraphicsMode", &pThis->u32GraphicsMode, UINT32_MAX);
2327 if (RT_FAILURE(rc))
2328 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2329 N_("Configuration error: Querying \"GraphicsMode\" as a 32-bit int failed"));
2330 if (pThis->u32GraphicsMode == UINT32_MAX)
2331 {
2332 /* get the legacy value if nothing else was specified */
2333 rc = CFGMR3QueryU32Def(pCfg, "GopMode", &pThis->u32GraphicsMode, UINT32_MAX);
2334 if (RT_FAILURE(rc))
2335 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2336 N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed"));
2337 }
2338 if (pThis->u32GraphicsMode == UINT32_MAX)
2339 pThis->u32GraphicsMode = 2; /* 1024x768, at least typically */
2340
2341 /*
2342 * EFI graphics resolution, defaults to 1024x768 (used to be UGA only, now
2343 * is the main config setting as the mode number is so hard to predict).
2344 */
2345 char szResolution[16];
2346 rc = CFGMR3QueryStringDef(pCfg, "GraphicsResolution", szResolution, sizeof(szResolution), "");
2347 if (RT_FAILURE(rc))
2348 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2349 N_("Configuration error: Querying \"GraphicsResolution\" as a string failed"));
2350 if (szResolution[0])
2351 {
2352 const char *pszX = RTStrStr(szResolution, "x");
2353 if (pszX)
2354 {
2355 pThis->u32HorizontalResolution = RTStrToUInt32(szResolution);
2356 pThis->u32VerticalResolution = RTStrToUInt32(pszX + 1);
2357 }
2358 }
2359 else
2360 {
2361 /* get the legacy values if nothing else was specified */
2362 rc = CFGMR3QueryU32Def(pCfg, "UgaHorizontalResolution", &pThis->u32HorizontalResolution, 0);
2363 AssertRCReturn(rc, rc);
2364 rc = CFGMR3QueryU32Def(pCfg, "UgaVerticalResolution", &pThis->u32VerticalResolution, 0);
2365 AssertRCReturn(rc, rc);
2366 }
2367 if (pThis->u32HorizontalResolution == 0 || pThis->u32VerticalResolution == 0)
2368 {
2369 pThis->u32HorizontalResolution = 1024;
2370 pThis->u32VerticalResolution = 768;
2371 }
2372
2373 /*
2374 * Load firmware volume and thunk ROM.
2375 */
2376 rc = efiLoadRom(pThis, pCfg);
2377 if (RT_FAILURE(rc))
2378 return rc;
2379
2380 /*
2381 * Register our I/O ports.
2382 */
2383 rc = PDMDevHlpIOPortRegister(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, NULL,
2384 efiIOPortWrite, efiIOPortRead,
2385 NULL, NULL, "EFI communication ports");
2386 if (RT_FAILURE(rc))
2387 return rc;
2388
2389 /*
2390 * Plant DMI and MPS tables.
2391 */
2392 /** @todo XXX I wonder if we really need these tables as there is no SMBIOS header... */
2393 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThis->aUuid,
2394 pDevIns->pCfg, pThis->cCpus, &pThis->cbDmiTables, &pThis->cNumDmiTables);
2395 AssertRCReturn(rc, rc);
2396 if (pThis->u8IOAPIC)
2397 FwCommonPlantMpsTable(pDevIns,
2398 pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
2399 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
2400 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
2401 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
2402
2403 AssertRCReturn(rc, rc);
2404
2405 /*
2406 * Register info handlers.
2407 */
2408 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "nvram", "Dumps the NVRAM variables.\n", efiInfoNvram);
2409 AssertRCReturn(rc, rc);
2410
2411 /*
2412 * Call reset to set things up.
2413 */
2414 efiReset(pDevIns);
2415
2416 return VINF_SUCCESS;
2417}
2418
2419/**
2420 * The device registration structure.
2421 */
2422const PDMDEVREG g_DeviceEFI =
2423{
2424 /* u32Version */
2425 PDM_DEVREG_VERSION,
2426 /* szName */
2427 "efi",
2428 /* szRCMod */
2429 "",
2430 /* szR0Mod */
2431 "",
2432 /* pszDescription */
2433 "Extensible Firmware Interface Device. "
2434 "LUN#0 - NVRAM port",
2435 /* fFlags */
2436 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
2437 /* fClass */
2438 PDM_DEVREG_CLASS_ARCH_BIOS,
2439 /* cMaxInstances */
2440 1,
2441 /* cbInstance */
2442 sizeof(DEVEFI),
2443 /* pfnConstruct */
2444 efiConstruct,
2445 /* pfnDestruct */
2446 efiDestruct,
2447 /* pfnRelocate */
2448 NULL,
2449 /* pfnMemSetup */
2450 efiMemSetup,
2451 /* pfnPowerOn */
2452 NULL,
2453 /* pfnReset */
2454 efiReset,
2455 /* pfnSuspend */
2456 NULL,
2457 /* pfnResume */
2458 NULL,
2459 /* pfnAttach */
2460 NULL,
2461 /* pfnDetach */
2462 NULL,
2463 /* pfnQueryInterface. */
2464 NULL,
2465 /* pfnInitComplete. */
2466 efiInitComplete,
2467 /* pfnPowerOff */
2468 efiPowerOff,
2469 /* pfnSoftReset */
2470 NULL,
2471 /* u32VersionEnd */
2472 PDM_DEVREG_VERSION
2473};
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