VirtualBox

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

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

EFI: nits.

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