VirtualBox

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

Last change on this file since 62506 was 61621, checked in by vboxsync, 8 years ago

DevEFI: Display the value of BugCheck* variables to get blue screen details in the log.

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