VirtualBox

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

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

Some fixes and instrumentation

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