VirtualBox

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

Last change on this file since 57398 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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