VirtualBox

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

Last change on this file since 50584 was 50584, checked in by vboxsync, 11 years ago

CPUM,DevEFI: Bus vs cpu clock ratio fixes for more recent CPUs. Older CPUs (<=Core2) still need some more work.

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