VirtualBox

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

Last change on this file since 24951 was 24951, checked in by vboxsync, 15 years ago

EFI: further compatibility work

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/* $Id: DevEFI.cpp 24951 2009-11-25 12:39:24Z vboxsync $ */
2/** @file
3 * DevEFI - EFI <-> VirtualBox Integration Framework.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_DEV_EFI
26
27#include <VBox/pdmdev.h>
28#include <VBox/pgm.h>
29#include <VBox/mm.h>
30#include <VBox/log.h>
31#include <VBox/err.h>
32#include <VBox/param.h>
33#include <VBox/dbgf.h>
34
35#include <iprt/assert.h>
36#include <iprt/alloc.h>
37#include <iprt/file.h>
38#include <iprt/string.h>
39#include <iprt/uuid.h>
40#include <iprt/path.h>
41#include <iprt/string.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 "../Builtins.h"
49#include "../Builtins2.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#include <IndustryStandard/PeImage.h>
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62typedef struct DEVEFI
63{
64 /** Pointer back to the device instance. */
65 PPDMDEVINS pDevIns;
66 /** EFI message buffer. */
67 char szMsg[VBOX_EFI_DEBUG_BUFFER];
68 /** EFI message buffer index. */
69 uint32_t iMsg;
70 /** EFI panic message buffer. */
71 char szPanicMsg[2048];
72 /** EFI panic message buffer index. */
73 uint32_t iPanicMsg;
74 /** The system EFI ROM data. */
75 uint8_t *pu8EfiRom;
76 /** The size of the system EFI ROM. */
77 uint64_t cbEfiRom;
78 /** The name of the EFI ROM file. */
79 char *pszEfiRomFile;
80 /** Thunk page pointer. */
81 uint8_t *pu8EfiThunk;
82 /** First entry point of the EFI firmware */
83 RTGCPHYS GCEntryPoint0;
84 /* Second Entry Point (PeiCore)*/
85 RTGCPHYS GCEntryPoint1;
86 /** EFI firmware physical load address */
87 RTGCPHYS GCLoadAddress;
88 /** Current info selector */
89 uint32_t iInfoSelector;
90 /** Current info position */
91 int32_t iInfoPosition;
92
93 /** Number of virtual CPUs. (Config) */
94 uint32_t cCpus;
95 /** RAM below 4GB (in bytes). (Config) */
96 uint32_t cbBelow4GB;
97 /** RAM above 4GB (in bytes). (Config) */
98 uint64_t cbAbove4GB;
99
100 uint64_t cbRam;
101
102 uint64_t cbRamHole;
103
104 /** The DMI tables. */
105 uint8_t au8DMIPage[0x1000];
106
107 /** I/O-APIC enabled? */
108 uint8_t u8IOAPIC;
109} DEVEFI;
110typedef DEVEFI *PDEVEFI;
111
112/**
113 * Write to CMOS memory.
114 * This is used by the init complete code.
115 */
116static void cmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
117{
118 Assert(off < 128);
119 Assert(u32Val < 256);
120
121 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
122 AssertRC(rc);
123}
124
125
126static uint32_t efiInfoSize(PDEVEFI pThis)
127{
128 /* So far, everything is 4 bytes, as we only support 32-bit EFI */
129 switch (pThis->iInfoSelector)
130 {
131 case EFI_INFO_INDEX_VOLUME_BASE:
132 case EFI_INFO_INDEX_VOLUME_SIZE:
133 case EFI_INFO_INDEX_TEMPMEM_BASE:
134 case EFI_INFO_INDEX_TEMPMEM_SIZE:
135 case EFI_INFO_INDEX_STACK_BASE:
136 case EFI_INFO_INDEX_STACK_SIZE:
137 return 4;
138 }
139 Assert(false);
140 return 0;
141}
142
143static uint8_t efiInfoNextByte(PDEVEFI pThis)
144{
145 uint32_t iValue;
146 switch (pThis->iInfoSelector)
147 {
148 case EFI_INFO_INDEX_VOLUME_BASE:
149 iValue = pThis->GCLoadAddress;
150 break;
151 case EFI_INFO_INDEX_VOLUME_SIZE:
152 iValue = pThis->cbEfiRom;
153 break;
154 case EFI_INFO_INDEX_TEMPMEM_BASE:
155 iValue = VBOX_EFI_TOP_OF_STACK; /* just after stack */
156 break;
157 case EFI_INFO_INDEX_TEMPMEM_SIZE:
158 iValue = 512 * 1024; /* 512 K */
159 break;
160 case EFI_INFO_INDEX_STACK_BASE:
161 /* Keep in sync with value in EfiThunk.asm */
162 iValue = VBOX_EFI_TOP_OF_STACK - 128*1024; /* 2M - 128 K */
163 break;
164 case EFI_INFO_INDEX_STACK_SIZE:
165 iValue = 128*1024; /* 128 K */
166 break;
167 default:
168 Assert(false);
169 iValue = 0;
170 break;
171 }
172 /* somewhat ugly, but works atm */
173 return *((uint8_t*)&iValue+pThis->iInfoPosition);
174}
175
176/**
177 * Port I/O Handler for IN operations.
178 *
179 * @returns VBox status code.
180 *
181 * @param pDevIns The device instance.
182 * @param pvUser User argument - ignored.
183 * @param Port Port number used for the IN operation.
184 * @param pu32 Where to store the result.
185 * @param cb Number of bytes read.
186 */
187static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
188{
189 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
190 Log4(("EFI in: %x %x\n", Port, cb));
191
192 switch (Port)
193 {
194 case EFI_INFO_PORT:
195 if (pThis->iInfoPosition == -1 && cb == 4)
196 {
197 *pu32 = efiInfoSize(pThis);
198 pThis->iInfoPosition = 0;
199 }
200 else
201 {
202 /* So far */
203 if (cb != 1)
204 return VERR_IOM_IOPORT_UNUSED;
205 *pu32 = efiInfoNextByte(pThis);
206 pThis->iInfoPosition++;
207 }
208 return VINF_SUCCESS;
209 }
210
211 return VERR_IOM_IOPORT_UNUSED;
212}
213
214
215/**
216 * Port I/O Handler for OUT operations.
217 *
218 * @returns VBox status code.
219 *
220 * @param pDevIns The device instance.
221 * @param pvUser User argument - ignored.
222 * @param Port Port number used for the IN operation.
223 * @param u32 The value to output.
224 * @param cb The value size in bytes.
225 */
226static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
227{
228 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
229 Log4(("efi: out %x %x %d\n", Port, u32, cb));
230
231 switch (Port)
232 {
233 case EFI_INFO_PORT:
234 pThis->iInfoSelector = u32;
235 pThis->iInfoPosition = -1;
236 break;
237 case EFI_DEBUG_PORT:
238 {
239 /* The raw version. */
240 switch (u32)
241 {
242 case '\r': Log3(("efi: <return>\n")); break;
243 case '\n': Log3(("efi: <newline>\n")); break;
244 case '\t': Log3(("efi: <tab>\n")); break;
245 default: Log3(("efi: %c (%02x)\n", u32, u32)); break;
246 }
247 /* The readable, buffered version. */
248 if (u32 == '\n' || u32 == '\r')
249 {
250 pThis->szMsg[pThis->iMsg] = '\0';
251 if (pThis->iMsg)
252 {
253 Log(("efi: %s\n", pThis->szMsg));
254#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
255 const char *pszVBoxDbg = strstr(pThis->szMsg, "VBoxDbg> ");
256 if (pszVBoxDbg)
257 {
258 pszVBoxDbg += sizeof("VBoxDbg> ") - 1;
259
260 PRTSTREAM pStrm;
261 int rc = RTStrmOpen("./DevEFI.VBoxDbg", "a", &pStrm);
262 if (RT_SUCCESS(rc))
263 {
264 RTStrmPutStr(pStrm, pszVBoxDbg);
265 RTStrmPutCh(pStrm, '\n');
266 RTStrmClose(pStrm);
267 }
268 }
269#endif
270 }
271 pThis->iMsg = 0;
272 }
273 else
274 {
275 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
276 {
277 pThis->szMsg[pThis->iMsg] = '\0';
278 Log(("efi: %s\n", pThis->szMsg));
279 pThis->iMsg = 0;
280 }
281 pThis->szMsg[pThis->iMsg] = (char )u32;
282 pThis->szMsg[++pThis->iMsg] = '\0';
283 }
284 break;
285 }
286
287 case EFI_PANIC_PORT:
288 {
289 switch (u32)
290 {
291 case EFI_PANIC_CMD_BAD_ORG:
292 LogRel(("EFI Panic: You have to fix ORG offset in EfiThunk.asm! Must be 0x%x\n",
293 g_cbEfiThunkBinary));
294 AssertMsg2("Fix ORG offset in EfiThunk.asm: must be 0x%x\n",
295 g_cbEfiThunkBinary);
296 break;
297
298 case EFI_PANIC_CMD_THUNK_TRAP:
299 LogRel(("EFI Panic: Unexpected trap!!\n"));
300#ifdef VBOX_STRICT
301 return PDMDeviceDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n");
302#else
303 AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
304#endif
305 break;
306
307 case EFI_PANIC_CMD_START_MSG:
308 pThis->iPanicMsg = 0;
309 pThis->szPanicMsg[0] = '\0';
310 break;
311
312 case EFI_PANIC_CMD_END_MSG:
313 LogRel(("EFI Panic: %s\n", pThis->szPanicMsg));
314#ifdef VBOX_STRICT
315 return PDMDeviceDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThis->szPanicMsg);
316#else
317 return VERR_INTERNAL_ERROR;
318#endif
319
320 default:
321 if ( u32 >= EFI_PANIC_CMD_MSG_FIRST
322 && u32 <= EFI_PANIC_CMD_MSG_LAST)
323 {
324 /* Add the message char to the buffer. */
325 uint32_t i = pThis->iPanicMsg;
326 if (i + 1 < sizeof(pThis->szPanicMsg))
327 {
328 char ch = EFI_PANIC_CMD_MSG_GET_CHAR(u32);
329 if ( ch == '\n'
330 && i > 0
331 && pThis->szPanicMsg[i - 1] == '\r')
332 i--;
333 pThis->szPanicMsg[i] = ch;
334 pThis->szPanicMsg[i + 1] = '\0';
335 pThis->iPanicMsg = i + 1;
336 }
337 }
338 else
339 Log(("EFI: Unknown panic command: %#x (cb=%d)\n", u32, cb));
340 break;
341 }
342 break;
343 }
344
345 default:
346 Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", Port, u32, cb));
347 break;
348 }
349 return VINF_SUCCESS;
350}
351
352/**
353 * Init complete notification.
354 *
355 * @returns VBOX status code.
356 * @param pDevIns The device instance.
357 */
358static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
359{
360 /* PC Bios */
361 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
362 uint32_t u32;
363
364 /*
365 * Memory sizes.
366 */
367 uint64_t const offRamHole = _4G - pThis->cbRamHole;
368 if (pThis->cbRam > 16 * _1M)
369 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
370 else
371 u32 = 0;
372 cmosWrite(pDevIns, 0x34, u32 & 0xff);
373 cmosWrite(pDevIns, 0x35, u32 >> 8);
374
375 /*
376 * Number of CPUs.
377 */
378 cmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
379
380 return VINF_SUCCESS;
381}
382
383/**
384 * Reset notification.
385 *
386 * @returns VBox status.
387 * @param pDevIns The device instance data.
388 */
389static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
390{
391 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
392
393 LogFlow(("efiReset\n"));
394
395 pThis->iInfoSelector = 0;
396 pThis->iInfoPosition = -1;
397
398 pThis->iMsg = 0;
399 pThis->szMsg[0] = '\0';
400 pThis->iPanicMsg = 0;
401 pThis->szPanicMsg[0] = '\0';
402
403 /*
404 * Re-shadow the Firmware Volume and make it RAM/RAM.
405 */
406 uint32_t cPages = RT_ALIGN_64(pThis->cbEfiRom, PAGE_SIZE) >> PAGE_SHIFT;
407 RTGCPHYS GCPhys = pThis->GCLoadAddress;
408 while (cPages > 0)
409 {
410 uint8_t abPage[PAGE_SIZE];
411 int rc;
412
413 /* Read the (original) ROM page and write it back to the RAM page. */
414 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
415 AssertLogRelRC(rc);
416
417 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
418 AssertLogRelRC(rc);
419 if (RT_FAILURE(rc))
420 memset(abPage, 0xcc, sizeof(abPage));
421
422 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
423 AssertLogRelRC(rc);
424
425 /* Switch to the RAM/RAM mode. */
426 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
427 AssertLogRelRC(rc);
428
429 /* Advance */
430 GCPhys += PAGE_SIZE;
431 cPages--;
432 }
433}
434
435/**
436 * Destruct a device instance.
437 *
438 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
439 * resources can be freed correctly.
440 *
441 * @param pDevIns The device instance data.
442 */
443static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
444{
445 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
446
447 /*
448 * Free MM heap pointers.
449 */
450 if (pThis->pu8EfiRom)
451 {
452 RTFileReadAllFree(pThis->pu8EfiRom, (size_t)pThis->cbEfiRom);
453 pThis->pu8EfiRom = NULL;
454 }
455
456 if (pThis->pszEfiRomFile)
457 {
458 MMR3HeapFree(pThis->pszEfiRomFile);
459 pThis->pszEfiRomFile = NULL;
460 }
461
462 if (pThis->pu8EfiThunk)
463 {
464 MMR3HeapFree(pThis->pu8EfiThunk);
465 pThis->pu8EfiThunk = NULL;
466 }
467
468 return VINF_SUCCESS;
469}
470
471/**
472 * Helper that searches for a FFS file of a given type.
473 *
474 * @returns Pointer to the FFS file header if found, NULL if not.
475 *
476 * @param pFfsFile Pointer to the FFS file header to start searching at.
477 * @param pbEnd The end of the firmware volume.
478 * @param FileType The file type to look for.
479 * @param pcbFfsFile Where to store the FFS file size (includes header).
480 */
481DECLINLINE(EFI_FFS_FILE_HEADER const *)
482efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
483{
484#define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0)
485 while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd)
486 {
487 if (pFfsFile->Type == FileType)
488 {
489 *pcbFile = FFS_SIZE(pFfsFile);
490 return pFfsFile;
491 }
492 pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
493 }
494 return NULL;
495}
496
497static int efiFindEntryPoint(EFI_FFS_FILE_HEADER const *pFfsFile, uint32_t cbFfsFile, RTGCPHYS *pImageBase, uint8_t **ppbImage)
498{
499 /*
500 * Sections headers are lays at the begining of block it describes,
501 * the first section header is located immidiately after FFS header.
502 */
503 EFI_FILE_SECTION_POINTER uSecHdrPtr;
504 uint8_t const * const pbFfsFileEnd = (uint8_t *)pFfsFile + cbFfsFile;
505 uint8_t const *pbImage = NULL;
506 uint8_t const *pbSecHdr = (uint8_t const *)&pFfsFile[1]; /* FFS header has fixed size */
507 for (; (uintptr_t)pbSecHdr < (uintptr_t)pbFfsFileEnd;
508 pbSecHdr += SECTION_SIZE(uSecHdrPtr.CommonHeader))
509 {
510 uSecHdrPtr.CommonHeader = (EFI_COMMON_SECTION_HEADER *)pbSecHdr;
511 if ( uSecHdrPtr.CommonHeader->Type == EFI_SECTION_PE32
512 || uSecHdrPtr.CommonHeader->Type == EFI_SECTION_TE)
513 {
514 /*Here should be other code containing sections*/
515 pbImage = (uint8_t const *)&uSecHdrPtr.Pe32Section[1]; /* the PE/PE+/TE headers begins just after the Section Header */
516 break;
517 }
518 Log2(("EFI: Section of type:%d has been detected\n", uSecHdrPtr.CommonHeader->Type));
519 }
520 AssertLogRelMsgReturn(pbImage, ("Failed to find PE32 or TE section for the SECURITY_CORE FFS\n"), VERR_INVALID_PARAMETER);
521
522 /*
523 * Parse the image extracting the ImageBase and the EntryPoint.
524 */
525 int rc = VINF_SUCCESS;
526 union EfiHdrUnion
527 {
528 EFI_IMAGE_DOS_HEADER Dos;
529 EFI_IMAGE_NT_HEADERS32 Nt32;
530 EFI_IMAGE_NT_HEADERS64 Nt64;
531 EFI_TE_IMAGE_HEADER Te;
532 } const *pHdr = (EfiHdrUnion const *)pbImage;
533
534 /* Skip MZ if found. */
535 if (pHdr->Dos.e_magic == RT_MAKE_U16('M', 'Z'))
536 {
537 uint8_t const *pbNewHdr = (uint8_t const *)pHdr + pHdr->Dos.e_lfanew;
538 AssertLogRelMsgReturn( (uintptr_t)pbNewHdr < (uintptr_t)pbFfsFileEnd
539 && (uintptr_t)pbNewHdr >= (uintptr_t)&pHdr->Dos.e_lfanew + sizeof(pHdr->Dos.e_lfanew),
540 ("%x\n", pHdr->Dos.e_lfanew),
541 VERR_BAD_EXE_FORMAT);
542 pHdr = (EfiHdrUnion const *)pbNewHdr;
543 }
544
545 RTGCPHYS ImageBase;
546 RTGCPHYS EpRVA;
547 if (pHdr->Nt32.Signature == RT_MAKE_U32_FROM_U8('P', 'E', 0, 0))
548 {
549 AssertLogRelMsgReturn( ( pHdr->Nt32.FileHeader.Machine == EFI_IMAGE_FILE_MACHINE_I386
550 && pHdr->Nt32.FileHeader.SizeOfOptionalHeader == sizeof(pHdr->Nt32.OptionalHeader))
551 || ( pHdr->Nt32.FileHeader.Machine == EFI_IMAGE_MACHINE_X64
552 && pHdr->Nt32.FileHeader.SizeOfOptionalHeader == sizeof(pHdr->Nt64.OptionalHeader)),
553 ("%x / %x\n", pHdr->Nt32.FileHeader.Machine, pHdr->Nt32.FileHeader.SizeOfOptionalHeader),
554 VERR_LDR_ARCH_MISMATCH);
555 if (pHdr->Nt32.FileHeader.Machine == EFI_IMAGE_FILE_MACHINE_I386)
556 {
557 Log2(("EFI: PE32/i386\n"));
558 AssertLogRelMsgReturn(pHdr->Nt32.OptionalHeader.SizeOfImage < cbFfsFile,
559 ("%#x / %#x\n", pHdr->Nt32.OptionalHeader.SizeOfImage, cbFfsFile),
560 VERR_BAD_EXE_FORMAT);
561 ImageBase = pHdr->Nt32.OptionalHeader.ImageBase;
562 EpRVA = pHdr->Nt32.OptionalHeader.AddressOfEntryPoint;
563 AssertLogRelMsgReturn(EpRVA < pHdr->Nt32.OptionalHeader.SizeOfImage,
564 ("%#RGp / %#x\n", EpRVA, pHdr->Nt32.OptionalHeader.SizeOfImage),
565 VERR_BAD_EXE_FORMAT);
566 }
567 else
568 {
569 Log2(("EFI: PE+/AMD64\n"));
570 AssertLogRelMsgReturn(pHdr->Nt64.OptionalHeader.SizeOfImage < cbFfsFile,
571 ("%#x / %#x\n", pHdr->Nt64.OptionalHeader.SizeOfImage, cbFfsFile),
572 VERR_BAD_EXE_FORMAT);
573 ImageBase = pHdr->Nt64.OptionalHeader.ImageBase;
574 EpRVA = pHdr->Nt64.OptionalHeader.AddressOfEntryPoint;
575 AssertLogRelMsgReturn(EpRVA < pHdr->Nt64.OptionalHeader.SizeOfImage,
576 ("%#RGp / %#x\n", EpRVA, pHdr->Nt64.OptionalHeader.SizeOfImage),
577 VERR_BAD_EXE_FORMAT);
578 }
579 }
580 else if (pHdr->Te.Signature == RT_MAKE_U16('V', 'Z'))
581 {
582 /* TE header */
583 Log2(("EFI: TE header\n"));
584 AssertLogRelMsgReturn( pHdr->Te.Machine == EFI_IMAGE_FILE_MACHINE_I386
585 || pHdr->Te.Machine == EFI_IMAGE_MACHINE_X64,
586 ("%x\n", pHdr->Te.Machine),
587 VERR_LDR_ARCH_MISMATCH);
588 ImageBase = pHdr->Te.ImageBase;
589 EpRVA = pHdr->Te.AddressOfEntryPoint;
590 AssertLogRelMsgReturn(EpRVA < cbFfsFile,
591 ("%#RGp / %#x\n", EpRVA, cbFfsFile),
592 VERR_BAD_EXE_FORMAT);
593 }
594 else
595 AssertLogRelMsgFailedReturn(("%#x\n", pHdr->Nt32.Signature), VERR_INVALID_EXE_SIGNATURE);
596
597 Log2(("EFI: EpRVA=%RGp ImageBase=%RGp EntryPoint=%RGp\n", EpRVA, ImageBase, EpRVA + ImageBase));
598 if (pImageBase != NULL)
599 *pImageBase = ImageBase;
600 if (ppbImage != NULL)
601 *ppbImage = (uint8_t *)pbImage;
602 return ImageBase + EpRVA;
603}
604
605/**
606 * Parse EFI ROM headers and find entry points.
607 *
608 * @returns VBox status.
609 * @param pThis The device instance data.
610 */
611static int efiParseFirmware(PDEVEFI pThis)
612{
613 EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThis->pu8EfiRom;
614
615 /*
616 * Validate firmware volume header.
617 */
618 AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'),
619 ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')),
620 VERR_INVALID_MAGIC);
621 AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION,
622 ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION),
623 VERR_VERSION_MISMATCH);
624 /** @todo check checksum, see PE spec vol. 3 */
625 AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThis->cbEfiRom,
626 ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThis->cbEfiRom),
627 VERR_INVALID_PARAMETER);
628 AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0
629 && pFwVolHdr->BlockMap[0].NumBlocks > 0,
630 ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks),
631 VERR_INVALID_PARAMETER);
632
633 AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
634
635 uint8_t const * const pbFwVolEnd = pThis->pu8EfiRom + pFwVolHdr->FvLength;
636
637 /*
638 * Ffs files are stored one by one, so to find SECURITY_CORE we've to
639 * search thru every one on the way.
640 */
641 uint32_t cbFfsFile = 0; /* shut up gcc */
642 EFI_FFS_FILE_HEADER const *pFfsFile = (EFI_FFS_FILE_HEADER const *)(pThis->pu8EfiRom + pFwVolHdr->HeaderLength);
643 pFfsFile = efiFwVolFindFileByType(pFfsFile, pbFwVolEnd, EFI_FV_FILETYPE_SECURITY_CORE, &cbFfsFile);
644 AssertLogRelMsgReturn(pFfsFile, ("No SECURITY_CORE found in the firmware volume\n"), VERR_FILE_NOT_FOUND);
645
646 RTGCPHYS ImageBase;
647 uint8_t *pbImage;
648 pThis->GCEntryPoint0 = efiFindEntryPoint(pFfsFile, cbFfsFile, &ImageBase, &pbImage);
649
650 /*
651 * Calc the firmware load address from the image base and validate it.
652 */
653 pThis->GCLoadAddress = ImageBase - (pbImage - pThis->pu8EfiRom);
654 AssertLogRelMsgReturn(~(pThis->GCLoadAddress & PAGE_OFFSET_MASK),
655 ("%RGp\n", pThis->GCLoadAddress),
656 VERR_INVALID_PARAMETER);
657 AssertLogRelMsgReturn(pThis->GCLoadAddress > UINT32_C(0xf0000000),
658 ("%RGp\n", pThis->GCLoadAddress),
659 VERR_OUT_OF_RANGE);
660 AssertLogRelMsgReturn( pThis->GCLoadAddress + (pThis->cbEfiRom - 1) > UINT32_C(0xf0000000)
661 && pThis->GCLoadAddress + (pThis->cbEfiRom - 1) < UINT32_C(0xffffe000),
662 ("%RGp + %RX64\n", pThis->GCLoadAddress, pThis->cbEfiRom),
663 VERR_OUT_OF_RANGE);
664
665 LogRel(("EFI: Firmware volume loading at %RGp, SEC CORE at %RGp with EP at %RGp\n",
666 pThis->GCLoadAddress, ImageBase, pThis->GCEntryPoint0));
667
668 pFfsFile = efiFwVolFindFileByType(pFfsFile, pbFwVolEnd, EFI_FV_FILETYPE_PEI_CORE, &cbFfsFile);
669 pThis->GCEntryPoint1 = efiFindEntryPoint(pFfsFile, cbFfsFile, NULL, NULL);
670 LogRel(("EFI: Firmware volume loading at %RGp, PEI CORE at with EP at %RGp\n",
671 pThis->GCLoadAddress, pThis->GCEntryPoint1));
672 return VINF_SUCCESS;
673}
674
675/**
676 * Load EFI ROM file into the memory.
677 *
678 * @returns VBox status.
679 * @param pThis The device instance data.
680 * @param pCfgHandle Configuration node handle for the device.
681 */
682static int efiLoadRom(PDEVEFI pThis, PCFGMNODE pCfgHandle)
683{
684 /*
685 * Read the entire firmware volume into memory.
686 */
687 void *pvFile;
688 size_t cbFile;
689 int rc = RTFileReadAllEx(pThis->pszEfiRomFile,
690 0 /*off*/,
691 RTFOFF_MAX /*cbMax*/,
692 RTFILE_RDALL_O_DENY_WRITE,
693 &pvFile,
694 &cbFile);
695 if (RT_FAILURE(rc))
696 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
697 N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
698 pThis->pszEfiRomFile, rc);
699 pThis->pu8EfiRom = (uint8_t *)pvFile;
700 pThis->cbEfiRom = cbFile;
701
702 /*
703 * Validate firmware volume and figure out the load address as well as the SEC entry point.
704 */
705 rc = efiParseFirmware(pThis);
706 if (RT_FAILURE(rc))
707 return PDMDevHlpVMSetError(pThis->pDevIns, rc, RT_SRC_POS,
708 N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
709 pThis->pszEfiRomFile, rc);
710
711 /*
712 * Map the firmware volume into memory as shadowed ROM.
713 */
714 /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
715 RTGCPHYS cbQuart = RT_ALIGN_64(pThis->cbEfiRom / 4, PAGE_SIZE);
716 rc = PDMDevHlpROMRegister(pThis->pDevIns,
717 pThis->GCLoadAddress,
718 cbQuart,
719 pThis->pu8EfiRom,
720 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
721 "EFI Firmware Volume");
722 if (RT_FAILURE(rc))
723 return rc;
724 rc = PDMDevHlpROMRegister(pThis->pDevIns,
725 pThis->GCLoadAddress + cbQuart,
726 cbQuart,
727 pThis->pu8EfiRom + cbQuart,
728 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
729 "EFI Firmware Volume (Part 2)");
730 if (RT_FAILURE(rc))
731 return rc;
732 rc = PDMDevHlpROMRegister(pThis->pDevIns,
733 pThis->GCLoadAddress + cbQuart * 2,
734 cbQuart,
735 pThis->pu8EfiRom + cbQuart * 2,
736 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
737 "EFI Firmware Volume (Part 3)");
738 if (RT_FAILURE(rc))
739 return rc;
740 rc = PDMDevHlpROMRegister(pThis->pDevIns,
741 pThis->GCLoadAddress + cbQuart * 3,
742 pThis->cbEfiRom - cbQuart * 3,
743 pThis->pu8EfiRom + cbQuart * 3,
744 PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY,
745 "EFI Firmware Volume (Part 4)");
746 if (RT_FAILURE(rc))
747 return rc;
748 return VINF_SUCCESS;
749}
750
751/**
752 * Patches and loads the EfiThunk ROM image.
753 *
754 * The thunk image is where the CPU starts and will switch it into
755 * 32-bit protected or long mode and invoke the SEC CORE image in the
756 * firmware volume. It also contains some static VM configuration data
757 * at the very beginning of the page, see DEVEFIINFO.
758 *
759 * @returns VBox status code.
760 * @param pThis The device instance data.
761 * @param pCfgHandle Configuration node handle for the device.
762 */
763static int efiLoadThunk(PDEVEFI pThis, PCFGMNODE pCfgHandle)
764{
765 uint8_t f64BitEntry = 0;
766 int rc;
767
768 rc = CFGMR3QueryU8Def(pCfgHandle, "64BitEntry", &f64BitEntry, 0);
769 if (RT_FAILURE (rc))
770 return PDMDEV_SET_ERROR(pThis->pDevIns, rc,
771 N_("Configuration error: Failed to read \"64BitEntry\""));
772
773 /*
774 * Make a copy of the page and set the values of the DEVEFIINFO structure
775 * found at the beginning of it.
776 */
777
778 if (f64BitEntry)
779 LogRel(("Using 64-bit EFI firmware\n"));
780
781 /* Duplicate the page so we can change it. */
782 AssertRelease(g_cbEfiThunkBinary == PAGE_SIZE);
783 pThis->pu8EfiThunk = (uint8_t *)PDMDevHlpMMHeapAlloc(pThis->pDevIns, PAGE_SIZE);
784 if (pThis->pu8EfiThunk == NULL)
785 return VERR_NO_MEMORY;
786 memcpy(pThis->pu8EfiThunk, &g_abEfiThunkBinary[0], PAGE_SIZE);
787
788 /* Fill in the info. */
789 PDEVEFIINFO pEfiInfo = (PDEVEFIINFO)pThis->pu8EfiThunk;
790 pEfiInfo->pfnFirmwareEP = (uint32_t)pThis->GCEntryPoint0;
791 //AssertRelease(pEfiInfo->pfnFirmwareEP == pThis->GCEntryPoint0);
792 pEfiInfo->HighEPAddress = 0;
793 pEfiInfo->PhysFwVol = pThis->GCLoadAddress;
794 pEfiInfo->cbFwVol = (uint32_t)pThis->cbEfiRom;
795 AssertRelease(pEfiInfo->cbFwVol == (uint32_t)pThis->cbEfiRom);
796 pEfiInfo->cbBelow4GB = pThis->cbBelow4GB;
797 pEfiInfo->cbAbove4GB = pThis->cbAbove4GB;
798 /* zeroth bit controls use of 64-bit entry point in fw */
799 pEfiInfo->fFlags = f64BitEntry ? 1 : 0;
800 pEfiInfo->cCpus = pThis->cCpus;
801 pEfiInfo->pfnPeiEP = (uint32_t)pThis->GCEntryPoint1;
802 pEfiInfo->u32Reserved2 = 0;
803
804 /* Register the page as a ROM (data will be copied). */
805 rc = PDMDevHlpROMRegister(pThis->pDevIns, UINT32_C(0xfffff000), PAGE_SIZE,
806 pThis->pu8EfiThunk,
807 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Thunk");
808 if (RT_FAILURE(rc))
809 return rc;
810
811#if 1 /** @todo this is probably not necessary. */
812 /*
813 * Map thunk page also at low address, so that real->protected mode jump code can
814 * store GDT/IDT in code segment in low memory and load them during switch to the
815 * protected mode, while being in 16-bits mode.
816 *
817 * @todo: maybe need to unregister later or place somewhere else (although could
818 * be needed during reset)
819 */
820 rc = PDMDevHlpROMRegister(pThis->pDevIns, 0xff000, PAGE_SIZE,
821 pThis->pu8EfiThunk,
822 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Thunk (2)");
823 if (RT_FAILURE(rc))
824 return rc;
825#endif
826
827 return rc;
828}
829
830
831/**
832 * Construct a device instance for a VM.
833 *
834 * @returns VBox status.
835 * @param pDevIns The device instance data.
836 * If the registration structure is needed, pDevIns->pDevReg points to it.
837 * @param iInstance Instance number. Use this to figure out which registers and such to use.
838 * The device number is also found in pDevIns->iInstance, but since it's
839 * likely to be freqently used PDM passes it as parameter.
840 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
841 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
842 * iInstance it's expected to be used a bit in this function.
843 */
844static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
845{
846 PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
847 int rc;
848
849 Assert(iInstance == 0);
850
851 pThis->pDevIns = pDevIns;
852
853 /*
854 * Validate and read the configuration.
855 */
856 if (!CFGMR3AreValuesValid(pCfgHandle,
857 "EfiRom\0"
858 "RamSize\0"
859 "RamHoleSize\0"
860 "NumCPUs\0"
861 "UUID\0"
862 "IOAPIC\0"
863 "DmiBIOSVendor\0"
864 "DmiBIOSVersion\0"
865 "DmiBIOSReleaseDate\0"
866 "DmiBIOSReleaseMajor\0"
867 "DmiBIOSReleaseMinor\0"
868 "DmiBIOSFirmwareMajor\0"
869 "DmiBIOSFirmwareMinor\0"
870 "DmiSystemFamily\0"
871 "DmiSystemProduct\0"
872 "DmiSystemSerial\0"
873 "DmiSystemUuid\0"
874 "DmiSystemVendor\0"
875 "DmiSystemVersion\0"
876 "DmiChassisVendor\0"
877 "DmiChassisVersion\0"
878 "DmiChassisSerial\0"
879 "DmiChassisAssetTag\0"
880#ifdef VBOX_WITH_DMI_OEMSTRINGS
881 "DmiOEMVBoxVer\0"
882 "DmiOEMVBoxRev\0"
883#endif
884 "64BitEntry\0"
885 ))
886 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
887 N_("Configuration error: Invalid config value(s) for the EFI device"));
888
889 rc = CFGMR3QueryU8Def(pCfgHandle, "IOAPIC", &pThis->u8IOAPIC, 1);
890 if (RT_FAILURE (rc))
891 return PDMDEV_SET_ERROR(pDevIns, rc,
892 N_("Configuration error: Failed to read \"IOAPIC\""));
893
894 /*
895 * Query the machine's UUID for SMBIOS/DMI use.
896 */
897 RTUUID uuid;
898 rc = CFGMR3QueryBytes(pCfgHandle, "UUID", &uuid, sizeof(uuid));
899 if (RT_FAILURE(rc))
900 return PDMDEV_SET_ERROR(pDevIns, rc,
901 N_("Configuration error: Querying \"UUID\" failed"));
902
903 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
904 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
905 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
906 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
907 rc = sharedfwPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &uuid, pCfgHandle);
908 if (RT_FAILURE(rc))
909 return rc;
910 if (pThis->u8IOAPIC)
911 sharedfwPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE, pThis->cCpus);
912
913 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage,
914 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
915 if (RT_FAILURE(rc))
916 return rc;
917
918 /* RAM sizes */
919 rc = CFGMR3QueryU64(pCfgHandle, "RamSize", &pThis->cbRam);
920 AssertLogRelRCReturn(rc, rc);
921 rc = CFGMR3QueryU64(pCfgHandle, "RamHoleSize", &pThis->cbRamHole);
922 AssertLogRelRCReturn(rc, rc);
923 pThis->cbBelow4GB = RT_MIN(pThis->cbRam, _4G - pThis->cbRamHole);
924 pThis->cbAbove4GB = pThis->cbRam - pThis->cbBelow4GB;
925
926 /* CPU count (optional). */
927 rc = CFGMR3QueryU32Def(pCfgHandle, "NumCPUs", &pThis->cCpus, 1);
928 AssertLogRelRCReturn(rc, rc);
929 /*
930 * Get the system EFI ROM file name.
931 */
932 rc = CFGMR3QueryStringAlloc(pCfgHandle, "EfiRom", &pThis->pszEfiRomFile);
933 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
934 {
935 pThis->pszEfiRomFile = (char*)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX);
936 if (!pThis->pszEfiRomFile)
937 return VERR_NO_MEMORY;
938
939 int rc = RTPathAppPrivateArch(pThis->pszEfiRomFile, RTPATH_MAX - 32);
940 AssertRCReturn(rc, rc);
941
942 size_t offFilename = strlen(pThis->pszEfiRomFile);
943 strcpy(pThis->pszEfiRomFile+offFilename, "/VBoxEFI32.fd");
944 rc = VINF_SUCCESS;
945 }
946 else if (RT_FAILURE(rc))
947 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
948 N_("Configuration error: Querying \"EfiRom\" as a string failed"));
949 else if (!*pThis->pszEfiRomFile)
950 {
951 MMR3HeapFree(pThis->pszEfiRomFile);
952 pThis->pszEfiRomFile = NULL;
953 }
954
955#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
956 /*
957 * Zap the debugger script
958 */
959 RTFileDelete("./DevEFI.VBoxDbg");
960#endif
961
962 /*
963 * Load firmware volume and thunk ROM.
964 */
965 rc = efiLoadRom(pThis, pCfgHandle);
966 if (RT_FAILURE(rc))
967 return rc;
968
969 rc = efiLoadThunk(pThis, pCfgHandle);
970 if (RT_FAILURE(rc))
971 return rc;
972
973 /*
974 * Register our communication ports.
975 */
976 rc = PDMDevHlpIOPortRegister(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, NULL,
977 efiIOPortWrite, efiIOPortRead,
978 NULL, NULL, "EFI communication ports");
979 if (RT_FAILURE(rc))
980 return rc;
981
982 /*
983 * Call reset to set things up.
984 */
985 efiReset(pDevIns);
986
987 return VINF_SUCCESS;
988}
989
990/**
991 * The device registration structure.
992 */
993const PDMDEVREG g_DeviceEFI =
994{
995 /* u32Version */
996 PDM_DEVREG_VERSION,
997 /* szDeviceName */
998 "efi",
999 /* szRCMod */
1000 "",
1001 /* szR0Mod */
1002 "",
1003 /* pszDescription */
1004 "Extensible Firmware Interface Device",
1005 /* fFlags */
1006 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1007 /* fClass */
1008 PDM_DEVREG_CLASS_ARCH_BIOS,
1009 /* cMaxInstances */
1010 1,
1011 /* cbInstance */
1012 sizeof(DEVEFI),
1013 /* pfnConstruct */
1014 efiConstruct,
1015 /* pfnDestruct */
1016 efiDestruct,
1017 /* pfnRelocate */
1018 NULL,
1019 /* pfnIOCtl */
1020 NULL,
1021 /* pfnPowerOn */
1022 NULL,
1023 /* pfnReset */
1024 efiReset,
1025 /* pfnSuspend */
1026 NULL,
1027 /* pfnResume */
1028 NULL,
1029 /* pfnAttach */
1030 NULL,
1031 /* pfnDetach */
1032 NULL,
1033 /* pfnQueryInterface. */
1034 NULL,
1035 /* pfnInitComplete. */
1036 efiInitComplete,
1037 /* pfnPowerOff */
1038 NULL,
1039 /* pfnSoftReset */
1040 NULL,
1041 /* u32VersionEnd */
1042 PDM_DEVREG_VERSION
1043};
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