VirtualBox

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

Last change on this file since 60404 was 58170, checked in by vboxsync, 9 years ago

doxygen: fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.9 KB
Line 
1/* $Id: DevEFI.cpp 58170 2015-10-12 09:27:14Z 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 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
1812 */
1813static DECLCALLBACK(void) efiPowerOff(PPDMDEVINS pDevIns)
1814{
1815 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1816
1817 if (pThis->Lun0.pNvramDrv)
1818 nvramStore(pThis);
1819}
1820
1821
1822
1823/**
1824 * Destruct a device instance.
1825 *
1826 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1827 * resources can be freed correctly.
1828 *
1829 * @param pDevIns The device instance data.
1830 */
1831static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
1832{
1833 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1834 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1835
1836 nvramFlushDeviceVariableList(pThis);
1837
1838 if (pThis->pu8EfiRom)
1839 {
1840 RTFileReadAllFree(pThis->pu8EfiRom, (size_t)pThis->cbEfiRom);
1841 pThis->pu8EfiRom = NULL;
1842 }
1843
1844 /*
1845 * Free MM heap pointers (waste of time, but whatever).
1846 */
1847 if (pThis->pszEfiRomFile)
1848 {
1849 MMR3HeapFree(pThis->pszEfiRomFile);
1850 pThis->pszEfiRomFile = NULL;
1851 }
1852
1853 if (pThis->pu8EfiThunk)
1854 {
1855 MMR3HeapFree(pThis->pu8EfiThunk);
1856 pThis->pu8EfiThunk = NULL;
1857 }
1858
1859 if (pThis->pbDeviceProps)
1860 {
1861 MMR3HeapFree(pThis->pbDeviceProps);
1862 pThis->pbDeviceProps = NULL;
1863 pThis->cbDeviceProps = 0;
1864 }
1865
1866 return VINF_SUCCESS;
1867}
1868
1869/**
1870 * Helper that searches for a FFS file of a given type.
1871 *
1872 * @returns Pointer to the FFS file header if found, NULL if not.
1873 *
1874 * @param pFfsFile Pointer to the FFS file header to start searching at.
1875 * @param pbEnd The end of the firmware volume.
1876 * @param FileType The file type to look for.
1877 * @param pcbFfsFile Where to store the FFS file size (includes header).
1878 */
1879DECLINLINE(EFI_FFS_FILE_HEADER const *)
1880efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
1881{
1882#define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
1883 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
1884 {
1885 if (pFfsFile->Type == FileType)
1886 {
1887 *pcbFile = FFS_SIZE(pFfsFile);
1888 LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType));
1889 return pFfsFile;
1890 }
1891 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
1892 }
1893#undef FFS_SIZE
1894 return NULL;
1895}
1896
1897
1898/**
1899 * Parse EFI ROM headers and find entry points.
1900 *
1901 * @returns VBox status code.
1902 * @param pThis The device instance data.
1903 */
1904static int efiParseFirmware(PDEVEFI pThis)
1905{
1906 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThis->pu8EfiRom;
1907
1908 /*
1909 * Validate firmware volume header.
1910 */
1911 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
1912 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
1913 VERR_INVALID_MAGIC);
1914 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
1915 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
1916 VERR_VERSION_MISMATCH);
1917 /** @todo check checksum, see PE spec vol. 3 */
1918 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThis->cbEfiRom,
1919 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThis->cbEfiRom),
1920 VERR_INVALID_PARAMETER);
1921 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
1922 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
1923 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
1924 VERR_INVALID_PARAMETER);
1925
1926 AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
1927
1928 uint8_t const * const pbFwVolEnd = pThis->pu8EfiRom + pFwVolHdr->FvLength;
1929 pThis->GCLoadAddress = UINT32_C(0xfffff000) - pThis->cbEfiRom + PAGE_SIZE;
1930
1931 return VINF_SUCCESS;
1932}
1933
1934/**
1935 * Load EFI ROM file into the memory.
1936 *
1937 * @returns VBox status code.
1938 * @param pThis The device instance data.
1939 * @param pCfg Configuration node handle for the device.
1940 */
1941static int efiLoadRom(PDEVEFI pThis, PCFGMNODE pCfg)
1942{
1943 /*
1944 * Read the entire firmware volume into memory.
1945 */
1946 void *pvFile;
1947 size_t cbFile;
1948 int rc = RTFileReadAllEx(pThis->pszEfiRomFile,
1949 0 /*off*/,
1950 RTFOFF_MAX /*cbMax*/,
1951 RTFILE_RDALL_O_DENY_WRITE,
1952 &pvFile,
1953 &cbFile);
1954 if (RT_FAILURE(rc))
1955 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
1956 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
1957 pThis->pszEfiRomFile, rc);
1958 pThis->pu8EfiRom = (uint8_t *)pvFile;
1959 pThis->cbEfiRom = cbFile;
1960
1961 /*
1962 * Validate firmware volume and figure out the load address as well as the SEC entry point.
1963 */
1964 rc = efiParseFirmware(pThis);
1965 if (RT_FAILURE(rc))
1966 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
1967 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
1968 pThis->pszEfiRomFile, rc);
1969
1970 /*
1971 * Map the firmware volume into memory as shadowed ROM.
1972 */
1973 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
1974 RTGCPHYS cbQuart = RT_ALIGN_64(pThis->cbEfiRom / 4, PAGE_SIZE);
1975 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1976 pThis->GCLoadAddress,
1977 cbQuart,
1978 pThis->pu8EfiRom,
1979 cbQuart,
1980 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1981 "EFI Firmware Volume");
1982 AssertRCReturn(rc, rc);
1983 rc = PDMDevHlpROMProtectShadow(pThis->pDevIns, pThis->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
1984 AssertRCReturn(rc, rc);
1985 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1986 pThis->GCLoadAddress + cbQuart,
1987 cbQuart,
1988 pThis->pu8EfiRom + cbQuart,
1989 cbQuart,
1990 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1991 "EFI Firmware Volume (Part 2)");
1992 if (RT_FAILURE(rc))
1993 return rc;
1994 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1995 pThis->GCLoadAddress + cbQuart * 2,
1996 cbQuart,
1997 pThis->pu8EfiRom + cbQuart * 2,
1998 cbQuart,
1999 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2000 "EFI Firmware Volume (Part 3)");
2001 if (RT_FAILURE(rc))
2002 return rc;
2003 rc = PDMDevHlpROMRegister(pThis->pDevIns,
2004 pThis->GCLoadAddress + cbQuart * 3,
2005 pThis->cbEfiRom - cbQuart * 3,
2006 pThis->pu8EfiRom + cbQuart * 3,
2007 pThis->cbEfiRom - cbQuart * 3,
2008 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
2009 "EFI Firmware Volume (Part 4)");
2010 if (RT_FAILURE(rc))
2011 return rc;
2012 return VINF_SUCCESS;
2013}
2014
2015static uint8_t efiGetHalfByte(char ch)
2016{
2017 uint8_t val;
2018
2019 if (ch >= '0' && ch <= '9')
2020 val = ch - '0';
2021 else if (ch >= 'A' && ch <= 'F')
2022 val = ch - 'A' + 10;
2023 else if(ch >= 'a' && ch <= 'f')
2024 val = ch - 'a' + 10;
2025 else
2026 val = 0xff;
2027
2028 return val;
2029
2030}
2031
2032
2033/**
2034 * Converts a hex string into a binary data blob located at
2035 * pThis->pbDeviceProps, size returned as pThis->cbDeviceProps.
2036 *
2037 * @returns VERR_NO_MEMORY or VINF_SUCCESS.
2038 * @param pThis The EFI instance data.
2039 * @param pszDeviceProps The device property hex string to decode.
2040 */
2041static int efiParseDeviceString(PDEVEFI pThis, const char *pszDeviceProps)
2042{
2043 uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
2044 pThis->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThis->pDevIns, cbOut);
2045 if (!pThis->pbDeviceProps)
2046 return VERR_NO_MEMORY;
2047
2048 uint32_t iHex = 0;
2049 bool fUpper = true;
2050 uint8_t u8Value = 0; /* (shut up gcc) */
2051 for (uint32_t iStr = 0; pszDeviceProps[iStr]; iStr++)
2052 {
2053 uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]);
2054 if (u8Hb > 0xf)
2055 continue;
2056
2057 if (fUpper)
2058 u8Value = u8Hb << 4;
2059 else
2060 pThis->pbDeviceProps[iHex++] = u8Hb | u8Value;
2061
2062 Assert(iHex < cbOut);
2063 fUpper = !fUpper;
2064 }
2065
2066 Assert(iHex == 0 || fUpper);
2067 pThis->cbDeviceProps = iHex;
2068
2069 return VINF_SUCCESS;
2070}
2071
2072
2073/**
2074 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2075 */
2076static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2077{
2078 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
2079 int rc;
2080 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2081
2082 Assert(iInstance == 0);
2083
2084 /*
2085 * Initalize the basic variables so that the destructor always works.
2086 */
2087 pThis->pDevIns = pDevIns;
2088 RTListInit(&pThis->NVRAM.VarList);
2089 pThis->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface;
2090
2091
2092 /*
2093 * Validate and read the configuration.
2094 */
2095 if (!CFGMR3AreValuesValid(pCfg,
2096 "EfiRom\0"
2097 "RamSize\0"
2098 "RamHoleSize\0"
2099 "NumCPUs\0"
2100 "UUID\0"
2101 "IOAPIC\0"
2102 "DmiBIOSFirmwareMajor\0"
2103 "DmiBIOSFirmwareMinor\0"
2104 "DmiBIOSReleaseDate\0"
2105 "DmiBIOSReleaseMajor\0"
2106 "DmiBIOSReleaseMinor\0"
2107 "DmiBIOSVendor\0"
2108 "DmiBIOSVersion\0"
2109 "DmiSystemFamily\0"
2110 "DmiSystemProduct\0"
2111 "DmiSystemSerial\0"
2112 "DmiSystemSKU\0"
2113 "DmiSystemUuid\0"
2114 "DmiSystemVendor\0"
2115 "DmiSystemVersion\0"
2116 "DmiBoardAssetTag\0"
2117 "DmiBoardBoardType\0"
2118 "DmiBoardLocInChass\0"
2119 "DmiBoardProduct\0"
2120 "DmiBoardSerial\0"
2121 "DmiBoardVendor\0"
2122 "DmiBoardVersion\0"
2123 "DmiChassisAssetTag\0"
2124 "DmiChassisSerial\0"
2125 "DmiChassisType\0"
2126 "DmiChassisVendor\0"
2127 "DmiChassisVersion\0"
2128 "DmiProcManufacturer\0"
2129 "DmiProcVersion\0"
2130 "DmiOEMVBoxVer\0"
2131 "DmiOEMVBoxRev\0"
2132 "DmiUseHostInfo\0"
2133 "DmiExposeMemoryTable\0"
2134 "DmiExposeProcInf\0"
2135 "64BitEntry\0"
2136 "BootArgs\0"
2137 "DeviceProps\0"
2138 "GopMode\0"
2139 "UgaHorizontalResolution\0"
2140 "UgaVerticalResolution\0"))
2141 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2142 N_("Configuration error: Invalid config value(s) for the EFI device"));
2143
2144 /* CPU count (optional). */
2145 rc = CFGMR3QueryU32Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
2146 AssertLogRelRCReturn(rc, rc);
2147
2148 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
2149 if (RT_FAILURE (rc))
2150 return PDMDEV_SET_ERROR(pDevIns, rc,
2151 N_("Configuration error: Failed to read \"IOAPIC\""));
2152
2153 /*
2154 * Query the machine's UUID for SMBIOS/DMI use.
2155 */
2156 RTUUID uuid;
2157 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
2158 if (RT_FAILURE(rc))
2159 return PDMDEV_SET_ERROR(pDevIns, rc,
2160 N_("Configuration error: Querying \"UUID\" failed"));
2161
2162 /*
2163 * Convert the UUID to network byte order. Not entirely straightforward as
2164 * parts are MSB already...
2165 */
2166 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
2167 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
2168 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
2169 memcpy(&pThis->aUuid, &uuid, sizeof pThis->aUuid);
2170
2171 /*
2172 * RAM sizes
2173 */
2174 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
2175 AssertLogRelRCReturn(rc, rc);
2176 rc = CFGMR3QueryU64(pCfg, "RamHoleSize", &pThis->cbRamHole);
2177 AssertLogRelRCReturn(rc, rc);
2178 pThis->cbBelow4GB = RT_MIN(pThis->cbRam, _4G - pThis->cbRamHole);
2179 pThis->cbAbove4GB = pThis->cbRam - pThis->cbBelow4GB;
2180
2181 /*
2182 * Get the system EFI ROM file name.
2183 */
2184 rc = CFGMR3QueryStringAlloc(pCfg, "EfiRom", &pThis->pszEfiRomFile);
2185 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2186 {
2187 pThis->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
2188 if (!pThis->pszEfiRomFile)
2189 return VERR_NO_MEMORY;
2190
2191 rc = RTPathAppPrivateArchTop(pThis->pszEfiRomFile, RTPATH_MAX);
2192 AssertRCReturn(rc, rc);
2193 rc = RTPathAppend(pThis->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd");
2194 AssertRCReturn(rc, rc);
2195 }
2196 else if (RT_FAILURE(rc))
2197 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2198 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
2199 else if (!*pThis->pszEfiRomFile)
2200 {
2201 MMR3HeapFree(pThis->pszEfiRomFile);
2202 pThis->pszEfiRomFile = NULL;
2203 }
2204
2205 /*
2206 * NVRAM processing.
2207 */
2208 rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThis), efiSaveExec, efiLoadExec);
2209 AssertRCReturn(rc, rc);
2210
2211 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "NvramStorage");
2212 if (RT_FAILURE(rc))
2213 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
2214
2215 pThis->Lun0.pNvramDrv = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pDrvBase, PDMINVRAMCONNECTOR);
2216 AssertPtrReturn(pThis->Lun0.pNvramDrv, VERR_PDM_MISSING_INTERFACE_BELOW);
2217
2218 rc = nvramLoad(pThis);
2219 AssertRCReturn(rc, rc);
2220
2221 /*
2222 * Get boot args.
2223 */
2224 rc = CFGMR3QueryStringDef(pCfg, "BootArgs", pThis->szBootArgs, sizeof(pThis->szBootArgs), "");
2225 if (RT_FAILURE(rc))
2226 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2227 N_("Configuration error: Querying \"BootArgs\" as a string failed"));
2228
2229 //strcpy(pThis->szBootArgs, "-v keepsyms=1 io=0xf debug=0x2a");
2230 //strcpy(pThis->szBootArgs, "-v keepsyms=1 debug=0x2a");
2231 LogRel(("EFI: boot args = %s\n", pThis->szBootArgs));
2232
2233 /*
2234 * Get device props.
2235 */
2236 char *pszDeviceProps;
2237 rc = CFGMR3QueryStringAllocDef(pCfg, "DeviceProps", &pszDeviceProps, NULL);
2238 if (RT_FAILURE(rc))
2239 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2240 N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
2241 if (pszDeviceProps)
2242 {
2243 LogRel(("EFI: device props = %s\n", pszDeviceProps));
2244 rc = efiParseDeviceString(pThis, pszDeviceProps);
2245 MMR3HeapFree(pszDeviceProps);
2246 if (RT_FAILURE(rc))
2247 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2248 N_("Configuration error: Cannot parse device properties"));
2249 }
2250 else
2251 {
2252 pThis->pbDeviceProps = NULL;
2253 pThis->cbDeviceProps = 0;
2254 }
2255
2256 /*
2257 * CPU frequencies.
2258 */
2259 pThis->u64TscFrequency = TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
2260 pThis->u64CpuFrequency = pThis->u64TscFrequency;
2261 pThis->u64FsbFrequency = CPUMGetGuestScalableBusFrequency(PDMDevHlpGetVM(pDevIns));
2262
2263 /*
2264 * GOP graphics.
2265 */
2266 rc = CFGMR3QueryU32Def(pCfg, "GopMode", &pThis->u32GopMode, 2 /* 1024x768 */);
2267 if (RT_FAILURE(rc))
2268 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2269 N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed"));
2270 if (pThis->u32GopMode == UINT32_MAX)
2271 pThis->u32GopMode = 2; /* 1024x768 */
2272
2273 /*
2274 * Uga graphics, default to 1024x768.
2275 */
2276 rc = CFGMR3QueryU32Def(pCfg, "UgaHorizontalResolution", &pThis->cxUgaResolution, 0);
2277 AssertRCReturn(rc, rc);
2278 rc = CFGMR3QueryU32Def(pCfg, "UgaVerticalResolution", &pThis->cyUgaResolution, 0);
2279 AssertRCReturn(rc, rc);
2280 if (pThis->cxUgaResolution == 0 || pThis->cyUgaResolution == 0)
2281 {
2282 pThis->cxUgaResolution = 1024;
2283 pThis->cyUgaResolution = 768;
2284 }
2285
2286 /*
2287 * Load firmware volume and thunk ROM.
2288 */
2289 rc = efiLoadRom(pThis, pCfg);
2290 if (RT_FAILURE(rc))
2291 return rc;
2292
2293 /*
2294 * Register our I/O ports.
2295 */
2296 rc = PDMDevHlpIOPortRegister(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, NULL,
2297 efiIOPortWrite, efiIOPortRead,
2298 NULL, NULL, "EFI communication ports");
2299 if (RT_FAILURE(rc))
2300 return rc;
2301
2302 /*
2303 * Plant DMI and MPS tables.
2304 */
2305 /** @todo XXX I wonder if we really need these tables as there is no SMBIOS header... */
2306 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThis->aUuid,
2307 pDevIns->pCfg, pThis->cCpus, &pThis->cbDmiTables, &pThis->cNumDmiTables);
2308 AssertRCReturn(rc, rc);
2309 if (pThis->u8IOAPIC)
2310 FwCommonPlantMpsTable(pDevIns,
2311 pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
2312 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
2313 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
2314 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
2315
2316 AssertRCReturn(rc, rc);
2317
2318 /*
2319 * Register info handlers.
2320 */
2321 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "nvram", "Dumps the NVRAM variables.\n", efiInfoNvram);
2322 AssertRCReturn(rc, rc);
2323
2324 /*
2325 * Call reset to set things up.
2326 */
2327 efiReset(pDevIns);
2328
2329 return VINF_SUCCESS;
2330}
2331
2332/**
2333 * The device registration structure.
2334 */
2335const PDMDEVREG g_DeviceEFI =
2336{
2337 /* u32Version */
2338 PDM_DEVREG_VERSION,
2339 /* szName */
2340 "efi",
2341 /* szRCMod */
2342 "",
2343 /* szR0Mod */
2344 "",
2345 /* pszDescription */
2346 "Extensible Firmware Interface Device. "
2347 "LUN#0 - NVRAM port",
2348 /* fFlags */
2349 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
2350 /* fClass */
2351 PDM_DEVREG_CLASS_ARCH_BIOS,
2352 /* cMaxInstances */
2353 1,
2354 /* cbInstance */
2355 sizeof(DEVEFI),
2356 /* pfnConstruct */
2357 efiConstruct,
2358 /* pfnDestruct */
2359 efiDestruct,
2360 /* pfnRelocate */
2361 NULL,
2362 /* pfnMemSetup */
2363 efiMemSetup,
2364 /* pfnPowerOn */
2365 NULL,
2366 /* pfnReset */
2367 efiReset,
2368 /* pfnSuspend */
2369 NULL,
2370 /* pfnResume */
2371 NULL,
2372 /* pfnAttach */
2373 NULL,
2374 /* pfnDetach */
2375 NULL,
2376 /* pfnQueryInterface. */
2377 NULL,
2378 /* pfnInitComplete. */
2379 efiInitComplete,
2380 /* pfnPowerOff */
2381 efiPowerOff,
2382 /* pfnSoftReset */
2383 NULL,
2384 /* u32VersionEnd */
2385 PDM_DEVREG_VERSION
2386};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use