VirtualBox

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

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

efiDestruct fix.

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