VirtualBox

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

Last change on this file since 61541 was 61042, checked in by vboxsync, 9 years ago

Main/Machine+BIOSSettings: introduce APIC/X2APIC CPU feature settings and a BIOS setting what should be used.
Frontends/VBoxManage: corresponding changes to allow making the settings changes
Devices/BIOS+EFI: placeholder for the BIOS setting part, which isn't implemented yet

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