VirtualBox

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

Last change on this file since 44621 was 44621, checked in by vboxsync, 12 years ago

DevEFI.cpp: Fixed assertion in nvramStore (forgot it yesterday).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette