VirtualBox

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

Last change on this file since 80924 was 80704, checked in by vboxsync, 5 years ago

PDM,Devices: Changed PDM_DEVREG_FLAGS_MSI_X into a registration field giving the max MSI-X vector count config for the device (typically VBOX_MSIX_MAX_ENTRIES). bugref:9218

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