VirtualBox

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

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

DevEFI: warning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.9 KB
Line 
1/* $Id: DevEFI.cpp 48786 2013-10-01 06:46:27Z vboxsync $ */
2/** @file
3 * DevEFI - EFI <-> VirtualBox Integration Framework.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DEV_EFI
22
23#include <VBox/vmm/pdmdev.h>
24#include <VBox/vmm/pgm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/log.h>
27#include <VBox/err.h>
28#include <VBox/param.h>
29#include <VBox/vmm/dbgf.h>
30#include <VBox/vmm/pdmnvram.h>
31
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/ctype.h>
35#include <iprt/file.h>
36#include <iprt/mem.h>
37#include <iprt/string.h>
38#include <iprt/uuid.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41#include <iprt/mp.h>
42#include <iprt/list.h>
43#if defined(DEBUG) && defined(IN_RING3)
44# include <iprt/stream.h>
45# define DEVEFI_WITH_VBOXDBG_SCRIPT
46#endif
47
48#include "DevEFI.h"
49#include "VBoxDD.h"
50#include "VBoxDD2.h"
51#include "../PC/DevFwCommon.h"
52
53/* EFI includes */
54#include <ProcessorBind.h>
55#include <Common/UefiBaseTypes.h>
56#include <Common/PiFirmwareVolume.h>
57#include <Common/PiFirmwareFile.h>
58
59
60/*******************************************************************************
61* Structures and Typedefs *
62*******************************************************************************/
63/**
64 * EFI NVRAM variable.
65 */
66typedef struct EFIVAR
67{
68 /** The list node for the variable. */
69 RTLISTNODE ListNode;
70 /** The unique sequence number of the variable.
71 * This is used to find pCurVar when restoring saved state and therefore only
72 * set when saving. */
73 uint32_t idUniqueSavedState;
74 /** The value attributess. */
75 uint32_t fAttributes;
76 /** The variable name length (not counting the terminator char). */
77 uint32_t cchName;
78 /** The size of the value. This cannot be zero. */
79 uint32_t cbValue;
80 /** The vendor UUID scoping the variable name. */
81 RTUUID uuid;
82 /** The variable name. */
83 char szName[EFI_VARIABLE_NAME_MAX];
84 /** The variable value bytes. */
85 uint8_t abValue[EFI_VARIABLE_VALUE_MAX];
86} EFIVAR;
87/** Pointer to an EFI NVRAM variable. */
88typedef EFIVAR *PEFIVAR;
89/** Pointer to a const EFI NVRAM variable. */
90typedef EFIVAR const *PCEFIVAR;
91/** Pointer to an EFI NVRAM variable pointer. */
92typedef PEFIVAR *PPEFIVAR;
93
94/**
95 * NVRAM state.
96 */
97typedef struct NVRAMDESC
98{
99 /** The current operation. */
100 EFIVAROP enmOp;
101 /** The current status. */
102 uint32_t u32Status;
103 /** The current */
104 uint32_t offOpBuffer;
105 /** The current number of variables. */
106 uint32_t cVariables;
107 /** The list of variables. */
108 RTLISTANCHOR VarList;
109
110 /** The unique variable sequence ID, for the saved state only.
111 * @todo It's part of this structure for hysterical raisins, consider remove it
112 * when changing the saved state format the next time. */
113 uint32_t idUniqueCurVar;
114 /** Variable buffered used both when adding and querying NVRAM variables.
115 * When querying a variable, a copy of it is stored in this buffer and read
116 * from it. When adding, updating or deleting a variable, this buffer is used
117 * to set up the parameters before taking action. */
118 EFIVAR VarOpBuf;
119 /** The current variable. This is only used by EFI_VARIABLE_OP_QUERY_NEXT,
120 * the attribute readers work against the copy in VarOpBuf. */
121 PEFIVAR pCurVar;
122} NVRAMDESC;
123
124
125/**
126 * The EFI device state structure.
127 */
128typedef struct DEVEFI
129{
130 /** Pointer back to the device instance. */
131 PPDMDEVINS pDevIns;
132
133 /** EFI message buffer. */
134 char szMsg[VBOX_EFI_DEBUG_BUFFER];
135 /** EFI message buffer index. */
136 uint32_t iMsg;
137
138 /** EFI panic message buffer. */
139 char szPanicMsg[2048];
140 /** EFI panic message buffer index. */
141 uint32_t iPanicMsg;
142
143 struct
144 {
145 /** The current/last image event. */
146 uint8_t uEvt;
147 /** Module path/name offset. */
148 uint8_t offName;
149 /** The offset of the last component in the module path/name. */
150 uint8_t offNameLastComponent;
151 /** Alignment padding. */
152 uint8_t abPadding[5];
153 /** First address associated with the event (image address). */
154 uint64_t uAddr0;
155 /** Second address associated with the event (old image address). */
156 uint64_t uAddr1;
157 /** The size associated with the event (0 if none). */
158 uint64_t cb0;
159 /** The module name. */
160 char szName[256];
161 } ImageEvt;
162
163 /** The system EFI ROM data. */
164 uint8_t *pu8EfiRom;
165 /** The size of the system EFI ROM. */
166 uint64_t cbEfiRom;
167 /** The name of the EFI ROM file. */
168 char *pszEfiRomFile;
169 /** Thunk page pointer. */
170 uint8_t *pu8EfiThunk;
171 /** First entry point of the EFI firmware. */
172 RTGCPHYS GCEntryPoint0;
173 /** Second Entry Point (PeiCore)*/
174 RTGCPHYS GCEntryPoint1;
175 /** EFI firmware physical load address. */
176 RTGCPHYS GCLoadAddress;
177 /** Current info selector. */
178 uint32_t iInfoSelector;
179 /** Current info position. */
180 int32_t offInfo;
181
182 /** Number of virtual CPUs. (Config) */
183 uint32_t cCpus;
184 /** RAM below 4GB (in bytes). (Config) */
185 uint32_t cbBelow4GB;
186 /** RAM above 4GB (in bytes). (Config) */
187 uint64_t cbAbove4GB;
188 /** The total amount of memory. */
189 uint64_t cbRam;
190 /** The size of the RAM hole below 4GB. */
191 uint64_t cbRamHole;
192
193 /** The size of the DMI tables. */
194 uint16_t cbDmiTables;
195 /** Number of the DMI tables. */
196 uint16_t cNumDmiTables;
197 /** The DMI tables. */
198 uint8_t au8DMIPage[0x1000];
199
200 /** I/O-APIC enabled? */
201 uint8_t u8IOAPIC;
202
203 /** Boot parameters passed to the firmware. */
204 char szBootArgs[256];
205
206 /** Host UUID (for DMI). */
207 RTUUID aUuid;
208
209 /** Device properties buffer. */
210 R3PTRTYPE(uint8_t *) pbDeviceProps;
211 /** Device properties buffer size. */
212 uint32_t cbDeviceProps;
213
214 /** Virtual machine front side bus frequency. */
215 uint64_t u64FsbFrequency;
216 /** Virtual machine time stamp counter frequency. */
217 uint64_t u64TscFrequency;
218 /** Virtual machine CPU frequency. */
219 uint64_t u64CpuFrequency;
220 /** GOP mode. */
221 uint32_t u32GopMode;
222 /** Uga mode horisontal resolution. */
223 uint32_t cxUgaResolution;
224 /** Uga mode vertical resolution. */
225 uint32_t cyUgaResolution;
226
227
228 /** NVRAM state variables. */
229 NVRAMDESC NVRAM;
230
231 /**
232 * NVRAM port - LUN\#0.
233 */
234 struct
235 {
236 /** The base interface we provide the NVRAM driver. */
237 PDMIBASE IBase;
238 /** The NVRAM driver base interface. */
239 PPDMIBASE pDrvBase;
240 /** The NVRAM interface provided by the driver. */
241 PPDMINVRAMCONNECTOR pNvramDrv;
242 } Lun0;
243} DEVEFI;
244typedef DEVEFI *PDEVEFI;
245
246
247/*******************************************************************************
248* Defined Constants And Macros *
249*******************************************************************************/
250/** The saved state version. */
251#define EFI_SSM_VERSION 2
252/** The saved state version from VBox 4.2. */
253#define EFI_SSM_VERSION_4_2 1
254
255/** Non-volatile EFI variable. */
256#define VBOX_EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
257/** Non-volatile EFI variable. */
258#define VBOX_EFI_VARIABLE_READ_ONLY UINT32_C(0x00000008)
259
260
261/*******************************************************************************
262* Global Variables *
263*******************************************************************************/
264/** Saved state NVRAMDESC field descriptors. */
265static SSMFIELD const g_aEfiNvramDescField[] =
266{
267 SSMFIELD_ENTRY( NVRAMDESC, enmOp),
268 SSMFIELD_ENTRY( NVRAMDESC, u32Status),
269 SSMFIELD_ENTRY( NVRAMDESC, offOpBuffer),
270 SSMFIELD_ENTRY_IGNORE(NVRAMDESC, VarOpBuf),
271 SSMFIELD_ENTRY( NVRAMDESC, cVariables),
272 SSMFIELD_ENTRY_OLD( idUnquireLast, 4),
273 SSMFIELD_ENTRY_IGNORE(NVRAMDESC, VarList),
274 SSMFIELD_ENTRY( NVRAMDESC, idUniqueCurVar),
275 SSMFIELD_ENTRY_IGNORE(NVRAMDESC, pCurVar),
276 SSMFIELD_ENTRY_TERM()
277};
278
279/** Saved state EFIVAR field descriptors. */
280static SSMFIELD const g_aEfiVariableDescFields[] =
281{
282 SSMFIELD_ENTRY_IGNORE(EFIVAR, ListNode),
283 SSMFIELD_ENTRY( EFIVAR, idUniqueSavedState),
284 SSMFIELD_ENTRY( EFIVAR, uuid),
285 SSMFIELD_ENTRY( EFIVAR, szName),
286 SSMFIELD_ENTRY_OLD( cchName, 4),
287 SSMFIELD_ENTRY( EFIVAR, abValue),
288 SSMFIELD_ENTRY( EFIVAR, cbValue),
289 SSMFIELD_ENTRY( EFIVAR, fAttributes),
290 SSMFIELD_ENTRY_TERM()
291};
292
293
294
295
296/**
297 * Flushes the variable list.
298 *
299 * @param pThis The EFI state.
300 */
301static void nvramFlushDeviceVariableList(PDEVEFI pThis)
302{
303 while (!RTListIsEmpty(&pThis->NVRAM.VarList))
304 {
305 PEFIVAR pEfiVar = RTListNodeGetNext(&pThis->NVRAM.VarList, EFIVAR, ListNode);
306 RTListNodeRemove(&pEfiVar->ListNode);
307 RTMemFree(pEfiVar);
308 }
309
310 pThis->NVRAM.pCurVar = NULL;
311}
312
313/**
314 * This function looks up variable in NVRAM list.
315 */
316static int nvramLookupVariableByUuidAndName(PDEVEFI pThis, char *pszVariableName, PCRTUUID pUuid, PPEFIVAR ppEfiVar)
317{
318 LogFlowFunc(("%RTuuid::'%s'\n", pUuid, pszVariableName));
319 size_t const cchVariableName = strlen(pszVariableName);
320 int rc = VERR_NOT_FOUND;
321
322 /*
323 * Start by checking the last variable queried.
324 */
325 if ( pThis->NVRAM.pCurVar
326 && pThis->NVRAM.pCurVar->cchName == cchVariableName
327 && memcmp(pThis->NVRAM.pCurVar->szName, pszVariableName, cchVariableName + 1) == 0
328 && RTUuidCompare(&pThis->NVRAM.pCurVar->uuid, pUuid) == 0
329 )
330 {
331 *ppEfiVar = pThis->NVRAM.pCurVar;
332 rc = VINF_SUCCESS;
333 }
334 else
335 {
336 /*
337 * Linear list search.
338 */
339 PEFIVAR pEfiVar;
340 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
341 {
342 Assert(strlen(pEfiVar->szName) == pEfiVar->cchName);
343 if ( pEfiVar->cchName == cchVariableName
344 && memcmp(pEfiVar->szName, pszVariableName, cchVariableName + 1) == 0
345 && RTUuidCompare(&pEfiVar->uuid, pUuid) == 0)
346 {
347 *ppEfiVar = pEfiVar;
348 rc = VINF_SUCCESS;
349 break;
350 }
351 }
352 }
353
354 LogFlowFunc(("rc=%Rrc pEfiVar=%p\n", rc, *ppEfiVar));
355 return rc;
356}
357
358
359/**
360 * Inserts the EFI variable into the list.
361 *
362 * This enforces the desired list ordering and/or insertion policy.
363 *
364 * @param pThis The EFI state.
365 * @param pEfiVar The variable to insert.
366 */
367static void nvramInsertVariable(PDEVEFI pThis, PEFIVAR pEfiVar)
368{
369#if 1
370 /*
371 * Sorted by UUID and name.
372 */
373 PEFIVAR pCurVar;
374 RTListForEach(&pThis->NVRAM.VarList, pCurVar, EFIVAR, ListNode)
375 {
376 int iDiff = RTUuidCompare(&pEfiVar->uuid, &pCurVar->uuid);
377 if (!iDiff)
378 iDiff = strcmp(pEfiVar->szName, pCurVar->szName);
379 if (iDiff < 0)
380 {
381 RTListNodeInsertBefore(&pCurVar->ListNode, &pEfiVar->ListNode);
382 return;
383 }
384 }
385#endif
386
387 /*
388 * Add it at the end.
389 */
390 RTListAppend(&pThis->NVRAM.VarList, &pEfiVar->ListNode);
391}
392
393
394/**
395 * Creates an device internal list of variables.
396 *
397 * @returns VBox status code.
398 * @param pThis The EFI state.
399 */
400static int nvramLoad(PDEVEFI pThis)
401{
402 int rc;
403 for (uint32_t iVar = 0; iVar < EFI_VARIABLE_MAX; iVar++)
404 {
405 PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
406 AssertReturn(pEfiVar, VERR_NO_MEMORY);
407
408 pEfiVar->cchName = sizeof(pEfiVar->szName);
409 pEfiVar->cbValue = sizeof(pEfiVar->abValue);
410 rc = pThis->Lun0.pNvramDrv->pfnVarQueryByIndex(pThis->Lun0.pNvramDrv, iVar,
411 &pEfiVar->uuid, &pEfiVar->szName[0], &pEfiVar->cchName,
412 &pEfiVar->fAttributes, &pEfiVar->abValue[0], &pEfiVar->cbValue);
413 if (RT_SUCCESS(rc))
414 {
415 /* Some validations. */
416 rc = RTStrValidateEncoding(pEfiVar->szName);
417 size_t cchName = RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
418 if (cchName != pEfiVar->cchName)
419 rc = VERR_INVALID_PARAMETER;
420 if (pEfiVar->cbValue == 0)
421 rc = VERR_NO_DATA;
422 if (RT_FAILURE(rc))
423 LogRel(("EFI/nvramLoad: Bad variable #%u: cbValue=%#x cchName=%#x (strlen=%#x) szName=%.*Rhxs\n",
424 pEfiVar->cbValue, pEfiVar->cchName, cchName, pEfiVar->cchName + 1, pEfiVar->szName));
425 }
426 if (RT_FAILURE(rc))
427 {
428 RTMemFree(pEfiVar);
429 if (rc == VERR_NOT_FOUND)
430 rc = VINF_SUCCESS;
431 AssertRC(rc);
432 return rc;
433 }
434
435 /* Append it. */
436 nvramInsertVariable(pThis, pEfiVar);
437 pThis->NVRAM.cVariables++;
438 }
439
440 AssertLogRelMsgFailed(("EFI: Too many variables.\n"));
441 return VERR_TOO_MUCH_DATA;
442}
443
444
445/**
446 * Let the NVRAM driver store the internal NVRAM variable list.
447 *
448 * @returns VBox status code.
449 * @param pThis The EFI state.
450 */
451static int nvramStore(PDEVEFI pThis)
452{
453 /*
454 * Count the non-volatile variables and issue the begin call.
455 */
456 PEFIVAR pEfiVar;
457 uint32_t cNonVolatile = 0;
458 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
459 if (pEfiVar->fAttributes & VBOX_EFI_VARIABLE_NON_VOLATILE)
460 cNonVolatile++;
461 int rc = pThis->Lun0.pNvramDrv->pfnVarStoreSeqBegin(pThis->Lun0.pNvramDrv, cNonVolatile);
462 if (RT_SUCCESS(rc))
463 {
464 /*
465 * Store each non-volatile variable.
466 */
467 uint32_t idxVar = 0;
468 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
469 {
470 /* Skip volatile variables. */
471 if (!(pEfiVar->fAttributes & VBOX_EFI_VARIABLE_NON_VOLATILE))
472 continue;
473
474 int rc2 = pThis->Lun0.pNvramDrv->pfnVarStoreSeqPut(pThis->Lun0.pNvramDrv, idxVar,
475 &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cchName,
476 pEfiVar->fAttributes, pEfiVar->abValue, pEfiVar->cbValue);
477 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rc))
478 {
479 LogRel(("EFI: pfnVarStoreVarByIndex failed: %Rrc\n", rc));
480 rc = rc2;
481 }
482 idxVar++;
483 }
484 Assert(idxVar == cNonVolatile);
485
486 /*
487 * Done.
488 */
489 rc = pThis->Lun0.pNvramDrv->pfnVarStoreSeqEnd(pThis->Lun0.pNvramDrv, rc);
490 }
491 else
492 LogRel(("EFI: pfnVarStoreBegin failed: %Rrc\n", rc));
493 return rc;
494}
495
496/**
497 * EFI_VARIABLE_OP_QUERY and EFI_VARIABLE_OP_QUERY_NEXT worker that copies the
498 * variable into the VarOpBuf, set pCurVar and u32Status.
499 *
500 * @param pThis The EFI state.
501 * @param pEfiVar The resulting variable. NULL if not found / end.
502 * @param fEnumQuery Set if enumeration query, clear if specific.
503 */
504static void nvramWriteVariableOpQueryCopyResult(PDEVEFI pThis, PEFIVAR pEfiVar, bool fEnumQuery)
505{
506 RT_ZERO(pThis->NVRAM.VarOpBuf.abValue);
507 if (pEfiVar)
508 {
509 RT_ZERO(pThis->NVRAM.VarOpBuf.szName);
510 pThis->NVRAM.VarOpBuf.uuid = pEfiVar->uuid;
511 pThis->NVRAM.VarOpBuf.cchName = pEfiVar->cchName;
512 memcpy(pThis->NVRAM.VarOpBuf.szName, pEfiVar->szName, pEfiVar->cchName); /* no need for + 1. */
513 pThis->NVRAM.VarOpBuf.fAttributes = pEfiVar->fAttributes;
514 pThis->NVRAM.VarOpBuf.cbValue = pEfiVar->cbValue;
515 memcpy(pThis->NVRAM.VarOpBuf.abValue, pEfiVar->abValue, pEfiVar->cbValue);
516 pThis->NVRAM.pCurVar = pEfiVar;
517 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
518 LogFlow(("EFI: Variable query -> %RTuuid::'%s' (%d) abValue=%.*Rhxs\n", &pThis->NVRAM.VarOpBuf.uuid,
519 pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.cchName,
520 pThis->NVRAM.VarOpBuf.cbValue, pThis->NVRAM.VarOpBuf.abValue));
521 }
522 else
523 {
524 if (fEnumQuery)
525 LogFlow(("EFI: Variable query -> NOT_FOUND \n"));
526 else
527 LogFlow(("EFI: Variable query %RTuuid::'%s' -> NOT_FOUND \n",
528 &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName));
529 RT_ZERO(pThis->NVRAM.VarOpBuf.szName);
530 pThis->NVRAM.VarOpBuf.fAttributes = 0;
531 pThis->NVRAM.VarOpBuf.cbValue = 0;
532 pThis->NVRAM.VarOpBuf.cchName = 0;
533 pThis->NVRAM.pCurVar = NULL;
534 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_NOT_FOUND;
535 }
536}
537
538/**
539 * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_QUERY.
540 *
541 * @returns IOM strict status code.
542 * @param pThis The EFI state.
543 */
544static int nvramWriteVariableOpQuery(PDEVEFI pThis)
545{
546 Log(("EFI_VARIABLE_OP_QUERY: %RTuuid::'%s'\n", &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName));
547
548 PEFIVAR pEfiVar;
549 int rc = nvramLookupVariableByUuidAndName(pThis,
550 pThis->NVRAM.VarOpBuf.szName,
551 &pThis->NVRAM.VarOpBuf.uuid,
552 &pEfiVar);
553 nvramWriteVariableOpQueryCopyResult(pThis, RT_SUCCESS(rc) ? pEfiVar : NULL, false /*fEnumQuery*/);
554 return VINF_SUCCESS;
555}
556
557/**
558 * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_QUERY_NEXT.
559 *
560 * This simply walks the list.
561 *
562 * @returns IOM strict status code.
563 * @param pThis The EFI state.
564 */
565static int nvramWriteVariableOpQueryNext(PDEVEFI pThis)
566{
567 Log(("EFI_VARIABLE_OP_QUERY_NEXT: pCurVar=%p\n", pThis->NVRAM.pCurVar));
568 PEFIVAR pEfiVar = pThis->NVRAM.pCurVar;
569 if (pEfiVar)
570 pEfiVar = RTListGetNext(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode);
571 else
572 pEfiVar = RTListGetFirst(&pThis->NVRAM.VarList, EFIVAR, ListNode);
573 nvramWriteVariableOpQueryCopyResult(pThis, pEfiVar, true /* fEnumQuery */);
574 return VINF_SUCCESS;
575}
576
577/**
578 * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_ADD.
579 *
580 * @returns IOM strict status code.
581 * @param pThis The EFI state.
582 */
583static int nvramWriteVariableOpAdd(PDEVEFI pThis)
584{
585 Log(("EFI_VARIABLE_OP_ADD: %RTuuid::'%s' fAttributes=%#x abValue=%.*Rhxs\n",
586 &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes,
587 pThis->NVRAM.VarOpBuf.cbValue, pThis->NVRAM.VarOpBuf.abValue));
588
589 /*
590 * Validate and adjust the input a little before we start.
591 */
592 int rc = RTStrValidateEncoding(pThis->NVRAM.VarOpBuf.szName);
593 if (RT_FAILURE(rc))
594 LogRel(("EFI: Badly encoded variable name: %.*Rhxs\n", pThis->NVRAM.VarOpBuf.cchName + 1, pThis->NVRAM.VarOpBuf.szName));
595 if (RT_FAILURE(rc))
596 {
597 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
598 return VINF_SUCCESS;
599 }
600 pThis->NVRAM.VarOpBuf.cchName = (uint32_t)RTStrNLen(pThis->NVRAM.VarOpBuf.szName, sizeof(pThis->NVRAM.VarOpBuf.szName));
601
602 /*
603 * Look it up and see what to do.
604 */
605 PEFIVAR pEfiVar;
606 rc = nvramLookupVariableByUuidAndName(pThis,
607 pThis->NVRAM.VarOpBuf.szName,
608 &pThis->NVRAM.VarOpBuf.uuid,
609 &pEfiVar);
610 if (RT_SUCCESS(rc))
611 {
612 LogFlowFunc(("Old abValue=%.*Rhxs\n", pEfiVar->cbValue, pEfiVar->abValue));
613#if 0 /** @todo Implement read-only EFI variables. */
614 if (pEfiVar->fAttributes & EFI_VARIABLE_XXXXXXX)
615 {
616 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_RO;
617 break;
618 }
619#endif
620
621 if (pThis->NVRAM.VarOpBuf.cbValue == 0)
622 {
623 /*
624 * Delete it.
625 */
626 LogRel(("EFI: Deleting variable %RTuuid::'%s'\n", &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName));
627 RTListNodeRemove(&pEfiVar->ListNode);
628 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
629 pThis->NVRAM.cVariables--;
630
631 if (pThis->NVRAM.pCurVar == pEfiVar)
632 pThis->NVRAM.pCurVar = NULL;
633 RTMemFree(pEfiVar);
634 pEfiVar = NULL;
635 }
636 else
637 {
638 /*
639 * Update/replace it. (The name and UUID are unchanged, of course.)
640 */
641 LogRel(("EFI: Replacing variable %RTuuid::'%s' fAttrib=%#x cbValue=%#x\n", &pThis->NVRAM.VarOpBuf.uuid,
642 pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes, pThis->NVRAM.VarOpBuf.cbValue));
643 pEfiVar->fAttributes = pThis->NVRAM.VarOpBuf.fAttributes;
644 pEfiVar->cbValue = pThis->NVRAM.VarOpBuf.cbValue;
645 memcpy(pEfiVar->abValue, pThis->NVRAM.VarOpBuf.abValue, pEfiVar->cbValue);
646 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
647 }
648 }
649 else if (pThis->NVRAM.VarOpBuf.cbValue == 0)
650 {
651 /* delete operation, but nothing to delete. */
652 LogFlow(("nvramWriteVariableOpAdd: Delete (not found)\n"));
653 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
654 }
655 else if (pThis->NVRAM.cVariables < EFI_VARIABLE_MAX)
656 {
657 /*
658 * Add a new variable.
659 */
660 LogRel(("EFI: Adding variable %RTuuid::'%s' fAttrib=%#x cbValue=%#x\n", &pThis->NVRAM.VarOpBuf.uuid,
661 pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes, pThis->NVRAM.VarOpBuf.cbValue));
662 pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
663 if (pEfiVar)
664 {
665 pEfiVar->uuid = pThis->NVRAM.VarOpBuf.uuid;
666 pEfiVar->cchName = pThis->NVRAM.VarOpBuf.cchName;
667 memcpy(pEfiVar->szName, pThis->NVRAM.VarOpBuf.szName, pEfiVar->cchName); /* The buffer is zeroed, so skip '\0'. */
668 pEfiVar->fAttributes = pThis->NVRAM.VarOpBuf.fAttributes;
669 pEfiVar->cbValue = pThis->NVRAM.VarOpBuf.cbValue;
670 memcpy(pEfiVar->abValue, pThis->NVRAM.VarOpBuf.abValue, pEfiVar->cbValue);
671
672 nvramInsertVariable(pThis, pEfiVar);
673 pThis->NVRAM.cVariables++;
674 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
675 }
676 else
677 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
678 }
679 else
680 {
681 /*
682 * Too many variables.
683 */
684 static unsigned s_cWarnings = 0;
685 if (s_cWarnings++ < 5)
686 LogRel(("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_HORISONTAL_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_HORISONTAL_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));
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 static uint64_t s_cDbgPointLogged = 0;
1531 if (s_cDbgPointLogged < 1024)
1532 {
1533 s_cDbgPointLogged++;
1534 LogRel(("EFI: debug point %s\n", efiDbgPointName((EFIDBGPOINT)u32)));
1535 }
1536 rc = VINF_SUCCESS;
1537 }
1538 else
1539 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Invalid debug point %#x\n", u32);
1540 break;
1541#else
1542 return VINF_IOM_R3_IOPORT_WRITE;
1543#endif
1544
1545 case EFI_PORT_IMAGE_EVENT:
1546 rc = efiPortImageEventWrite(pThis, u32, cb);
1547 break;
1548
1549 default:
1550 Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", Port, u32, cb));
1551 break;
1552 }
1553 return rc;
1554}
1555
1556static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1557{
1558 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1559 LogFlow(("efiSaveExec:\n"));
1560
1561 /*
1562 * Set variables only used when saving state.
1563 */
1564 uint32_t idUniqueSavedState = 0;
1565 PEFIVAR pEfiVar;
1566 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
1567 {
1568 pEfiVar->idUniqueSavedState = idUniqueSavedState++;
1569 }
1570 Assert(idUniqueSavedState == pThis->NVRAM.cVariables);
1571
1572 pThis->NVRAM.idUniqueCurVar = pThis->NVRAM.pCurVar
1573 ? pThis->NVRAM.pCurVar->idUniqueSavedState
1574 : UINT32_MAX;
1575
1576 /*
1577 * Save the NVRAM state.
1578 */
1579 SSMR3PutStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
1580 SSMR3PutStructEx(pSSM, &pThis->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1581
1582 /*
1583 * Save the list variables (we saved the length above).
1584 */
1585 RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
1586 {
1587 SSMR3PutStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1588 }
1589
1590 return VINF_SUCCESS; /* SSM knows */
1591}
1592
1593static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1594{
1595 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1596 LogFlow(("efiLoadExec: uVersion=%d uPass=%d\n", uVersion, uPass));
1597
1598 /*
1599 * Validate input.
1600 */
1601 if (uPass != SSM_PASS_FINAL)
1602 return VERR_SSM_UNEXPECTED_PASS;
1603 if ( uVersion != EFI_SSM_VERSION
1604 && uVersion != EFI_SSM_VERSION_4_2
1605 )
1606 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1607
1608 /*
1609 * Kill the current variables before loading anything.
1610 */
1611 nvramFlushDeviceVariableList(pThis);
1612
1613 /*
1614 * Load the NVRAM state.
1615 */
1616 int rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
1617 AssertRCReturn(rc, rc);
1618 pThis->NVRAM.pCurVar = NULL;
1619
1620 rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1621 AssertRCReturn(rc, rc);
1622
1623 /*
1624 * Load variables.
1625 */
1626 pThis->NVRAM.pCurVar = NULL;
1627 Assert(RTListIsEmpty(&pThis->NVRAM.VarList));
1628 RTListInit(&pThis->NVRAM.VarList);
1629 for (uint32_t i = 0; i < pThis->NVRAM.cVariables; i++)
1630 {
1631 PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
1632 AssertPtrReturn(pEfiVar, VERR_NO_MEMORY);
1633
1634 rc = SSMR3GetStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
1635 if (RT_SUCCESS(rc))
1636 {
1637 if ( pEfiVar->cbValue > sizeof(pEfiVar->abValue)
1638 || pEfiVar->cbValue == 0)
1639 {
1640 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1641 LogRel(("EFI: Loaded invalid variable value length %#x\n", pEfiVar->cbValue));
1642 }
1643 uint32_t cchVarName = (uint32_t)RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
1644 if (cchVarName >= sizeof(pEfiVar->szName))
1645 {
1646 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1647 LogRel(("EFI: Loaded variable name is unterminated.\n"));
1648 }
1649 if (pEfiVar->cchName > cchVarName) /* No check for 0 here, busted load code in 4.2, so now storing 0 here. */
1650 {
1651 rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1652 LogRel(("EFI: Loaded invalid variable name length %#x (cchVarName=%#x)\n", pEfiVar->cchName, cchVarName));
1653 }
1654 if (RT_SUCCESS(rc))
1655 pEfiVar->cchName = cchVarName;
1656 }
1657 AssertRCReturnStmt(rc, RTMemFree(pEfiVar), rc);
1658
1659 /* Add it (not using nvramInsertVariable to preserve saved order),
1660 updating the current variable pointer while we're here. */
1661#if 1
1662 RTListAppend(&pThis->NVRAM.VarList, &pEfiVar->ListNode);
1663#else
1664 nvramInsertVariable(pThis, pEfiVar);
1665#endif
1666 if (pThis->NVRAM.idUniqueCurVar == pEfiVar->idUniqueSavedState)
1667 pThis->NVRAM.pCurVar = pEfiVar;
1668 }
1669
1670 return VINF_SUCCESS;
1671}
1672
1673
1674/**
1675 * @copydoc(PDMIBASE::pfnQueryInterface)
1676 */
1677static DECLCALLBACK(void *) devEfiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1678{
1679 LogFlowFunc(("ENTER: pIBase: %p, pszIID:%p\n", __FUNCTION__, pInterface, pszIID));
1680 PDEVEFI pThis = RT_FROM_MEMBER(pInterface, DEVEFI, Lun0.IBase);
1681
1682 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase);
1683 return NULL;
1684}
1685
1686
1687/**
1688 * Write to CMOS memory.
1689 * This is used by the init complete code.
1690 */
1691static void cmosWrite(PPDMDEVINS pDevIns, unsigned off, uint32_t u32Val)
1692{
1693 Assert(off < 128);
1694 Assert(u32Val < 256);
1695
1696 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
1697 AssertRC(rc);
1698}
1699
1700/**
1701 * Init complete notification.
1702 *
1703 * @returns VBOX status code.
1704 * @param pDevIns The device instance.
1705 */
1706static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
1707{
1708 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1709
1710 /*
1711 * Memory sizes.
1712 */
1713 uint64_t const offRamHole = _4G - pThis->cbRamHole;
1714 uint32_t u32;
1715 if (pThis->cbRam > 16 * _1M)
1716 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
1717 else
1718 u32 = 0;
1719 cmosWrite(pDevIns, 0x34, u32 & 0xff);
1720 cmosWrite(pDevIns, 0x35, u32 >> 8);
1721
1722 /*
1723 * Number of CPUs.
1724 */
1725 cmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
1726
1727 return VINF_SUCCESS;
1728}
1729
1730
1731/**
1732 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1733 */
1734static DECLCALLBACK(void) efiMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1735{
1736 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1737
1738 /*
1739 * Plan some structures in RAM.
1740 */
1741 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->cbDmiTables, pThis->cNumDmiTables);
1742 if (pThis->u8IOAPIC)
1743 FwCommonPlantMpsFloatPtr(pDevIns);
1744
1745 /*
1746 * Re-shadow the Firmware Volume and make it RAM/RAM.
1747 */
1748 uint32_t cPages = RT_ALIGN_64(pThis->cbEfiRom, PAGE_SIZE) >> PAGE_SHIFT;
1749 RTGCPHYS GCPhys = pThis->GCLoadAddress;
1750 while (cPages > 0)
1751 {
1752 uint8_t abPage[PAGE_SIZE];
1753
1754 /* Read the (original) ROM page and write it back to the RAM page. */
1755 int rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1756 AssertLogRelRC(rc);
1757
1758 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
1759 AssertLogRelRC(rc);
1760 if (RT_FAILURE(rc))
1761 memset(abPage, 0xcc, sizeof(abPage));
1762
1763 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
1764 AssertLogRelRC(rc);
1765
1766 /* Switch to the RAM/RAM mode. */
1767 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1768 AssertLogRelRC(rc);
1769
1770 /* Advance */
1771 GCPhys += PAGE_SIZE;
1772 cPages--;
1773 }
1774}
1775
1776
1777/**
1778 * @interface_method_impl{PDMDEVREG,pfnReset}
1779 */
1780static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
1781{
1782 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1783
1784 LogFlow(("efiReset\n"));
1785
1786 pThis->iInfoSelector = 0;
1787 pThis->offInfo = -1;
1788
1789 pThis->iMsg = 0;
1790 pThis->szMsg[0] = '\0';
1791 pThis->iPanicMsg = 0;
1792 pThis->szPanicMsg[0] = '\0';
1793
1794#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
1795 /*
1796 * Zap the debugger script
1797 */
1798 RTFileDelete("./DevEFI.VBoxDbg");
1799#endif
1800}
1801
1802
1803/**
1804 * Destruct a device instance.
1805 *
1806 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1807 * resources can be freed correctly.
1808 *
1809 * @param pDevIns The device instance data.
1810 */
1811static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
1812{
1813 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1814 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1815
1816 if (pThis->Lun0.pNvramDrv)
1817 nvramStore(pThis);
1818 nvramFlushDeviceVariableList(pThis);
1819
1820 if (pThis->pu8EfiRom)
1821 {
1822 RTFileReadAllFree(pThis->pu8EfiRom, (size_t)pThis->cbEfiRom);
1823 pThis->pu8EfiRom = NULL;
1824 }
1825
1826 /*
1827 * Free MM heap pointers (waste of time, but whatever).
1828 */
1829 if (pThis->pszEfiRomFile)
1830 {
1831 MMR3HeapFree(pThis->pszEfiRomFile);
1832 pThis->pszEfiRomFile = NULL;
1833 }
1834
1835 if (pThis->pu8EfiThunk)
1836 {
1837 MMR3HeapFree(pThis->pu8EfiThunk);
1838 pThis->pu8EfiThunk = NULL;
1839 }
1840
1841 if (pThis->pbDeviceProps)
1842 {
1843 MMR3HeapFree(pThis->pbDeviceProps);
1844 pThis->pbDeviceProps = NULL;
1845 pThis->cbDeviceProps = 0;
1846 }
1847
1848 return VINF_SUCCESS;
1849}
1850
1851/**
1852 * Helper that searches for a FFS file of a given type.
1853 *
1854 * @returns Pointer to the FFS file header if found, NULL if not.
1855 *
1856 * @param pFfsFile Pointer to the FFS file header to start searching at.
1857 * @param pbEnd The end of the firmware volume.
1858 * @param FileType The file type to look for.
1859 * @param pcbFfsFile Where to store the FFS file size (includes header).
1860 */
1861DECLINLINE(EFI_FFS_FILE_HEADER const *)
1862efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
1863{
1864#define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
1865 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
1866 {
1867 if (pFfsFile->Type == FileType)
1868 {
1869 *pcbFile = FFS_SIZE(pFfsFile);
1870 LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType));
1871 return pFfsFile;
1872 }
1873 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
1874 }
1875#undef FFS_SIZE
1876 return NULL;
1877}
1878
1879
1880/**
1881 * Parse EFI ROM headers and find entry points.
1882 *
1883 * @returns VBox status.
1884 * @param pThis The device instance data.
1885 */
1886static int efiParseFirmware(PDEVEFI pThis)
1887{
1888 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThis->pu8EfiRom;
1889
1890 /*
1891 * Validate firmware volume header.
1892 */
1893 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
1894 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
1895 VERR_INVALID_MAGIC);
1896 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
1897 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
1898 VERR_VERSION_MISMATCH);
1899 /** @todo check checksum, see PE spec vol. 3 */
1900 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThis->cbEfiRom,
1901 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThis->cbEfiRom),
1902 VERR_INVALID_PARAMETER);
1903 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
1904 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
1905 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
1906 VERR_INVALID_PARAMETER);
1907
1908 AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
1909
1910 uint8_t const * const pbFwVolEnd = pThis->pu8EfiRom + pFwVolHdr->FvLength;
1911 pThis->GCLoadAddress = UINT32_C(0xfffff000) - pThis->cbEfiRom + PAGE_SIZE;
1912
1913 return VINF_SUCCESS;
1914}
1915
1916/**
1917 * Load EFI ROM file into the memory.
1918 *
1919 * @returns VBox status.
1920 * @param pThis The device instance data.
1921 * @param pCfg Configuration node handle for the device.
1922 */
1923static int efiLoadRom(PDEVEFI pThis, PCFGMNODE pCfg)
1924{
1925 /*
1926 * Read the entire firmware volume into memory.
1927 */
1928 void *pvFile;
1929 size_t cbFile;
1930 int rc = RTFileReadAllEx(pThis->pszEfiRomFile,
1931 0 /*off*/,
1932 RTFOFF_MAX /*cbMax*/,
1933 RTFILE_RDALL_O_DENY_WRITE,
1934 &pvFile,
1935 &cbFile);
1936 if (RT_FAILURE(rc))
1937 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
1938 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
1939 pThis->pszEfiRomFile, rc);
1940 pThis->pu8EfiRom = (uint8_t *)pvFile;
1941 pThis->cbEfiRom = cbFile;
1942
1943 /*
1944 * Validate firmware volume and figure out the load address as well as the SEC entry point.
1945 */
1946 rc = efiParseFirmware(pThis);
1947 if (RT_FAILURE(rc))
1948 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
1949 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
1950 pThis->pszEfiRomFile, rc);
1951
1952 /*
1953 * Map the firmware volume into memory as shadowed ROM.
1954 */
1955 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
1956 RTGCPHYS cbQuart = RT_ALIGN_64(pThis->cbEfiRom / 4, PAGE_SIZE);
1957 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1958 pThis->GCLoadAddress,
1959 cbQuart,
1960 pThis->pu8EfiRom,
1961 cbQuart,
1962 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1963 "EFI Firmware Volume");
1964 AssertRCReturn(rc, rc);
1965 rc = PDMDevHlpROMProtectShadow(pThis->pDevIns, pThis->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
1966 AssertRCReturn(rc, rc);
1967 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1968 pThis->GCLoadAddress + cbQuart,
1969 cbQuart,
1970 pThis->pu8EfiRom + cbQuart,
1971 cbQuart,
1972 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1973 "EFI Firmware Volume (Part 2)");
1974 if (RT_FAILURE(rc))
1975 return rc;
1976 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1977 pThis->GCLoadAddress + cbQuart * 2,
1978 cbQuart,
1979 pThis->pu8EfiRom + cbQuart * 2,
1980 cbQuart,
1981 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1982 "EFI Firmware Volume (Part 3)");
1983 if (RT_FAILURE(rc))
1984 return rc;
1985 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1986 pThis->GCLoadAddress + cbQuart * 3,
1987 pThis->cbEfiRom - cbQuart * 3,
1988 pThis->pu8EfiRom + cbQuart * 3,
1989 pThis->cbEfiRom - cbQuart * 3,
1990 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1991 "EFI Firmware Volume (Part 4)");
1992 if (RT_FAILURE(rc))
1993 return rc;
1994 return VINF_SUCCESS;
1995}
1996
1997static uint8_t efiGetHalfByte(char ch)
1998{
1999 uint8_t val;
2000
2001 if (ch >= '0' && ch <= '9')
2002 val = ch - '0';
2003 else if (ch >= 'A' && ch <= 'F')
2004 val = ch - 'A' + 10;
2005 else if(ch >= 'a' && ch <= 'f')
2006 val = ch - 'a' + 10;
2007 else
2008 val = 0xff;
2009
2010 return val;
2011
2012}
2013
2014
2015/**
2016 * Converts a hex string into a binary data blob located at
2017 * pThis->pbDeviceProps, size returned as pThis->cbDeviceProps.
2018 *
2019 * @returns VERR_NO_MEMORY or VINF_SUCCESS.
2020 * @param pThis The EFI instance data.
2021 * @param pszDeviceProps The device property hex string to decode.
2022 */
2023static int efiParseDeviceString(PDEVEFI pThis, const char *pszDeviceProps)
2024{
2025 uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
2026 pThis->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThis->pDevIns, cbOut);
2027 if (!pThis->pbDeviceProps)
2028 return VERR_NO_MEMORY;
2029
2030 uint32_t iHex = 0;
2031 bool fUpper = true;
2032 uint8_t u8Value = 0; /* (shut up gcc) */
2033 for (uint32_t iStr = 0; pszDeviceProps[iStr]; iStr++)
2034 {
2035 uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]);
2036 if (u8Hb > 0xf)
2037 continue;
2038
2039 if (fUpper)
2040 u8Value = u8Hb << 4;
2041 else
2042 pThis->pbDeviceProps[iHex++] = u8Hb | u8Value;
2043
2044 Assert(iHex < cbOut);
2045 fUpper = !fUpper;
2046 }
2047
2048 Assert(iHex == 0 || fUpper);
2049 pThis->cbDeviceProps = iHex;
2050
2051 return VINF_SUCCESS;
2052}
2053
2054
2055/**
2056 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2057 */
2058static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2059{
2060 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
2061 int rc;
2062 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2063
2064 Assert(iInstance == 0);
2065
2066 /*
2067 * Initalize the basic variables so that the destructor always works.
2068 */
2069 pThis->pDevIns = pDevIns;
2070 RTListInit(&pThis->NVRAM.VarList);
2071 pThis->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface;
2072
2073
2074 /*
2075 * Validate and read the configuration.
2076 */
2077 if (!CFGMR3AreValuesValid(pCfg,
2078 "EfiRom\0"
2079 "RamSize\0"
2080 "RamHoleSize\0"
2081 "NumCPUs\0"
2082 "UUID\0"
2083 "IOAPIC\0"
2084 "DmiBIOSFirmwareMajor\0"
2085 "DmiBIOSFirmwareMinor\0"
2086 "DmiBIOSReleaseDate\0"
2087 "DmiBIOSReleaseMajor\0"
2088 "DmiBIOSReleaseMinor\0"
2089 "DmiBIOSVendor\0"
2090 "DmiBIOSVersion\0"
2091 "DmiSystemFamily\0"
2092 "DmiSystemProduct\0"
2093 "DmiSystemSerial\0"
2094 "DmiSystemSKU\0"
2095 "DmiSystemUuid\0"
2096 "DmiSystemVendor\0"
2097 "DmiSystemVersion\0"
2098 "DmiBoardAssetTag\0"
2099 "DmiBoardBoardType\0"
2100 "DmiBoardLocInChass\0"
2101 "DmiBoardProduct\0"
2102 "DmiBoardSerial\0"
2103 "DmiBoardVendor\0"
2104 "DmiBoardVersion\0"
2105 "DmiChassisAssetTag\0"
2106 "DmiChassisSerial\0"
2107 "DmiChassisType\0"
2108 "DmiChassisVendor\0"
2109 "DmiChassisVersion\0"
2110 "DmiProcManufacturer\0"
2111 "DmiProcVersion\0"
2112 "DmiOEMVBoxVer\0"
2113 "DmiOEMVBoxRev\0"
2114 "DmiUseHostInfo\0"
2115 "DmiExposeMemoryTable\0"
2116 "DmiExposeProcInf\0"
2117 "64BitEntry\0"
2118 "BootArgs\0"
2119 "DeviceProps\0"
2120 "GopMode\0"
2121 "UgaHorizontalResolution\0"
2122 "UgaVerticalResolution\0"))
2123 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2124 N_("Configuration error: Invalid config value(s) for the EFI device"));
2125
2126 /* CPU count (optional). */
2127 rc = CFGMR3QueryU32Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
2128 AssertLogRelRCReturn(rc, rc);
2129
2130 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
2131 if (RT_FAILURE (rc))
2132 return PDMDEV_SET_ERROR(pDevIns, rc,
2133 N_("Configuration error: Failed to read \"IOAPIC\""));
2134
2135 /*
2136 * Query the machine's UUID for SMBIOS/DMI use.
2137 */
2138 RTUUID uuid;
2139 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
2140 if (RT_FAILURE(rc))
2141 return PDMDEV_SET_ERROR(pDevIns, rc,
2142 N_("Configuration error: Querying \"UUID\" failed"));
2143
2144 /*
2145 * Convert the UUID to network byte order. Not entirely straightforward as
2146 * parts are MSB already...
2147 */
2148 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
2149 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
2150 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
2151 memcpy(&pThis->aUuid, &uuid, sizeof pThis->aUuid);
2152
2153 /*
2154 * RAM sizes
2155 */
2156 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
2157 AssertLogRelRCReturn(rc, rc);
2158 rc = CFGMR3QueryU64(pCfg, "RamHoleSize", &pThis->cbRamHole);
2159 AssertLogRelRCReturn(rc, rc);
2160 pThis->cbBelow4GB = RT_MIN(pThis->cbRam, _4G - pThis->cbRamHole);
2161 pThis->cbAbove4GB = pThis->cbRam - pThis->cbBelow4GB;
2162
2163 /*
2164 * Get the system EFI ROM file name.
2165 */
2166 rc = CFGMR3QueryStringAlloc(pCfg, "EfiRom", &pThis->pszEfiRomFile);
2167 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2168 {
2169 pThis->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
2170 if (!pThis->pszEfiRomFile)
2171 return VERR_NO_MEMORY;
2172
2173 rc = RTPathAppPrivateArchTop(pThis->pszEfiRomFile, RTPATH_MAX);
2174 AssertRCReturn(rc, rc);
2175 rc = RTPathAppend(pThis->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd");
2176 AssertRCReturn(rc, rc);
2177 }
2178 else if (RT_FAILURE(rc))
2179 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2180 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
2181 else if (!*pThis->pszEfiRomFile)
2182 {
2183 MMR3HeapFree(pThis->pszEfiRomFile);
2184 pThis->pszEfiRomFile = NULL;
2185 }
2186
2187 /*
2188 * NVRAM processing.
2189 */
2190 rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThis), efiSaveExec, efiLoadExec);
2191 AssertRCReturn(rc, rc);
2192
2193 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "NvramStorage");
2194 if (RT_FAILURE(rc))
2195 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
2196
2197 pThis->Lun0.pNvramDrv = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pDrvBase, PDMINVRAMCONNECTOR);
2198 AssertPtrReturn(pThis->Lun0.pNvramDrv, VERR_PDM_MISSING_INTERFACE_BELOW);
2199
2200 rc = nvramLoad(pThis);
2201 AssertRCReturn(rc, rc);
2202
2203 /*
2204 * Get boot args.
2205 */
2206 rc = CFGMR3QueryStringDef(pCfg, "BootArgs", pThis->szBootArgs, sizeof(pThis->szBootArgs), "");
2207 if (RT_FAILURE(rc))
2208 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2209 N_("Configuration error: Querying \"BootArgs\" as a string failed"));
2210
2211 //strcpy(pThis->szBootArgs, "-v keepsyms=1 io=0xf");
2212 LogRel(("EFI: boot args = %s\n", pThis->szBootArgs));
2213
2214 /*
2215 * Get device props.
2216 */
2217 char *pszDeviceProps;
2218 rc = CFGMR3QueryStringAllocDef(pCfg, "DeviceProps", &pszDeviceProps, NULL);
2219 if (RT_FAILURE(rc))
2220 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2221 N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
2222 if (pszDeviceProps)
2223 {
2224 LogRel(("EFI: device props = %s\n", pszDeviceProps));
2225 rc = efiParseDeviceString(pThis, pszDeviceProps);
2226 MMR3HeapFree(pszDeviceProps);
2227 if (RT_FAILURE(rc))
2228 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2229 N_("Configuration error: Cannot parse device properties"));
2230 }
2231 else
2232 {
2233 pThis->pbDeviceProps = NULL;
2234 pThis->cbDeviceProps = 0;
2235 }
2236
2237 /*
2238 * CPU frequencies.
2239 */
2240 pThis->u64TscFrequency = TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
2241 /* Multiplier is read from MSR_IA32_PERF_STATUS, and now is hardcoded as 4. */
2242 pThis->u64FsbFrequency = pThis->u64TscFrequency / 4;
2243 pThis->u64CpuFrequency = pThis->u64TscFrequency;
2244
2245 /*
2246 * GOP graphics.
2247 */
2248 rc = CFGMR3QueryU32Def(pCfg, "GopMode", &pThis->u32GopMode, 2 /* 1024x768 */);
2249 if (RT_FAILURE(rc))
2250 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2251 N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed"));
2252 if (pThis->u32GopMode == UINT32_MAX)
2253 pThis->u32GopMode = 2; /* 1024x768 */
2254
2255 /*
2256 * Uga graphics, default to 1024x768.
2257 */
2258 rc = CFGMR3QueryU32Def(pCfg, "UgaHorizontalResolution", &pThis->cxUgaResolution, 0);
2259 AssertRCReturn(rc, rc);
2260 rc = CFGMR3QueryU32Def(pCfg, "UgaVerticalResolution", &pThis->cyUgaResolution, 0);
2261 AssertRCReturn(rc, rc);
2262 if (pThis->cxUgaResolution == 0 || pThis->cyUgaResolution == 0)
2263 {
2264 pThis->cxUgaResolution = 1024;
2265 pThis->cyUgaResolution = 768;
2266 }
2267
2268 /*
2269 * Load firmware volume and thunk ROM.
2270 */
2271 rc = efiLoadRom(pThis, pCfg);
2272 if (RT_FAILURE(rc))
2273 return rc;
2274
2275 /*
2276 * Register our I/O ports.
2277 */
2278 rc = PDMDevHlpIOPortRegister(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, NULL,
2279 efiIOPortWrite, efiIOPortRead,
2280 NULL, NULL, "EFI communication ports");
2281 if (RT_FAILURE(rc))
2282 return rc;
2283
2284 /*
2285 * Plant DMI and MPS tables.
2286 */
2287 /** @todo XXX I wonder if we really need these tables as there is no SMBIOS header... */
2288 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThis->aUuid,
2289 pDevIns->pCfg, pThis->cCpus, &pThis->cbDmiTables, &pThis->cNumDmiTables);
2290 AssertRCReturn(rc, rc);
2291 if (pThis->u8IOAPIC)
2292 FwCommonPlantMpsTable(pDevIns,
2293 pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
2294 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
2295 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
2296 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
2297
2298 AssertRCReturn(rc, rc);
2299
2300 /*
2301 * Register info handlers.
2302 */
2303 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "nvram", "Dumps the NVRAM variables.\n", efiInfoNvram);
2304 AssertRCReturn(rc, rc);
2305
2306 /*
2307 * Call reset to set things up.
2308 */
2309 efiReset(pDevIns);
2310
2311 return VINF_SUCCESS;
2312}
2313
2314/**
2315 * The device registration structure.
2316 */
2317const PDMDEVREG g_DeviceEFI =
2318{
2319 /* u32Version */
2320 PDM_DEVREG_VERSION,
2321 /* szName */
2322 "efi",
2323 /* szRCMod */
2324 "",
2325 /* szR0Mod */
2326 "",
2327 /* pszDescription */
2328 "Extensible Firmware Interface Device. "
2329 "LUN#0 - NVRAM port",
2330 /* fFlags */
2331 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
2332 /* fClass */
2333 PDM_DEVREG_CLASS_ARCH_BIOS,
2334 /* cMaxInstances */
2335 1,
2336 /* cbInstance */
2337 sizeof(DEVEFI),
2338 /* pfnConstruct */
2339 efiConstruct,
2340 /* pfnDestruct */
2341 efiDestruct,
2342 /* pfnRelocate */
2343 NULL,
2344 /* pfnMemSetup */
2345 efiMemSetup,
2346 /* pfnPowerOn */
2347 NULL,
2348 /* pfnReset */
2349 efiReset,
2350 /* pfnSuspend */
2351 NULL,
2352 /* pfnResume */
2353 NULL,
2354 /* pfnAttach */
2355 NULL,
2356 /* pfnDetach */
2357 NULL,
2358 /* pfnQueryInterface. */
2359 NULL,
2360 /* pfnInitComplete. */
2361 efiInitComplete,
2362 /* pfnPowerOff */
2363 NULL,
2364 /* pfnSoftReset */
2365 NULL,
2366 /* u32VersionEnd */
2367 PDM_DEVREG_VERSION
2368};
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