VirtualBox

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

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

BUGZ:6633 Expose OEM specific DMI table which contains TSC frequency value in kHz units provided by Time Manager; calculate number of DMI tables dynamically (no VBOX_DMI_TABLE_ENTR macro anymore).

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