VirtualBox

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

最後變更 在這個檔案從23353是 23353,由 vboxsync 提交於 15 年 前

DevEFI.cpp: shut up gcc warning.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette