VirtualBox

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

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

Filemuncher fixes

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