VirtualBox

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

Last change on this file since 65532 was 63478, checked in by vboxsync, 8 years ago

Devices: warnings (clang)

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