VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/DevOVMF.cpp@ 44516

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

BIOS/EFI: fixed broken DMI information

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.6 KB
Line 
1/* $Id: DevOVMF.cpp 43712 2012-10-23 14:02:24Z vboxsync $ */
2/** @file
3 * DevEFI - EFI <-> VirtualBox Integration Framework.
4 */
5
6/*
7 * Copyright (C) 2012 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#define VBOX_WITH_OVMF
48#include "Firmware2/VBoxPkg/Include/DevEFI.h"
49#include "VBoxDD.h"
50#include "VBoxDD2.h"
51#include "../PC/DevFwCommon.h"
52
53/* EFI includes */
54#include <ProcessorBind.h>
55#include <Common/UefiBaseTypes.h>
56#include <Common/PiFirmwareVolume.h>
57#include <Common/PiFirmwareFile.h>
58
59#define EFI_SSM_VERSION 1
60/*******************************************************************************
61* Structures and Typedefs *
62*******************************************************************************/
63typedef struct {
64 RTLISTNODE List;
65 int idxVariable;
66 RTUUID uuid;
67 char szVariableName[EFI_VARIABLE_NAME_MAX];
68 uint32_t cbVariableName;
69 uint8_t au8Value[EFI_VARIABLE_VALUE_MAX];
70 uint32_t cbValue;
71 uint32_t u32Attribute;
72} EFIVAR, *PEFIVAR;
73
74typedef PEFIVAR *PPEFIVAR;
75
76typedef struct {
77 EFIVAROP enmOp;
78 uint32_t u32Status;
79 uint32_t idxOpBuffer;
80 EFIVAR OperationVarOp;
81 int cNvramVariables;
82 int iNvramLastIndex;
83 EFIVAR NvramVariableList;
84 int idxCurrentVar;
85 PEFIVAR pCurrentVarOp;
86} NVRAMDESC;
87
88typedef struct DEVEFI
89{
90 /** Pointer back to the device instance. */
91 PPDMDEVINS pDevIns;
92 /** EFI message buffer. */
93 char szMsg[VBOX_EFI_DEBUG_BUFFER];
94 /** EFI message buffer index. */
95 uint32_t iMsg;
96 /** EFI panic message buffer. */
97 char szPanicMsg[2048];
98 /** EFI panic message buffer index. */
99 uint32_t iPanicMsg;
100 /** The system EFI ROM data. */
101 uint8_t *pu8EfiRom;
102 /** The size of the system EFI ROM. */
103 uint64_t cbEfiRom;
104 /** The name of the EFI ROM file. */
105 char *pszEfiRomFile;
106 /** Thunk page pointer. */
107 uint8_t *pu8EfiThunk;
108 /** First entry point of the EFI firmware */
109 RTGCPHYS GCEntryPoint0;
110 /* Second Entry Point (PeiCore)*/
111 RTGCPHYS GCEntryPoint1;
112 /** EFI firmware physical load address */
113 RTGCPHYS GCLoadAddress;
114 /** Current info selector */
115 uint32_t iInfoSelector;
116 /** Current info position */
117 int32_t iInfoPosition;
118
119 /** Number of virtual CPUs. (Config) */
120 uint32_t cCpus;
121 /** RAM below 4GB (in bytes). (Config) */
122 uint32_t cbBelow4GB;
123 /** RAM above 4GB (in bytes). (Config) */
124 uint64_t cbAbove4GB;
125
126 uint64_t cbRam;
127
128 uint64_t cbRamHole;
129
130 /** The size of the DMI tables */
131 uint16_t cbDmiTables;
132 /** The DMI tables. */
133 uint8_t au8DMIPage[0x1000];
134
135 /** I/O-APIC enabled? */
136 uint8_t u8IOAPIC;
137
138 /* Boot parameters passed to the firmware */
139 char szBootArgs[256];
140
141 /* Host UUID (for DMI) */
142 RTUUID aUuid;
143
144 /* Device properties buffer */
145 uint8_t* pu8DeviceProps;
146 /* Device properties buffer size */
147 uint32_t u32DevicePropsLen;
148
149 /* Virtual machine front side bus frequency */
150 uint64_t u64FsbFrequency;
151 /* Virtual machine time stamp counter frequency */
152 uint64_t u64TscFrequency;
153 /* Virtual machine CPU frequency */
154 uint64_t u64CpuFrequency;
155 /* GOP mode */
156 uint32_t u32GopMode;
157 /* Uga mode resolutions */
158 uint32_t u32UgaHorisontal;
159 uint32_t u32UgaVertical;
160 NVRAMDESC NVRAM;
161 struct
162 {
163 PPDMIBASE pDrvBase;
164 PDMIBASE IBase;
165 PPDMINVRAM pNvramDown;
166 } Lun0;
167} DEVEFI;
168typedef DEVEFI *PDEVEFI;
169
170static SSMFIELD const g_aEfiNvramDescField[] =
171{
172 SSMFIELD_ENTRY (NVRAMDESC, enmOp),
173 SSMFIELD_ENTRY (NVRAMDESC, u32Status),
174 SSMFIELD_ENTRY (NVRAMDESC, idxOpBuffer),
175 SSMFIELD_ENTRY_IGNORE (NVRAMDESC, OperationVarOp),
176 SSMFIELD_ENTRY (NVRAMDESC, cNvramVariables),
177 SSMFIELD_ENTRY (NVRAMDESC, iNvramLastIndex),
178 SSMFIELD_ENTRY_IGNORE (NVRAMDESC, NvramVariableList),
179 SSMFIELD_ENTRY (NVRAMDESC, idxCurrentVar),
180 SSMFIELD_ENTRY_IGNORE (NVRAMDESC, pCurrentVarOp),
181 SSMFIELD_ENTRY_TERM()
182};
183
184static SSMFIELD const g_aEfiVariableDescFields[] =
185{
186 SSMFIELD_ENTRY_IGNORE (EFIVAR, List),
187 SSMFIELD_ENTRY (EFIVAR, idxVariable),
188 SSMFIELD_ENTRY (EFIVAR, uuid),
189 SSMFIELD_ENTRY (EFIVAR, szVariableName),
190 SSMFIELD_ENTRY (EFIVAR, cbVariableName),
191 SSMFIELD_ENTRY (EFIVAR, au8Value),
192 SSMFIELD_ENTRY (EFIVAR, cbValue),
193 SSMFIELD_ENTRY (EFIVAR, u32Attribute),
194 SSMFIELD_ENTRY_TERM()
195};
196
197/**
198 * Write to CMOS memory.
199 * This is used by the init complete code.
200 */
201static void cmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
202{
203 Assert(off < 128);
204 Assert(u32Val < 256);
205
206 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
207 AssertRC(rc);
208}
209
210DECLINLINE(void) nvramFlushDeviceVariableList(PDEVEFI pThis)
211{
212 PEFIVAR pEfiVar = NULL;
213 while (!RTListIsEmpty(&pThis->NVRAM.NvramVariableList.List))
214 {
215 pEfiVar = RTListNodeGetNext(&pThis->NVRAM.NvramVariableList.List, EFIVAR, List);
216 RTListNodeRemove(&pEfiVar->List);
217 RTMemFree(pEfiVar);
218 }
219}
220
221/**
222 * This function looks up variable in NVRAM list
223 */
224static int nvramLookupVariableByUuidAndName(PDEVEFI pThis, char *pszVariableName, PCRTUUID pUuid, PPEFIVAR ppEfiVar)
225{
226 int rc = VERR_NOT_FOUND;
227 PEFIVAR pEfiVar = NULL;
228 LogFlowFunc(("pszVariableName:%s, pUuid:%RTuuid\n", pszVariableName, pUuid));
229 int idxVar = 0;
230 RTListForEach((PRTLISTNODE)&pThis->NVRAM.NvramVariableList.List, pEfiVar, EFIVAR, List)
231 {
232 LogFlowFunc(("pEfiVar:%p\n", pEfiVar));
233 idxVar++;
234 if ( pEfiVar
235 && RTUuidCompare(pUuid, &pEfiVar->uuid) == 0
236 && RTStrCmp(pszVariableName, pEfiVar->szVariableName) == 0)
237 {
238 *ppEfiVar = pEfiVar;
239 rc = VINF_SUCCESS;
240 break;
241 }
242 }
243 Assert(pThis->NVRAM.cNvramVariables >= idxVar);
244 LogFlowFuncLeaveRC(rc);
245 return rc;
246}
247
248static int nvramLoad(PDEVEFI pThis)
249{
250 int rc = VINF_SUCCESS;
251 PEFIVAR pEfiVar = NULL;
252 int idxValue = 0;
253 while(idxValue < 100)
254 {
255 pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
256 if (!pEfiVar)
257 {
258 LogRel(("EFI: Can't allocate space for stored EFI variable\n"));
259 return VERR_NO_MEMORY;
260 }
261 pEfiVar->cbVariableName = EFI_VARIABLE_NAME_MAX;
262 pEfiVar->cbValue = EFI_VARIABLE_VALUE_MAX;
263 rc = pThis->Lun0.pNvramDown->pfnLoadNvramValue(pThis->Lun0.pNvramDown,
264 idxValue,
265 &pEfiVar->uuid,
266 pEfiVar->szVariableName,
267 (size_t *)&pEfiVar->cbVariableName,
268 pEfiVar->au8Value,
269 (size_t *)&pEfiVar->cbValue);
270 idxValue++;
271 if (RT_FAILURE(rc))
272 {
273 RTMemFree(pEfiVar);
274 break;
275 }
276 pThis->NVRAM.cNvramVariables++;
277 RTListAppend((PRTLISTNODE)&pThis->NVRAM.NvramVariableList.List, &pEfiVar->List);
278 }
279 if ( RT_FAILURE(rc)
280 && rc == VERR_NOT_FOUND)
281 rc = VINF_SUCCESS;
282 AssertRCReturn(rc, rc);
283 return rc;
284}
285
286static int nvramStore(PDEVEFI pThis)
287{
288 int rc = VINF_SUCCESS;
289 PEFIVAR pEfiVar = NULL;
290 int idxVar = 0;
291 pThis->Lun0.pNvramDown->pfnFlushNvramStorage(pThis->Lun0.pNvramDown);
292
293 RTListForEach((PRTLISTNODE)&pThis->NVRAM.NvramVariableList.List, pEfiVar, EFIVAR, List)
294 {
295 pThis->Lun0.pNvramDown->pfnStoreNvramValue(pThis->Lun0.pNvramDown,
296 idxVar,
297 &pEfiVar->uuid,
298 pEfiVar->szVariableName,
299 pEfiVar->cbVariableName,
300 pEfiVar->au8Value,
301 pEfiVar->cbValue);
302 idxVar++;
303 }
304 Assert((pThis->NVRAM.cNvramVariables == idxVar));
305 return VINF_SUCCESS;
306}
307
308static uint32_t efiInfoSize(PDEVEFI pThis)
309{
310 switch (pThis->iInfoSelector)
311 {
312 case EFI_INFO_INDEX_VOLUME_BASE:
313 case EFI_INFO_INDEX_VOLUME_SIZE:
314 case EFI_INFO_INDEX_TEMPMEM_BASE:
315 case EFI_INFO_INDEX_TEMPMEM_SIZE:
316 case EFI_INFO_INDEX_STACK_BASE:
317 case EFI_INFO_INDEX_STACK_SIZE:
318 case EFI_INFO_INDEX_GOP_MODE:
319 case EFI_INFO_INDEX_UGA_VERTICAL_RESOLUTION:
320 case EFI_INFO_INDEX_UGA_HORISONTAL_RESOLUTION:
321 return 4;
322 case EFI_INFO_INDEX_BOOT_ARGS:
323 return (uint32_t)RTStrNLen(pThis->szBootArgs,
324 sizeof pThis->szBootArgs) + 1;
325 case EFI_INFO_INDEX_DEVICE_PROPS:
326 return pThis->u32DevicePropsLen;
327 case EFI_INFO_INDEX_FSB_FREQUENCY:
328 case EFI_INFO_INDEX_CPU_FREQUENCY:
329 case EFI_INFO_INDEX_TSC_FREQUENCY:
330 return 8;
331 }
332 Assert(false);
333 return 0;
334}
335
336static uint8_t efiInfoNextByte(PDEVEFI pThis)
337{
338 union
339 {
340 uint32_t u32;
341 uint64_t u64;
342 } value;
343
344 switch (pThis->iInfoSelector)
345 {
346 case EFI_INFO_INDEX_VOLUME_BASE:
347 value.u32 = pThis->GCLoadAddress;
348 break;
349 case EFI_INFO_INDEX_VOLUME_SIZE:
350 value.u32 = pThis->cbEfiRom;
351 break;
352 case EFI_INFO_INDEX_TEMPMEM_BASE:
353 value.u32 = VBOX_EFI_TOP_OF_STACK; /* just after stack */
354 break;
355 case EFI_INFO_INDEX_TEMPMEM_SIZE:
356 value.u32 = 512 * 1024; /* 512 K */
357 break;
358 case EFI_INFO_INDEX_STACK_BASE:
359 /* Keep in sync with value in EfiThunk.asm */
360 value.u32 = VBOX_EFI_TOP_OF_STACK - 128*1024; /* 2M - 128 K */
361 break;
362 case EFI_INFO_INDEX_STACK_SIZE:
363 value.u32 = 128*1024; /* 128 K */
364 break;
365 case EFI_INFO_INDEX_FSB_FREQUENCY:
366 value.u64 = pThis->u64FsbFrequency;
367 break;
368 case EFI_INFO_INDEX_TSC_FREQUENCY:
369 value.u64 = pThis->u64TscFrequency;
370 break;
371 case EFI_INFO_INDEX_CPU_FREQUENCY:
372 value.u64 = pThis->u64CpuFrequency;
373 break;
374 case EFI_INFO_INDEX_BOOT_ARGS:
375 return pThis->szBootArgs[pThis->iInfoPosition];
376 case EFI_INFO_INDEX_DEVICE_PROPS:
377 return pThis->pu8DeviceProps[pThis->iInfoPosition];
378 case EFI_INFO_INDEX_GOP_MODE:
379 value.u32 = pThis->u32GopMode;
380 break;
381 case EFI_INFO_INDEX_UGA_HORISONTAL_RESOLUTION:
382 value.u32 = pThis->u32UgaHorisontal;
383 break;
384 case EFI_INFO_INDEX_UGA_VERTICAL_RESOLUTION:
385 value.u32 = pThis->u32UgaVertical;
386 break;
387 default:
388 Assert(false);
389 value.u64 = 0;
390 break;
391 }
392
393 return *((uint8_t*)&value+pThis->iInfoPosition);
394}
395
396static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
397{
398 int rc = VINF_SUCCESS;
399 PEFIVAR pEfiVar = NULL;
400 LogFlowFuncEnter();
401 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
402 rc = SSMR3PutStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
403 AssertRCReturn(rc, rc);
404 rc = SSMR3PutStructEx(pSSM, &pThis->NVRAM.OperationVarOp, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
405 AssertRCReturn(rc, rc);
406 int idxV = 0;
407 RTListForEach(&pThis->NVRAM.NvramVariableList.List, pEfiVar, EFIVAR, List)
408 {
409 rc = SSMR3PutStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
410 AssertRCReturn(rc, rc);
411 idxV++;
412 }
413 Assert((pThis->NVRAM.cNvramVariables == idxV));
414 Log2(("idxV: %d\n", idxV));
415 LogFlowFuncLeaveRC(rc);
416 return rc;
417}
418
419static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
420{
421 int rc = VINF_SUCCESS;
422 NOREF(uPass);
423 LogFlowFunc(("ENTER: uVersion:%d, uPass:%d\n", uVersion, uPass));
424 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
425 if (uPass != SSM_PASS_FINAL)
426 return rc;
427 /* we should clean up the loaded values */
428 nvramFlushDeviceVariableList(pThis);
429 if (uVersion == 1)
430 {
431 rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
432 AssertRCReturn(rc, rc);
433 rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM.OperationVarOp, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
434 AssertRCReturn(rc, rc);
435 int idxVariable = 0;
436 Assert(RTListIsEmpty(&pThis->NVRAM.NvramVariableList.List));
437 RTListInit(&pThis->NVRAM.NvramVariableList.List);
438 for (idxVariable = 0; idxVariable < pThis->NVRAM.cNvramVariables; ++idxVariable)
439 {
440 PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
441 AssertPtrReturn(pEfiVar, VERR_NO_MEMORY);
442
443 rc = SSMR3GetStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
444 AssertRCReturn(rc, rc);
445
446 RTListInit(&pEfiVar->List);
447 RTListAppend(&pThis->NVRAM.NvramVariableList.List, &pEfiVar->List);
448
449 if (pThis->NVRAM.idxCurrentVar == pEfiVar->idxVariable)
450 pThis->NVRAM.pCurrentVarOp = pEfiVar;
451 }
452 }
453 LogFlowFuncLeaveRC(rc);
454 return rc;
455}
456
457#if 0
458static DECLCALLBACK(int) efiLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
459{
460 int rc = VINF_SUCCESS;
461 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
462 LogFlowFuncEnter();
463 if (RTListIsEmpty(&pThis->NVRAM.NvramVariableList.List))
464 nvramLoad(pThis);
465 LogFlowFuncLeaveRC(rc);
466 return rc;
467}
468#endif
469
470/**
471 * Port I/O Handler for IN operations.
472 *
473 * @returns VBox status code.
474 *
475 * @param pDevIns The device instance.
476 * @param pvUser User argument - ignored.
477 * @param Port Port number used for the IN operation.
478 * @param pu32 Where to store the result.
479 * @param cb Number of bytes read.
480 */
481static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
482{
483 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
484 Log4(("EFI in: %x %x\n", Port, cb));
485
486 switch (Port)
487 {
488 case EFI_INFO_PORT:
489 if (pThis->iInfoPosition == -1 && cb == 4)
490 {
491 *pu32 = efiInfoSize(pThis);
492 pThis->iInfoPosition = 0;
493 }
494 else
495 {
496 /* So far */
497 if (cb != 1)
498 return VERR_IOM_IOPORT_UNUSED;
499 *pu32 = efiInfoNextByte(pThis);
500 pThis->iInfoPosition++;
501 }
502 return VINF_SUCCESS;
503
504 case EFI_PANIC_PORT:
505#ifdef IN_RING3
506 LogRel(("Panic port read!\n"));
507 /* Insert special code here on panic reads */
508 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: panic port read!\n");
509#else
510 /* Reschedule to R3 */
511 return VINF_IOM_R3_IOPORT_READ;
512#endif
513 case EFI_VARIABLE_OP:
514 switch (pThis->NVRAM.enmOp)
515 {
516 case EFI_VM_VARIABLE_OP_START:
517 /* @todo: nop ? */
518 *pu32 = pThis->NVRAM.u32Status;
519 break;
520 case EFI_VM_VARIABLE_OP_END:
521 break;
522 case EFI_VM_VARIABLE_OP_INDEX:
523 break;
524 case EFI_VM_VARIABLE_OP_GUID:
525 *pu32 = pThis->NVRAM.OperationVarOp.uuid.au8[pThis->NVRAM.idxOpBuffer];
526 pThis->NVRAM.idxOpBuffer++;
527 break;
528 case EFI_VM_VARIABLE_OP_ATTRIBUTE:
529 *pu32 = pThis->NVRAM.OperationVarOp.u32Attribute;
530 break;
531 case EFI_VM_VARIABLE_OP_NAME:
532 *pu32 = pThis->NVRAM.OperationVarOp.szVariableName[pThis->NVRAM.idxOpBuffer];
533 pThis->NVRAM.idxOpBuffer++;
534 break;
535 case EFI_VM_VARIABLE_OP_NAME_LENGTH:
536 *pu32 = pThis->NVRAM.OperationVarOp.cbVariableName;
537 break;
538 case EFI_VM_VARIABLE_OP_VALUE:
539 *pu32 = pThis->NVRAM.OperationVarOp.au8Value[pThis->NVRAM.idxOpBuffer];
540 pThis->NVRAM.idxOpBuffer++;
541 break;
542 case EFI_VM_VARIABLE_OP_VALUE_LENGTH:
543 *pu32 = pThis->NVRAM.OperationVarOp.cbValue;
544 break;
545 default:
546 break;
547 }
548 return VINF_SUCCESS;
549 case EFI_VARIABLE_PARAM:
550 {
551 break;
552 }
553 return VINF_SUCCESS;
554 }
555
556 return VERR_IOM_IOPORT_UNUSED;
557}
558
559
560/**
561 * Port I/O Handler for OUT operations.
562 *
563 * @returns VBox status code.
564 *
565 * @param pDevIns The device instance.
566 * @param pvUser User argument - ignored.
567 * @param Port Port number used for the IN operation.
568 * @param u32 The value to output.
569 * @param cb The value size in bytes.
570 */
571static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
572{
573 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
574 Log4(("efi: out %x %x %d\n", Port, u32, cb));
575
576 switch (Port)
577 {
578 case EFI_INFO_PORT:
579 pThis->iInfoSelector = u32;
580 pThis->iInfoPosition = -1;
581 break;
582 case EFI_DEBUG_PORT:
583 {
584 /* The raw version. */
585 switch (u32)
586 {
587 case '\r': Log3(("efi: <return>\n")); break;
588 case '\n': Log3(("efi: <newline>\n")); break;
589 case '\t': Log3(("efi: <tab>\n")); break;
590 default: Log3(("efi: %c (%02x)\n", u32, u32)); break;
591 }
592 /* The readable, buffered version. */
593 if (u32 == '\n' || u32 == '\r')
594 {
595 pThis->szMsg[pThis->iMsg] = '\0';
596 if (pThis->iMsg)
597 {
598 Log(("efi: %s\n", pThis->szMsg));
599#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
600 const char *pszVBoxDbg = strstr(pThis->szMsg, "VBoxDbg> ");
601 if (pszVBoxDbg)
602 {
603 pszVBoxDbg += sizeof("VBoxDbg> ") - 1;
604
605 PRTSTREAM pStrm;
606 int rc = RTStrmOpen("./DevEFI.VBoxDbg", "a", &pStrm);
607 if (RT_SUCCESS(rc))
608 {
609 RTStrmPutStr(pStrm, pszVBoxDbg);
610 RTStrmPutCh(pStrm, '\n');
611 RTStrmClose(pStrm);
612 }
613 }
614#endif
615 }
616 pThis->iMsg = 0;
617 }
618 else
619 {
620 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
621 {
622 pThis->szMsg[pThis->iMsg] = '\0';
623 Log(("efi: %s\n", pThis->szMsg));
624 pThis->iMsg = 0;
625 }
626 pThis->szMsg[pThis->iMsg] = (char )u32;
627 pThis->szMsg[++pThis->iMsg] = '\0';
628 }
629 break;
630 }
631
632 case EFI_PANIC_PORT:
633 {
634 switch (u32)
635 {
636 case EFI_PANIC_CMD_BAD_ORG:
637 case EFI_PANIC_CMD_THUNK_TRAP:
638 LogRel(("EFI Panic: Unexpected trap!!\n"));
639#ifdef VBOX_STRICT
640 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n");
641#else
642 AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
643#endif
644 break;
645
646 case EFI_PANIC_CMD_START_MSG:
647 pThis->iPanicMsg = 0;
648 pThis->szPanicMsg[0] = '\0';
649 break;
650
651 case EFI_PANIC_CMD_END_MSG:
652 LogRel(("EFI Panic: %s\n", pThis->szPanicMsg));
653#ifdef VBOX_STRICT
654 return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThis->szPanicMsg);
655#else
656 return VERR_INTERNAL_ERROR;
657#endif
658
659 default:
660 if ( u32 >= EFI_PANIC_CMD_MSG_FIRST
661 && u32 <= EFI_PANIC_CMD_MSG_LAST)
662 {
663 /* Add the message char to the buffer. */
664 uint32_t i = pThis->iPanicMsg;
665 if (i + 1 < sizeof(pThis->szPanicMsg))
666 {
667 char ch = EFI_PANIC_CMD_MSG_GET_CHAR(u32);
668 if ( ch == '\n'
669 && i > 0
670 && pThis->szPanicMsg[i - 1] == '\r')
671 i--;
672 pThis->szPanicMsg[i] = ch;
673 pThis->szPanicMsg[i + 1] = '\0';
674 pThis->iPanicMsg = i + 1;
675 }
676 }
677 else
678 Log(("EFI: Unknown panic command: %#x (cb=%d)\n", u32, cb));
679 break;
680 }
681 break;
682 }
683 case EFI_VARIABLE_OP:
684 {
685 /* clear buffer index */
686 Assert(u32 < EFI_VM_VARIABLE_OP_MAX);
687 if (u32 >= EFI_VM_VARIABLE_OP_MAX)
688 {
689 u32 = EFI_VARIABLE_OP_STATUS_ERROR;
690 break;
691 }
692 pThis->NVRAM.idxOpBuffer = 0;
693 pThis->NVRAM.enmOp = (EFIVAROP)u32;
694 }
695 break;
696 case EFI_VARIABLE_PARAM:
697 {
698 switch (pThis->NVRAM.enmOp)
699 {
700 case EFI_VM_VARIABLE_OP_START:
701 {
702 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_BSY;
703 switch (u32)
704 {
705 case EFI_VARIABLE_OP_QUERY:
706 {
707 LogRel(("EFI: variable lookup %RTuuid, %s\n",
708 &pThis->NVRAM.OperationVarOp.uuid,
709 pThis->NVRAM.OperationVarOp.szVariableName));
710 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_BSY;
711 PEFIVAR pEfiVar = NULL;
712 memset(pThis->NVRAM.OperationVarOp.au8Value, 0, EFI_VARIABLE_NAME_MAX);
713 int nvramRc = nvramLookupVariableByUuidAndName(
714 pThis,
715 pThis->NVRAM.OperationVarOp.szVariableName,
716 &pThis->NVRAM.OperationVarOp.uuid,
717 &pEfiVar);
718 if (RT_SUCCESS(nvramRc))
719 {
720 pThis->NVRAM.OperationVarOp.u32Attribute = pEfiVar->u32Attribute;
721 pThis->NVRAM.OperationVarOp.cbVariableName = pEfiVar->cbVariableName;
722 pThis->NVRAM.OperationVarOp.cbValue = pEfiVar->cbValue;
723 memcpy(pThis->NVRAM.OperationVarOp.au8Value,
724 pEfiVar->au8Value, pEfiVar->cbValue);
725 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
726 pThis->NVRAM.pCurrentVarOp = pEfiVar;
727 pThis->NVRAM.idxCurrentVar = pEfiVar->idxVariable;
728 LogFlowFunc(("OperationVar: au8Value:%.*Rhxs\n",
729 pThis->NVRAM.OperationVarOp.cbValue,
730 pThis->NVRAM.OperationVarOp.au8Value));
731 }
732 else
733 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_NOT_FOUND;
734 }
735 break;
736 case EFI_VARIABLE_OP_ADD:
737 {
738 LogRel(("EFI: variable add %RTuuid, %s\n", &pThis->NVRAM.OperationVarOp.uuid, pThis->NVRAM.OperationVarOp.szVariableName));
739 PEFIVAR pEfiVar = NULL;
740 LogFlowFunc(("OperationVar: au8Value:%.*Rhxs\n",
741 pThis->NVRAM.OperationVarOp.cbValue,
742 pThis->NVRAM.OperationVarOp.au8Value));
743 int nvramRc = nvramLookupVariableByUuidAndName(
744 pThis,
745 pThis->NVRAM.OperationVarOp.szVariableName,
746 &pThis->NVRAM.OperationVarOp.uuid,
747 &pEfiVar);
748 if (RT_SUCCESS(nvramRc))
749 {
750 /* delete or update ? */
751 /* @todo: check whether pEfiVar is WP */
752 LogFlowFunc(("pEfiVar: au8Value:%.*Rhxs\n",
753 pEfiVar->cbValue,
754 pEfiVar->au8Value));
755 if (pThis->NVRAM.OperationVarOp.cbValue == 0)
756 {
757 /* delete */
758 RTListNodeRemove(&pEfiVar->List);
759 RTMemFree(pEfiVar);
760 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
761 pThis->NVRAM.cNvramVariables--;
762 }
763 else
764 {
765 /* update */
766 pEfiVar->cbValue = pThis->NVRAM.OperationVarOp.cbValue;
767 memcpy(pEfiVar->au8Value, pThis->NVRAM.OperationVarOp.au8Value, pEfiVar->cbValue);
768 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
769 }
770 }
771 else
772 {
773 if (pThis->NVRAM.OperationVarOp.cbValue != 0)
774 {
775 pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
776 if (!pEfiVar)
777 {
778 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
779 break;
780 }
781 }
782 else
783 {
784 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
785 break;
786 }
787
788 memcpy(pEfiVar, &pThis->NVRAM.OperationVarOp, sizeof(EFIVAR));
789 RTListInit(&pEfiVar->List);
790 RTListAppend(&pThis->NVRAM.NvramVariableList.List, &pEfiVar->List);
791 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
792 pThis->NVRAM.cNvramVariables++;
793 pEfiVar->idxVariable = pThis->NVRAM.iNvramLastIndex;
794 pThis->NVRAM.iNvramLastIndex++;
795 }
796 }
797 LogFunc(("cNvramVariables:%d, iNvramLastIndex:%d\n", pThis->NVRAM.cNvramVariables, pThis->NVRAM.iNvramLastIndex));
798 break;
799 case EFI_VARIABLE_OP_QUERY_NEXT:
800 {
801 PEFIVAR pEfiVar = RTListNodeGetNext(&pThis->NVRAM.pCurrentVarOp->List, EFIVAR, List);
802 if (pEfiVar)
803 {
804 memcpy(&pThis->NVRAM.OperationVarOp, pEfiVar, sizeof(EFIVAR));
805 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
806 }
807 else
808 pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_NOT_FOUND;
809 }
810 break;
811 default:
812 /* @todo: return error */
813 break;
814 }
815 }
816 case EFI_VM_VARIABLE_OP_END:
817 break;
818 case EFI_VM_VARIABLE_OP_INDEX:
819 break;
820 case EFI_VM_VARIABLE_OP_GUID:
821 pThis->NVRAM.OperationVarOp.uuid.au8[pThis->NVRAM.idxOpBuffer] = (uint8_t)u32;
822 pThis->NVRAM.idxOpBuffer++;
823 break;
824 case EFI_VM_VARIABLE_OP_ATTRIBUTE:
825 pThis->NVRAM.OperationVarOp.u32Attribute = u32;
826 break;
827 case EFI_VM_VARIABLE_OP_NAME:
828 pThis->NVRAM.OperationVarOp.szVariableName[pThis->NVRAM.idxOpBuffer] = (uint8_t)u32;
829 pThis->NVRAM.idxOpBuffer++;
830 break;
831 case EFI_VM_VARIABLE_OP_NAME_LENGTH:
832 pThis->NVRAM.OperationVarOp.cbVariableName = u32;
833 memset(pThis->NVRAM.OperationVarOp.szVariableName, 0, EFI_VARIABLE_NAME_MAX);
834 break;
835 case EFI_VM_VARIABLE_OP_VALUE:
836 pThis->NVRAM.OperationVarOp.au8Value[pThis->NVRAM.idxOpBuffer] = (uint8_t)u32;
837 pThis->NVRAM.idxOpBuffer++;
838 break;
839 case EFI_VM_VARIABLE_OP_VALUE_LENGTH:
840 pThis->NVRAM.OperationVarOp.cbValue = u32;
841 memset(pThis->NVRAM.OperationVarOp.au8Value, 0, EFI_VARIABLE_VALUE_MAX);
842 break;
843 default:
844 break;
845 }
846 }
847 break;
848
849 default:
850 Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", Port, u32, cb));
851 break;
852 }
853 return VINF_SUCCESS;
854}
855
856/**
857 * Init complete notification.
858 *
859 * @returns VBOX status code.
860 * @param pDevIns The device instance.
861 */
862static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
863{
864 /* PC Bios */
865 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
866 uint32_t u32;
867
868 /*
869 * Memory sizes.
870 */
871 uint64_t const offRamHole = _4G - pThis->cbRamHole;
872 if (pThis->cbRam > 16 * _1M)
873 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
874 else
875 u32 = 0;
876 cmosWrite(pDevIns, 0x34, u32 & 0xff);
877 cmosWrite(pDevIns, 0x35, u32 >> 8);
878
879 /*
880 * Number of CPUs.
881 */
882 cmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
883
884 return VINF_SUCCESS;
885}
886
887/**
888 * Reset notification.
889 *
890 * @returns VBox status.
891 * @param pDevIns The device instance data.
892 */
893static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
894{
895 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
896 int rc;
897
898 LogFlow(("efiReset\n"));
899
900 pThis->iInfoSelector = 0;
901 pThis->iInfoPosition = -1;
902
903 pThis->iMsg = 0;
904 pThis->szMsg[0] = '\0';
905 pThis->iPanicMsg = 0;
906 pThis->szPanicMsg[0] = '\0';
907
908 /*
909 * Plan some structures in RAM.
910 */
911 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->cbDmiTables);
912 if (pThis->u8IOAPIC)
913 FwCommonPlantMpsFloatPtr(pDevIns);
914
915 /*
916 * Re-shadow the Firmware Volume and make it RAM/RAM.
917 */
918 uint32_t cPages = RT_ALIGN_64(pThis->cbEfiRom, PAGE_SIZE) >> PAGE_SHIFT;
919 RTGCPHYS GCPhys = pThis->GCLoadAddress;
920 while (cPages > 0)
921 {
922 uint8_t abPage[PAGE_SIZE];
923
924 /* Read the (original) ROM page and write it back to the RAM page. */
925 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
926 AssertLogRelRC(rc);
927
928 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
929 AssertLogRelRC(rc);
930 if (RT_FAILURE(rc))
931 memset(abPage, 0xcc, sizeof(abPage));
932
933 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
934 AssertLogRelRC(rc);
935
936 /* Switch to the RAM/RAM mode. */
937 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
938 AssertLogRelRC(rc);
939
940 /* Advance */
941 GCPhys += PAGE_SIZE;
942 cPages--;
943 }
944}
945
946/**
947 * Destruct a device instance.
948 *
949 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
950 * resources can be freed correctly.
951 *
952 * @param pDevIns The device instance data.
953 */
954static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
955{
956 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
957 nvramStore(pThis);
958 nvramFlushDeviceVariableList(pThis);
959
960 /*
961 * Free MM heap pointers.
962 */
963 if (pThis->pu8EfiRom)
964 {
965 RTFileReadAllFree(pThis->pu8EfiRom, (size_t)pThis->cbEfiRom);
966 pThis->pu8EfiRom = NULL;
967 }
968
969 if (pThis->pszEfiRomFile)
970 {
971 MMR3HeapFree(pThis->pszEfiRomFile);
972 pThis->pszEfiRomFile = NULL;
973 }
974
975 if (pThis->pu8EfiThunk)
976 {
977 MMR3HeapFree(pThis->pu8EfiThunk);
978 pThis->pu8EfiThunk = NULL;
979 }
980
981 if (pThis->pu8DeviceProps)
982 {
983 MMR3HeapFree(pThis->pu8DeviceProps);
984 pThis->pu8DeviceProps = NULL;
985 pThis->u32DevicePropsLen = 0;
986 }
987
988 return VINF_SUCCESS;
989}
990
991/**
992 * Helper that searches for a FFS file of a given type.
993 *
994 * @returns Pointer to the FFS file header if found, NULL if not.
995 *
996 * @param pFfsFile Pointer to the FFS file header to start searching at.
997 * @param pbEnd The end of the firmware volume.
998 * @param FileType The file type to look for.
999 * @param pcbFfsFile Where to store the FFS file size (includes header).
1000 */
1001DECLINLINE(EFI_FFS_FILE_HEADER const *)
1002efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
1003{
1004#define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
1005 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
1006 {
1007 if (pFfsFile->Type == FileType)
1008 {
1009 *pcbFile = FFS_SIZE(pFfsFile);
1010 LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType));
1011 return pFfsFile;
1012 }
1013 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
1014 }
1015#undef FFS_SIZE
1016 return NULL;
1017}
1018
1019
1020/**
1021 * Parse EFI ROM headers and find entry points.
1022 *
1023 * @returns VBox status.
1024 * @param pThis The device instance data.
1025 */
1026static int efiParseFirmware(PDEVEFI pThis)
1027{
1028 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThis->pu8EfiRom;
1029
1030 /*
1031 * Validate firmware volume header.
1032 */
1033 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
1034 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
1035 VERR_INVALID_MAGIC);
1036 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
1037 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
1038 VERR_VERSION_MISMATCH);
1039 /** @todo check checksum, see PE spec vol. 3 */
1040 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThis->cbEfiRom,
1041 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThis->cbEfiRom),
1042 VERR_INVALID_PARAMETER);
1043 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
1044 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
1045 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
1046 VERR_INVALID_PARAMETER);
1047
1048 AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
1049
1050 uint8_t const * const pbFwVolEnd = pThis->pu8EfiRom + pFwVolHdr->FvLength;
1051 pThis->GCLoadAddress = UINT32_C(0xfffff000) - pThis->cbEfiRom + PAGE_SIZE;
1052
1053 return VINF_SUCCESS;
1054}
1055
1056/**
1057 * Load EFI ROM file into the memory.
1058 *
1059 * @returns VBox status.
1060 * @param pThis The device instance data.
1061 * @param pCfg Configuration node handle for the device.
1062 */
1063static int efiLoadRom(PDEVEFI pThis, PCFGMNODE pCfg)
1064{
1065 /*
1066 * Read the entire firmware volume into memory.
1067 */
1068 void *pvFile;
1069 size_t cbFile;
1070 int rc = RTFileReadAllEx(pThis->pszEfiRomFile,
1071 0 /*off*/,
1072 RTFOFF_MAX /*cbMax*/,
1073 RTFILE_RDALL_O_DENY_WRITE,
1074 &pvFile,
1075 &cbFile);
1076 if (RT_FAILURE(rc))
1077 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
1078 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
1079 pThis->pszEfiRomFile, rc);
1080 pThis->pu8EfiRom = (uint8_t *)pvFile;
1081 pThis->cbEfiRom = cbFile;
1082
1083 /*
1084 * Validate firmware volume and figure out the load address as well as the SEC entry point.
1085 */
1086 rc = efiParseFirmware(pThis);
1087 if (RT_FAILURE(rc))
1088 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
1089 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
1090 pThis->pszEfiRomFile, rc);
1091
1092 /*
1093 * Map the firmware volume into memory as shadowed ROM.
1094 */
1095 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
1096 RTGCPHYS cbQuart = RT_ALIGN_64(pThis->cbEfiRom / 4, PAGE_SIZE);
1097 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1098 pThis->GCLoadAddress,
1099 cbQuart,
1100 pThis->pu8EfiRom,
1101 cbQuart,
1102 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1103 "EFI Firmware Volume");
1104 AssertRCReturn(rc, rc);
1105 rc = PDMDevHlpROMProtectShadow(pThis->pDevIns, pThis->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
1106 AssertRCReturn(rc, rc);
1107 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1108 pThis->GCLoadAddress + cbQuart,
1109 cbQuart,
1110 pThis->pu8EfiRom + cbQuart,
1111 cbQuart,
1112 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1113 "EFI Firmware Volume (Part 2)");
1114 if (RT_FAILURE(rc))
1115 return rc;
1116 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1117 pThis->GCLoadAddress + cbQuart * 2,
1118 cbQuart,
1119 pThis->pu8EfiRom + cbQuart * 2,
1120 cbQuart,
1121 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1122 "EFI Firmware Volume (Part 3)");
1123 if (RT_FAILURE(rc))
1124 return rc;
1125 rc = PDMDevHlpROMRegister(pThis->pDevIns,
1126 pThis->GCLoadAddress + cbQuart * 3,
1127 pThis->cbEfiRom - cbQuart * 3,
1128 pThis->pu8EfiRom + cbQuart * 3,
1129 pThis->cbEfiRom - cbQuart * 3,
1130 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
1131 "EFI Firmware Volume (Part 4)");
1132 if (RT_FAILURE(rc))
1133 return rc;
1134 return VINF_SUCCESS;
1135}
1136
1137static uint8_t efiGetHalfByte(char ch)
1138{
1139 uint8_t val;
1140
1141 if (ch >= '0' && ch <= '9')
1142 val = ch - '0';
1143 else if (ch >= 'A' && ch <= 'F')
1144 val = ch - 'A' + 10;
1145 else if(ch >= 'a' && ch <= 'f')
1146 val = ch - 'a' + 10;
1147 else
1148 val = 0xff;
1149
1150 return val;
1151
1152}
1153
1154
1155static int efiParseDeviceString(PDEVEFI pThis, char* pszDeviceProps)
1156{
1157 int rc = 0;
1158 uint32_t iStr, iHex, u32OutLen;
1159 uint8_t u8Value = 0; /* (shut up gcc) */
1160 bool fUpper = true;
1161
1162 u32OutLen = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
1163
1164 pThis->pu8DeviceProps =
1165 (uint8_t*)PDMDevHlpMMHeapAlloc(pThis->pDevIns, u32OutLen);
1166 if (!pThis->pu8DeviceProps)
1167 return VERR_NO_MEMORY;
1168
1169 for (iStr=0, iHex = 0; pszDeviceProps[iStr]; iStr++)
1170 {
1171 uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]);
1172 if (u8Hb > 0xf)
1173 continue;
1174
1175 if (fUpper)
1176 u8Value = u8Hb << 4;
1177 else
1178 pThis->pu8DeviceProps[iHex++] = u8Hb | u8Value;
1179
1180 Assert(iHex < u32OutLen);
1181 fUpper = !fUpper;
1182 }
1183
1184 Assert(iHex == 0 || fUpper);
1185 pThis->u32DevicePropsLen = iHex;
1186
1187 return rc;
1188}
1189
1190/**
1191 * @copydoc(PDMIBASE::pfnQueryInterface)
1192 */
1193static DECLCALLBACK(void *) devEfi_pfnQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1194{
1195 LogFlowFunc(("ENTER: pIBase: %p, pszIID:%p\n", __FUNCTION__, pInterface, pszIID));
1196 PDEVEFI pThis = RT_FROM_MEMBER(pInterface, DEVEFI, Lun0.IBase);
1197
1198 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase);
1199 return NULL;
1200}
1201
1202/**
1203 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1204 */
1205static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1206{
1207 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
1208 int rc;
1209
1210 Assert(iInstance == 0);
1211
1212 pThis->pDevIns = pDevIns;
1213
1214 /*
1215 * Validate and read the configuration.
1216 */
1217 if (!CFGMR3AreValuesValid(pCfg,
1218 "EfiRom\0"
1219 "RamSize\0"
1220 "RamHoleSize\0"
1221 "NumCPUs\0"
1222 "UUID\0"
1223 "IOAPIC\0"
1224 "DmiBIOSFirmwareMajor\0"
1225 "DmiBIOSFirmwareMinor\0"
1226 "DmiBIOSReleaseDate\0"
1227 "DmiBIOSReleaseMajor\0"
1228 "DmiBIOSReleaseMinor\0"
1229 "DmiBIOSVendor\0"
1230 "DmiBIOSVersion\0"
1231 "DmiSystemFamily\0"
1232 "DmiSystemProduct\0"
1233 "DmiSystemSerial\0"
1234 "DmiSystemSKU\0"
1235 "DmiSystemUuid\0"
1236 "DmiSystemVendor\0"
1237 "DmiSystemVersion\0"
1238 "DmiBoardAssetTag\0"
1239 "DmiBoardBoardType\0"
1240 "DmiBoardLocInChass\0"
1241 "DmiBoardProduct\0"
1242 "DmiBoardSerial\0"
1243 "DmiBoardVendor\0"
1244 "DmiBoardVersion\0"
1245 "DmiChassisAssetTag\0"
1246 "DmiChassisSerial\0"
1247 "DmiChassisVendor\0"
1248 "DmiChassisVersion\0"
1249 "DmiProcManufacturer\0"
1250 "DmiProcVersion\0"
1251 "DmiOEMVBoxVer\0"
1252 "DmiOEMVBoxRev\0"
1253 "DmiUseHostInfo\0"
1254 "DmiExposeMemoryTable\0"
1255 "DmiExposeProcInf\0"
1256 "64BitEntry\0"
1257 "BootArgs\0"
1258 "DeviceProps\0"
1259 "GopMode\0"
1260 "UgaHorizontalResolution\0"
1261 "UgaVerticalResolution\0"))
1262 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1263 N_("Configuration error: Invalid config value(s) for the EFI device"));
1264
1265 /* CPU count (optional). */
1266 rc = CFGMR3QueryU32Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1267 AssertLogRelRCReturn(rc, rc);
1268
1269 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1270 if (RT_FAILURE (rc))
1271 return PDMDEV_SET_ERROR(pDevIns, rc,
1272 N_("Configuration error: Failed to read \"IOAPIC\""));
1273
1274 /*
1275 * Query the machine's UUID for SMBIOS/DMI use.
1276 */
1277 RTUUID uuid;
1278 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1279 if (RT_FAILURE(rc))
1280 return PDMDEV_SET_ERROR(pDevIns, rc,
1281 N_("Configuration error: Querying \"UUID\" failed"));
1282
1283 /*
1284 * Convert the UUID to network byte order. Not entirely straightforward as
1285 * parts are MSB already...
1286 */
1287 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1288 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1289 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1290 memcpy(&pThis->aUuid, &uuid, sizeof pThis->aUuid);
1291 RTListInit((PRTLISTNODE)&pThis->NVRAM.NvramVariableList.List);
1292
1293
1294 /*
1295 * RAM sizes
1296 */
1297 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
1298 AssertLogRelRCReturn(rc, rc);
1299 rc = CFGMR3QueryU64(pCfg, "RamHoleSize", &pThis->cbRamHole);
1300 AssertLogRelRCReturn(rc, rc);
1301 pThis->cbBelow4GB = RT_MIN(pThis->cbRam, _4G - pThis->cbRamHole);
1302 pThis->cbAbove4GB = pThis->cbRam - pThis->cbBelow4GB;
1303
1304 /*
1305 * Get the system EFI ROM file name.
1306 */
1307 rc = CFGMR3QueryStringAlloc(pCfg, "EfiRom", &pThis->pszEfiRomFile);
1308 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1309 {
1310 pThis->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
1311 if (!pThis->pszEfiRomFile)
1312 return VERR_NO_MEMORY;
1313
1314 rc = RTPathAppPrivateArchTop(pThis->pszEfiRomFile, RTPATH_MAX);
1315 AssertRCReturn(rc, rc);
1316 rc = RTPathAppend(pThis->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd");
1317 AssertRCReturn(rc, rc);
1318 }
1319 else if (RT_FAILURE(rc))
1320 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1321 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
1322 else if (!*pThis->pszEfiRomFile)
1323 {
1324 MMR3HeapFree(pThis->pszEfiRomFile);
1325 pThis->pszEfiRomFile = NULL;
1326 }
1327
1328 /* NVRAM processing */
1329 pThis->Lun0.IBase.pfnQueryInterface = devEfi_pfnQueryInterface;
1330
1331#if 0
1332 rc = PDMDevHlpSSMRegisterEx(pDevIns, EFI_SSM_VERSION, sizeof(*pThis), NULL /*pszBefore*/,
1333 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveDone*/,
1334 NULL /*pfnSavePrep*/, efiSaveExec, NULL /*pfnSaveDone*/,
1335 NULL /*pfnLoadPrep*/, efiLoadExec, efiLoadDone);
1336#else
1337 rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThis), efiSaveExec, efiLoadExec);
1338#endif
1339 AssertRCReturn(rc, rc);
1340
1341 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "NvramStorage");
1342 if (RT_FAILURE(rc))
1343 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
1344
1345 pThis->Lun0.pNvramDown = (PPDMINVRAM)pThis->Lun0.pDrvBase->pfnQueryInterface(pThis->Lun0.pDrvBase, PDMINVRAM_IID);
1346 AssertPtrReturn(pThis->Lun0.pNvramDown, VERR_PDM_MISSING_INTERFACE_BELOW);
1347
1348 nvramLoad(pThis);
1349 /*
1350 * Get boot args.
1351 */
1352 rc = CFGMR3QueryString(pCfg, "BootArgs",
1353 pThis->szBootArgs, sizeof pThis->szBootArgs);
1354 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1355 {
1356 strcpy(pThis->szBootArgs, "");
1357 rc = VINF_SUCCESS;
1358 }
1359 if (RT_FAILURE(rc))
1360 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1361 N_("Configuration error: Querying \"BootArgs\" as a string failed"));
1362
1363 LogRel(("EFI boot args: %s\n", pThis->szBootArgs));
1364
1365 /*
1366 * Get device props.
1367 */
1368 char* pszDeviceProps;
1369 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceProps", &pszDeviceProps);
1370 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1371 {
1372 pszDeviceProps = NULL;
1373 rc = VINF_SUCCESS;
1374 }
1375 if (RT_FAILURE(rc))
1376 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1377 N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
1378 if (pszDeviceProps)
1379 {
1380 LogRel(("EFI device props: %s\n", pszDeviceProps));
1381 rc = efiParseDeviceString(pThis, pszDeviceProps);
1382 MMR3HeapFree(pszDeviceProps);
1383 if (RT_FAILURE(rc))
1384 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1385 N_("Configuration error: Cannot parse device properties"));
1386 }
1387 else
1388 {
1389 pThis->pu8DeviceProps = NULL;
1390 pThis->u32DevicePropsLen = 0;
1391 }
1392
1393 /*
1394 * CPU frequencies
1395 */
1396 // @todo: we need to have VMM API to access TSC increase speed, for now provide reasonable default
1397 pThis->u64TscFrequency = RTMpGetMaxFrequency(0) * 1000 * 1000;// TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
1398 if (pThis->u64TscFrequency == 0)
1399 pThis->u64TscFrequency = UINT64_C(2500000000);
1400 /* Multiplier is read from MSR_IA32_PERF_STATUS, and now is hardcoded as 4 */
1401 pThis->u64FsbFrequency = pThis->u64TscFrequency / 4;
1402 pThis->u64CpuFrequency = pThis->u64TscFrequency;
1403
1404 /*
1405 * GOP graphics
1406 */
1407 rc = CFGMR3QueryU32(pCfg, "GopMode", &pThis->u32GopMode);
1408 AssertRC(rc);
1409 if (pThis->u32GopMode == UINT32_MAX)
1410 {
1411 pThis->u32GopMode = 2; /* 1024x768 */
1412 }
1413
1414 /*
1415 * Uga graphics
1416 */
1417 rc = CFGMR3QueryU32(pCfg, "UgaHorizontalResolution", &pThis->u32UgaHorisontal);
1418 AssertRC(rc);
1419 if (pThis->u32UgaHorisontal == 0)
1420 {
1421 pThis->u32UgaHorisontal = 1024; /* 1024x768 */
1422 }
1423 rc = CFGMR3QueryU32(pCfg, "UgaVerticalResolution", &pThis->u32UgaVertical);
1424 AssertRC(rc);
1425 if (pThis->u32UgaVertical == 0)
1426 {
1427 pThis->u32UgaVertical = 768; /* 1024x768 */
1428 }
1429
1430#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
1431 /*
1432 * Zap the debugger script
1433 */
1434 RTFileDelete("./DevEFI.VBoxDbg");
1435#endif
1436
1437 /*
1438 * Load firmware volume and thunk ROM.
1439 */
1440 rc = efiLoadRom(pThis, pCfg);
1441 if (RT_FAILURE(rc))
1442 return rc;
1443
1444 /*
1445 * Register our communication ports.
1446 */
1447 rc = PDMDevHlpIOPortRegister(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, NULL,
1448 efiIOPortWrite, efiIOPortRead,
1449 NULL, NULL, "EFI communication ports");
1450 if (RT_FAILURE(rc))
1451 return rc;
1452
1453 /*
1454 * Plant DMI and MPS tables
1455 * XXX I wonder if we really need these tables as there is no SMBIOS header...
1456 */
1457 uint16_t cbDmiTablesDummy;
1458 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThis->aUuid,
1459 pDevIns->pCfg, pThis->cCpus, &pThis->cbDmiTables);
1460 AssertRCReturn(rc, rc);
1461 if (pThis->u8IOAPIC)
1462 FwCommonPlantMpsTable(pDevIns,
1463 pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1464 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1465 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
1466 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1467
1468 AssertRCReturn(rc, rc);
1469
1470 /*
1471 * Call reset to set things up.
1472 */
1473 efiReset(pDevIns);
1474
1475 return VINF_SUCCESS;
1476}
1477
1478/**
1479 * The device registration structure.
1480 */
1481const PDMDEVREG g_DeviceEFI =
1482{
1483 /* u32Version */
1484 PDM_DEVREG_VERSION,
1485 /* szName */
1486 "efi",
1487 /* szRCMod */
1488 "",
1489 /* szR0Mod */
1490 "",
1491 /* pszDescription */
1492 "Extensible Firmware Interface Device",
1493 /* fFlags */
1494 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1495 /* fClass */
1496 PDM_DEVREG_CLASS_ARCH_BIOS,
1497 /* cMaxInstances */
1498 1,
1499 /* cbInstance */
1500 sizeof(DEVEFI),
1501 /* pfnConstruct */
1502 efiConstruct,
1503 /* pfnDestruct */
1504 efiDestruct,
1505 /* pfnRelocate */
1506 NULL,
1507 /* pfnIOCtl */
1508 NULL,
1509 /* pfnPowerOn */
1510 NULL,
1511 /* pfnReset */
1512 efiReset,
1513 /* pfnSuspend */
1514 NULL,
1515 /* pfnResume */
1516 NULL,
1517 /* pfnAttach */
1518 NULL,
1519 /* pfnDetach */
1520 NULL,
1521 /* pfnQueryInterface. */
1522 NULL,
1523 /* pfnInitComplete. */
1524 efiInitComplete,
1525 /* pfnPowerOff */
1526 NULL,
1527 /* pfnSoftReset */
1528 NULL,
1529 /* u32VersionEnd */
1530 PDM_DEVREG_VERSION
1531};
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