VirtualBox

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

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

DevEFI,Nvram.cpp: Cleaning up. (partly tested)

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