/* $Id: DevEFI.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */ /** @file * DevEFI - EFI <-> VirtualBox Integration Framework. */ /* * Copyright (C) 2006-2022 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DEV_EFI #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(DEBUG) && defined(IN_RING3) # include # define DEVEFI_WITH_VBOXDBG_SCRIPT #endif #include #include "DevEFI.h" #include "FlashCore.h" #include "VBoxDD.h" #include "VBoxDD2.h" #include "../PC/DevFwCommon.h" /* EFI includes */ #ifdef IN_RING3 # ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4668) # endif # include # ifdef _MSC_VER # pragma warning(pop) # endif # include # include # include #endif /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * The EFI device shared state structure. */ typedef struct DEVEFI { /** The flash device containing the NVRAM. */ FLASHCORE Flash; /** The 8 I/O ports at 0xEF10 (EFI_PORT_BASE). */ IOMIOPORTHANDLE hIoPorts; /** The flash MMIO handle. */ IOMMMIOHANDLE hMmioFlash; } DEVEFI; /** Pointer to the shared EFI state. */ typedef DEVEFI *PDEVEFI; /** * The EFI device state structure for ring-3. */ typedef struct DEVEFIR3 { /** Pointer back to the device instance. */ PPDMDEVINS pDevIns; /** EFI message buffer. */ char szMsg[VBOX_EFI_DEBUG_BUFFER]; /** EFI message buffer index. */ uint32_t iMsg; /** EFI panic message buffer. */ char szPanicMsg[2048]; /** EFI panic message buffer index. */ uint32_t iPanicMsg; struct { /** The current/last image event. */ uint8_t uEvt; /** Module path/name offset. */ uint8_t offName; /** The offset of the last component in the module path/name. */ uint8_t offNameLastComponent; /** Alignment padding. */ uint8_t abPadding[5]; /** First address associated with the event (image address). */ uint64_t uAddr0; /** Second address associated with the event (old image address). */ uint64_t uAddr1; /** The size associated with the event (0 if none). */ uint64_t cb0; /** The module name. */ char szName[256]; } ImageEvt; /** The system EFI ROM data. */ uint8_t const *pu8EfiRom; /** The system EFI ROM data pointer to be passed to RTFileReadAllFree. */ uint8_t *pu8EfiRomFree; /** The size of the system EFI ROM. */ uint64_t cbEfiRom; /** Offset into the actual ROM within EFI FW volume. */ uint64_t offEfiRom; /** The name of the EFI ROM file. */ char *pszEfiRomFile; /** Thunk page pointer. */ uint8_t *pu8EfiThunk; /** First entry point of the EFI firmware. */ RTGCPHYS GCEntryPoint0; /** Second Entry Point (PeiCore)*/ RTGCPHYS GCEntryPoint1; /** EFI firmware physical load address. */ RTGCPHYS GCLoadAddress; /** Current info selector. */ uint32_t iInfoSelector; /** Current info position. */ int32_t offInfo; /** Number of virtual CPUs. (Config) */ uint32_t cCpus; /** The size of the DMI tables. */ uint16_t cbDmiTables; /** Number of the DMI tables. */ uint16_t cNumDmiTables; /** The DMI tables. */ uint8_t au8DMIPage[0x1000]; /** I/O-APIC enabled? */ uint8_t u8IOAPIC; /** APIC mode to be set up by firmware. */ uint8_t u8APIC; /** Boot parameters passed to the firmware. */ char szBootArgs[256]; /** Host UUID (for DMI). */ RTUUID aUuid; /** Device properties buffer. */ R3PTRTYPE(uint8_t *) pbDeviceProps; /** Device properties buffer size. */ uint32_t cbDeviceProps; /** Virtual machine front side bus frequency. */ uint64_t u64FsbFrequency; /** Virtual machine time stamp counter frequency. */ uint64_t u64TscFrequency; /** Virtual machine CPU frequency. */ uint64_t u64CpuFrequency; /** EFI Graphics mode (used as fallback if resolution is not known). */ uint32_t u32GraphicsMode; /** EFI Graphics (GOP or UGA) horizontal resolution. */ uint32_t u32HorizontalResolution; /** EFI Graphics (GOP or UGA) vertical resolution. */ uint32_t u32VerticalResolution; /** Physical address of PCI config space MMIO region */ uint64_t u64McfgBase; /** Length of PCI config space MMIO region */ uint64_t cbMcfgLength; /** Size of the configured NVRAM device. */ uint32_t cbNvram; /** Start address of the NVRAM flash. */ RTGCPHYS GCPhysNvram; /** Filename of the file containing the NVRAM store. */ char *pszNvramFile; /** * NVRAM port - LUN\#0. */ struct { /** The base interface we provide the NVRAM driver. */ PDMIBASE IBase; /** The NVRAM driver base interface. */ PPDMIBASE pDrvBase; /** The VFS interface of the driver below for NVRAM state loading and storing. */ PPDMIVFSCONNECTOR pDrvVfs; } Lun0; } DEVEFIR3; /** Pointer to the ring-3 EFI state. */ typedef DEVEFIR3 *PDEVEFIR3; /** * The EFI device state structure for ring-0. */ typedef struct DEVEFIR0 { uint32_t uEmpty; } DEVEFIR0; /** Pointer to the ring-0 EFI state. */ typedef DEVEFIR0 *PDEVEFIR0; /** * The EFI device state structure for raw-mode. */ typedef struct DEVEFIRC { uint32_t uEmpty; } DEVEFIRC; /** Pointer to the raw-mode EFI state. */ typedef DEVEFIRC *PDEVEFIRC; /** @typedef DEVEFICC * The instance data for the current context. */ /** @typedef PDEVEFICC * Pointer to the instance data for the current context. */ #ifdef IN_RING3 typedef DEVEFIR3 DEVEFICC; typedef PDEVEFIR3 PDEVEFICC; #elif defined(IN_RING0) typedef DEVEFIR0 DEVEFICC; typedef PDEVEFIR0 PDEVEFICC; #elif defined(IN_RC) typedef DEVEFIRC DEVEFICC; typedef PDEVEFIRC PDEVEFICC; #else # error "Not IN_RING3, IN_RING0 or IN_RC" #endif /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ /** The saved state version. */ #define EFI_SSM_VERSION 3 /** The saved state version before working NVRAM support was implemented. */ #define EFI_SSM_VERSION_PRE_PROPER_NVRAM 2 /** The saved state version from VBox 4.2. */ #define EFI_SSM_VERSION_4_2 1 /** Non-volatile EFI variable. */ #define VBOX_EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001) /** Non-volatile EFI variable. */ #define VBOX_EFI_VARIABLE_READ_ONLY UINT32_C(0x00000008) /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ #ifdef IN_RING3 /** The EfiSystemNvDataFv GUID for NVRAM storage. */ static const RTUUID g_UuidNvDataFv = { { 0x8d, 0x2b, 0xf1, 0xff, 0x96, 0x76, 0x8b, 0x4c, 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50} }; # ifdef VBOX_WITH_EFI_IN_DD2 /** Special file name value for indicating the 32-bit built-in EFI firmware. */ static const char g_szEfiBuiltin32[] = "VBoxEFI32.fd"; /** Special file name value for indicating the 64-bit built-in EFI firmware. */ static const char g_szEfiBuiltin64[] = "VBoxEFI64.fd"; # endif #endif /* IN_RING3 */ #ifdef IN_RING3 /** * Gets the info item size. * * @returns Size in bytes, UINT32_MAX on error. * @param pThisCC The EFI state for the current context. */ static uint32_t efiInfoSize(PDEVEFIR3 pThisCC) { switch (pThisCC->iInfoSelector) { case EFI_INFO_INDEX_VOLUME_BASE: case EFI_INFO_INDEX_VOLUME_SIZE: case EFI_INFO_INDEX_TEMPMEM_BASE: case EFI_INFO_INDEX_TEMPMEM_SIZE: case EFI_INFO_INDEX_STACK_BASE: case EFI_INFO_INDEX_STACK_SIZE: case EFI_INFO_INDEX_GRAPHICS_MODE: case EFI_INFO_INDEX_VERTICAL_RESOLUTION: case EFI_INFO_INDEX_HORIZONTAL_RESOLUTION: return 4; case EFI_INFO_INDEX_BOOT_ARGS: return (uint32_t)RTStrNLen(pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs)) + 1; case EFI_INFO_INDEX_DEVICE_PROPS: return pThisCC->cbDeviceProps; case EFI_INFO_INDEX_FSB_FREQUENCY: case EFI_INFO_INDEX_CPU_FREQUENCY: case EFI_INFO_INDEX_TSC_FREQUENCY: case EFI_INFO_INDEX_MCFG_BASE: case EFI_INFO_INDEX_MCFG_SIZE: return 8; case EFI_INFO_INDEX_APIC_MODE: return 1; } return UINT32_MAX; } /** * efiInfoNextByte for a uint8_t value. * * @returns Next (current) byte. * @param pThisCC The EFI state for the current context. * @param u8 The value. */ static uint8_t efiInfoNextByteU8(PDEVEFIR3 pThisCC, uint8_t u8) { uint32_t off = pThisCC->offInfo; if (off >= 1) return 0; return (uint8_t)u8; } /** * efiInfoNextByte for a uint64_t value. * * @returns Next (current) byte. * @param pThisCC The EFI state for the current context. * @param u64 The value. */ static uint8_t efiInfoNextByteU64(PDEVEFIR3 pThisCC, uint64_t u64) { uint64_t off = pThisCC->offInfo; if (off >= 8) return 0; return (uint8_t)(u64 >> (off * 8)); } /** * efiInfoNextByte for a uint32_t value. * * @returns Next (current) byte. * @param pThisCC The EFI state for the current context. * @param u32 The value. */ static uint8_t efiInfoNextByteU32(PDEVEFIR3 pThisCC, uint32_t u32) { uint32_t off = pThisCC->offInfo; if (off >= 4) return 0; return (uint8_t)(u32 >> (off * 8)); } /** * efiInfoNextByte for a buffer. * * @returns Next (current) byte. * @param pThisCC The EFI state for the current context. * @param pvBuf The buffer. * @param cbBuf The buffer size. */ static uint8_t efiInfoNextByteBuf(PDEVEFIR3 pThisCC, void const *pvBuf, size_t cbBuf) { uint32_t off = pThisCC->offInfo; if (off >= cbBuf) return 0; return ((uint8_t const *)pvBuf)[off]; } /** * Gets the next info byte. * * @returns Next (current) byte. * @param pThisCC The EFI state for the current context. */ static uint8_t efiInfoNextByte(PDEVEFIR3 pThisCC) { switch (pThisCC->iInfoSelector) { case EFI_INFO_INDEX_VOLUME_BASE: return efiInfoNextByteU64(pThisCC, pThisCC->GCLoadAddress); case EFI_INFO_INDEX_VOLUME_SIZE: return efiInfoNextByteU64(pThisCC, pThisCC->cbEfiRom); case EFI_INFO_INDEX_TEMPMEM_BASE: return efiInfoNextByteU32(pThisCC, VBOX_EFI_TOP_OF_STACK); /* just after stack */ case EFI_INFO_INDEX_TEMPMEM_SIZE: return efiInfoNextByteU32(pThisCC, _512K); case EFI_INFO_INDEX_FSB_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64FsbFrequency); case EFI_INFO_INDEX_TSC_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64TscFrequency); case EFI_INFO_INDEX_CPU_FREQUENCY: return efiInfoNextByteU64(pThisCC, pThisCC->u64CpuFrequency); case EFI_INFO_INDEX_BOOT_ARGS: return efiInfoNextByteBuf(pThisCC, pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs)); case EFI_INFO_INDEX_DEVICE_PROPS: return efiInfoNextByteBuf(pThisCC, pThisCC->pbDeviceProps, pThisCC->cbDeviceProps); case EFI_INFO_INDEX_GRAPHICS_MODE: return efiInfoNextByteU32(pThisCC, pThisCC->u32GraphicsMode); case EFI_INFO_INDEX_HORIZONTAL_RESOLUTION: return efiInfoNextByteU32(pThisCC, pThisCC->u32HorizontalResolution); case EFI_INFO_INDEX_VERTICAL_RESOLUTION: return efiInfoNextByteU32(pThisCC, pThisCC->u32VerticalResolution); /* Keep in sync with value in EfiThunk.asm */ case EFI_INFO_INDEX_STACK_BASE: return efiInfoNextByteU32(pThisCC, VBOX_EFI_TOP_OF_STACK - _128K); /* 2M - 128 K */ case EFI_INFO_INDEX_STACK_SIZE: return efiInfoNextByteU32(pThisCC, _128K); case EFI_INFO_INDEX_MCFG_BASE: return efiInfoNextByteU64(pThisCC, pThisCC->u64McfgBase); case EFI_INFO_INDEX_MCFG_SIZE: return efiInfoNextByteU64(pThisCC, pThisCC->cbMcfgLength); case EFI_INFO_INDEX_APIC_MODE: return efiInfoNextByteU8(pThisCC, pThisCC->u8APIC); default: PDMDevHlpDBGFStop(pThisCC->pDevIns, RT_SRC_POS, "%#x", pThisCC->iInfoSelector); return 0; } } #ifdef IN_RING3 static void efiVBoxDbgScript(const char *pszFormat, ...) { # ifdef DEVEFI_WITH_VBOXDBG_SCRIPT PRTSTREAM pStrm; int rc2 = RTStrmOpen("./DevEFI.VBoxDbg", "a", &pStrm); if (RT_SUCCESS(rc2)) { va_list va; va_start(va, pszFormat); RTStrmPrintfV(pStrm, pszFormat, va); va_end(va); RTStrmClose(pStrm); } # else RT_NOREF(pszFormat); # endif } #endif /* IN_RING3 */ /** * Handles writes to the image event port. * * @returns VBox status suitable for I/O port write handler. * * @param pThisCC The EFI state for the current context. * @param u32 The value being written. * @param cb The size of the value. */ static int efiPortImageEventWrite(PDEVEFIR3 pThisCC, uint32_t u32, unsigned cb) { RT_NOREF(cb); switch (u32 & EFI_IMAGE_EVT_CMD_MASK) { case EFI_IMAGE_EVT_CMD_START_LOAD32: case EFI_IMAGE_EVT_CMD_START_LOAD64: case EFI_IMAGE_EVT_CMD_START_UNLOAD32: case EFI_IMAGE_EVT_CMD_START_UNLOAD64: case EFI_IMAGE_EVT_CMD_START_RELOC32: case EFI_IMAGE_EVT_CMD_START_RELOC64: AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0); /* Reset the state. */ RT_ZERO(pThisCC->ImageEvt); pThisCC->ImageEvt.uEvt = (uint8_t)u32; Assert(pThisCC->ImageEvt.uEvt == u32); return VINF_SUCCESS; case EFI_IMAGE_EVT_CMD_COMPLETE: { # ifdef IN_RING3 AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0); /* For now, just log it. */ static uint64_t s_cImageEvtLogged = 0; if (s_cImageEvtLogged < 2048) { s_cImageEvtLogged++; switch (pThisCC->ImageEvt.uEvt) { /* ASSUMES the name ends with .pdb and the image file ends with .efi! */ case EFI_IMAGE_EVT_CMD_START_LOAD32: LogRel(("EFI: VBoxDbg> loadimage32 '%.*s.efi' %#llx LB %#llx\n", pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent, &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent], pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0)); if (pThisCC->ImageEvt.offName > 4) efiVBoxDbgScript("loadimage32 '%.*s.efi' %#llx\n", pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent, &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent], pThisCC->ImageEvt.uAddr0); break; case EFI_IMAGE_EVT_CMD_START_LOAD64: LogRel(("EFI: VBoxDbg> loadimage64 '%.*s.efi' %#llx LB %#llx\n", pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent, &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent], pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0)); if (pThisCC->ImageEvt.offName > 4) efiVBoxDbgScript("loadimage64 '%.*s.efi' %#llx\n", pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent, &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent], pThisCC->ImageEvt.uAddr0); break; case EFI_IMAGE_EVT_CMD_START_UNLOAD32: case EFI_IMAGE_EVT_CMD_START_UNLOAD64: { LogRel(("EFI: VBoxDbg> unload '%.*s.efi' # %#llx LB %#llx\n", pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent, &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent], pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.cb0)); if (pThisCC->ImageEvt.offName > 4) efiVBoxDbgScript("unload '%.*s.efi'\n", pThisCC->ImageEvt.offName - 4 - pThisCC->ImageEvt.offNameLastComponent, &pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offNameLastComponent]); break; } case EFI_IMAGE_EVT_CMD_START_RELOC32: case EFI_IMAGE_EVT_CMD_START_RELOC64: { LogRel(("EFI: relocate module to %#llx from %#llx\n", pThisCC->ImageEvt.uAddr0, pThisCC->ImageEvt.uAddr1)); break; } } } return VINF_SUCCESS; # else return VINF_IOM_R3_IOPORT_WRITE; # endif } case EFI_IMAGE_EVT_CMD_ADDR0: AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX); pThisCC->ImageEvt.uAddr0 <<= 16; pThisCC->ImageEvt.uAddr0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32); return VINF_SUCCESS; case EFI_IMAGE_EVT_CMD_ADDR1: AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX); pThisCC->ImageEvt.uAddr1 <<= 16; pThisCC->ImageEvt.uAddr1 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32); return VINF_SUCCESS; case EFI_IMAGE_EVT_CMD_SIZE0: AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= UINT16_MAX); pThisCC->ImageEvt.cb0 <<= 16; pThisCC->ImageEvt.cb0 |= EFI_IMAGE_EVT_GET_PAYLOAD_U16(u32); return VINF_SUCCESS; case EFI_IMAGE_EVT_CMD_NAME: AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) <= 0x7f); if (pThisCC->ImageEvt.offName < sizeof(pThisCC->ImageEvt.szName) - 1) { char ch = EFI_IMAGE_EVT_GET_PAYLOAD_U8(u32); if (ch == '\\') ch = '/'; pThisCC->ImageEvt.szName[pThisCC->ImageEvt.offName++] = ch; if (ch == '/' || ch == ':') pThisCC->ImageEvt.offNameLastComponent = pThisCC->ImageEvt.offName; } else Log(("EFI: Image name overflow\n")); return VINF_SUCCESS; } Log(("EFI: Unknown image event: %#x (cb=%d)\n", u32, cb)); return VINF_SUCCESS; } /** * @callback_method_impl{FNIOMIOPORTNEWIN} * * @note The @a offPort parameter is absolute! */ static DECLCALLBACK(VBOXSTRICTRC) efiR3IoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb) { RT_NOREF(pvUser); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); Log4(("EFI in: %x %x\n", offPort, cb)); switch (offPort) { case EFI_INFO_PORT: if (pThisCC->offInfo == -1 && cb == 4) { pThisCC->offInfo = 0; uint32_t cbInfo = *pu32 = efiInfoSize(pThisCC); if (cbInfo == UINT32_MAX) return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "iInfoSelector=%#x (%d)\n", pThisCC->iInfoSelector, pThisCC->iInfoSelector); } else { if (cb != 1) return VERR_IOM_IOPORT_UNUSED; *pu32 = efiInfoNextByte(pThisCC); pThisCC->offInfo++; } return VINF_SUCCESS; case EFI_PANIC_PORT: # ifdef IN_RING3 LogRel(("EFI panic port read!\n")); /* Insert special code here on panic reads */ return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: panic port read!\n"); # else /* Reschedule to R3 */ return VINF_IOM_R3_IOPORT_READ; # endif case EFI_PORT_VARIABLE_OP: /* Obsolete */ case EFI_PORT_VARIABLE_PARAM: case EFI_PORT_DEBUG_POINT: case EFI_PORT_IMAGE_EVENT: *pu32 = UINT32_MAX; return VINF_SUCCESS; } return VERR_IOM_IOPORT_UNUSED; } /** * Translates a debug point value into a string for logging. * * @returns read-only string * @param enmDbgPoint Valid debug point value. */ static const char *efiDbgPointName(EFIDBGPOINT enmDbgPoint) { switch (enmDbgPoint) { case EFIDBGPOINT_SEC_PREMEM: return "SEC_PREMEM"; case EFIDBGPOINT_SEC_POSTMEM: return "SEC_POSTMEM"; case EFIDBGPOINT_DXE_CORE: return "DXE_CORE"; case EFIDBGPOINT_SMM: return "SMM"; case EFIDBGPOINT_SMI_ENTER: return "SMI_ENTER"; case EFIDBGPOINT_SMI_EXIT: return "SMI_EXIT"; case EFIDBGPOINT_GRAPHICS: return "GRAPHICS"; case EFIDBGPOINT_DXE_AP: return "DXE_AP"; default: AssertFailed(); return "Unknown"; } } /** * @callback_method_impl{FNIOMIOPORTNEWOUT} * * @note The @a offPort parameter is absolute! */ static DECLCALLBACK(VBOXSTRICTRC) efiR3IoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb) { RT_NOREF(pvUser); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); VBOXSTRICTRC rc = VINF_SUCCESS; Log4(("efi: out %x %x %d\n", offPort, u32, cb)); switch (offPort) { case EFI_INFO_PORT: Log2(("EFI_INFO_PORT: iInfoSelector=%#x\n", u32)); pThisCC->iInfoSelector = u32; pThisCC->offInfo = -1; break; case EFI_DEBUG_PORT: { /* The raw version. */ switch (u32) { case '\r': Log3(("efi: \n")); break; case '\n': Log3(("efi: \n")); break; case '\t': Log3(("efi: \n")); break; default: Log3(("efi: %c (%02x)\n", u32, u32)); break; } /* The readable, buffered version. */ if (u32 == '\n' || u32 == '\r') { Assert(pThisCC->iMsg < sizeof(pThisCC->szMsg)); pThisCC->szMsg[pThisCC->iMsg] = '\0'; if (pThisCC->iMsg) LogRel2(("efi: %s\n", pThisCC->szMsg)); pThisCC->iMsg = 0; } else { if (pThisCC->iMsg >= sizeof(pThisCC->szMsg) - 1) { pThisCC->szMsg[pThisCC->iMsg] = '\0'; LogRel2(("efi: %s\n", pThisCC->szMsg)); pThisCC->iMsg = 0; } pThisCC->szMsg[pThisCC->iMsg] = (char)u32; pThisCC->szMsg[++pThisCC->iMsg] = '\0'; } break; } case EFI_PANIC_PORT: { switch (u32) { case EFI_PANIC_CMD_BAD_ORG: /* Legacy */ case EFI_PANIC_CMD_THUNK_TRAP: #ifdef IN_RING3 LogRel(("EFI: Panic! Unexpected trap!!\n")); # ifdef VBOX_STRICT return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n"); # else AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n")); # endif break; #else return VINF_IOM_R3_IOPORT_WRITE; #endif case EFI_PANIC_CMD_START_MSG: LogRel(("Receiving EFI panic...\n")); pThisCC->iPanicMsg = 0; pThisCC->szPanicMsg[0] = '\0'; break; case EFI_PANIC_CMD_END_MSG: #ifdef IN_RING3 LogRel(("EFI: Panic! %s\n", pThisCC->szPanicMsg)); # ifdef VBOX_STRICT return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThisCC->szPanicMsg); # else return VERR_INTERNAL_ERROR; # endif #else return VINF_IOM_R3_IOPORT_WRITE; #endif default: if ( u32 >= EFI_PANIC_CMD_MSG_FIRST && u32 <= EFI_PANIC_CMD_MSG_LAST) { /* Add the message char to the buffer. */ uint32_t i = pThisCC->iPanicMsg; if (i + 1 < sizeof(pThisCC->szPanicMsg)) { char ch = EFI_PANIC_CMD_MSG_GET_CHAR(u32); if ( ch == '\n' && i > 0 && pThisCC->szPanicMsg[i - 1] == '\r') i--; pThisCC->szPanicMsg[i] = ch; pThisCC->szPanicMsg[i + 1] = '\0'; pThisCC->iPanicMsg = i + 1; } } else Log(("EFI: Unknown panic command: %#x (cb=%d)\n", u32, cb)); break; } break; } case EFI_PORT_VARIABLE_OP: case EFI_PORT_VARIABLE_PARAM: { /* Ignore access to the obsolete variable handling port. */ Log(("EFI: Write to obsolete variable handling port %RTiop: %#x (cb=%d)\n", offPort, u32, cb)); break; } case EFI_PORT_DEBUG_POINT: # ifdef IN_RING3 if (u32 > EFIDBGPOINT_INVALID && u32 < EFIDBGPOINT_END) { /* For now, just log it. */ LogRelMax(1024, ("EFI: debug point %s\n", efiDbgPointName((EFIDBGPOINT)u32))); rc = VINF_SUCCESS; } else rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Invalid debug point %#x\n", u32); break; # else return VINF_IOM_R3_IOPORT_WRITE; # endif case EFI_PORT_IMAGE_EVENT: rc = efiPortImageEventWrite(pThisCC, u32, cb); break; default: Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", offPort, u32, cb)); break; } return rc; } #endif /* IN_RING3 */ /** * @callback_method_impl{FNIOMMMIONEWWRITE, Flash memory write} */ static DECLCALLBACK(VBOXSTRICTRC) efiR3NvMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb) { PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); RT_NOREF(pvUser); return flashWrite(&pThis->Flash, off, pv, cb); } /** * @callback_method_impl{FNIOMMMIONEWREAD, Flash memory read} */ static DECLCALLBACK(VBOXSTRICTRC) efiR3NvMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb) { PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); RT_NOREF(pvUser); return flashRead(&pThis->Flash, off, pv, cb); } #ifdef IN_RING3 static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); LogFlow(("efiSaveExec:\n")); return flashR3SaveExec(&pThis->Flash, pDevIns, pSSM); } static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) { PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3; LogFlow(("efiLoadExec: uVersion=%d uPass=%d\n", uVersion, uPass)); /* * Validate input. */ if (uPass != SSM_PASS_FINAL) return VERR_SSM_UNEXPECTED_PASS; if ( uVersion != EFI_SSM_VERSION && uVersion != EFI_SSM_VERSION_PRE_PROPER_NVRAM && uVersion != EFI_SSM_VERSION_4_2 ) return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; int rc; if (uVersion > EFI_SSM_VERSION_PRE_PROPER_NVRAM) rc = flashR3LoadExec(&pThis->Flash, pDevIns, pSSM); else { /* * Ignore the old NVRAM state. */ rc = pHlp->pfnSSMSkipToEndOfUnit(pSSM); } return rc; } /** * @copydoc(PDMIBASE::pfnQueryInterface) */ static DECLCALLBACK(void *) devEfiQueryInterface(PPDMIBASE pInterface, const char *pszIID) { LogFlowFunc(("ENTER: pIBase=%p pszIID=%p\n", pInterface, pszIID)); PDEVEFIR3 pThisCC = RT_FROM_MEMBER(pInterface, DEVEFIR3, Lun0.IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->Lun0.IBase); return NULL; } /** * Write to CMOS memory. * This is used by the init complete code. */ static void cmosWrite(PPDMDEVINS pDevIns, unsigned off, uint32_t u32Val) { Assert(off < 128); Assert(u32Val < 256); int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val); AssertRC(rc); } /** * Init complete notification. * * @returns VBOX status code. * @param pDevIns The device instance. */ static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns) { PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); uint64_t const cbRamSize = PDMDevHlpMMPhysGetRamSize(pDevIns); uint32_t const cbBelow4GB = PDMDevHlpMMPhysGetRamSizeBelow4GB(pDevIns); uint64_t const cbAbove4GB = PDMDevHlpMMPhysGetRamSizeAbove4GB(pDevIns); NOREF(cbAbove4GB); /* * Memory sizes. */ uint32_t u32Low = 0; uint32_t u32Chunks = 0; if (cbRamSize > 16 * _1M) { u32Low = RT_MIN(cbBelow4GB, UINT32_C(0xfe000000)); u32Chunks = (u32Low - 16U * _1M) / _64K; } cmosWrite(pDevIns, 0x34, RT_BYTE1(u32Chunks)); cmosWrite(pDevIns, 0x35, RT_BYTE2(u32Chunks)); if (u32Low < cbRamSize) { uint64_t u64 = cbRamSize - u32Low; u32Chunks = (uint32_t)(u64 / _64K); cmosWrite(pDevIns, 0x5b, RT_BYTE1(u32Chunks)); cmosWrite(pDevIns, 0x5c, RT_BYTE2(u32Chunks)); cmosWrite(pDevIns, 0x5d, RT_BYTE3(u32Chunks)); cmosWrite(pDevIns, 0x5e, RT_BYTE4(u32Chunks)); } /* * Number of CPUs. */ cmosWrite(pDevIns, 0x60, pThisCC->cCpus & 0xff); return VINF_SUCCESS; } /** * @interface_method_impl{PDMDEVREG,pfnMemSetup} */ static DECLCALLBACK(void) efiMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx) { RT_NOREF(enmCtx); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); /* * Re-shadow the Firmware Volume and make it RAM/RAM. */ uint32_t cPages = RT_ALIGN_64(pThisCC->cbEfiRom, PAGE_SIZE) >> PAGE_SHIFT; RTGCPHYS GCPhys = pThisCC->GCLoadAddress; while (cPages > 0) { uint8_t abPage[PAGE_SIZE]; /* Read the (original) ROM page and write it back to the RAM page. */ int rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM); AssertLogRelRC(rc); rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE); AssertLogRelRC(rc); if (RT_FAILURE(rc)) memset(abPage, 0xcc, sizeof(abPage)); rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE); AssertLogRelRC(rc); /* Switch to the RAM/RAM mode. */ rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM); AssertLogRelRC(rc); /* Advance */ GCPhys += PAGE_SIZE; cPages--; } } /** * @interface_method_impl{PDMDEVREG,pfnReset} */ static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns) { PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); LogFlow(("efiReset\n")); pThisCC->iInfoSelector = 0; pThisCC->offInfo = -1; pThisCC->iMsg = 0; pThisCC->szMsg[0] = '\0'; pThisCC->iPanicMsg = 0; pThisCC->szPanicMsg[0] = '\0'; flashR3Reset(&pThis->Flash); #ifdef DEVEFI_WITH_VBOXDBG_SCRIPT /* * Zap the debugger script */ RTFileDelete("./DevEFI.VBoxDbg"); #endif } /** * @interface_method_impl{PDMDEVREG,pfnPowerOff} */ static DECLCALLBACK(void) efiPowerOff(PPDMDEVINS pDevIns) { PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); if (pThisCC->Lun0.pDrvVfs) { int rc = flashR3SaveToVfs(&pThis->Flash, pDevIns, pThisCC->Lun0.pDrvVfs, pDevIns->pReg->szName, "nvram"); if (RT_FAILURE(rc)) LogRel(("EFI: Failed to save flash file to NVRAM store: %Rrc\n", rc)); } else if (pThisCC->pszNvramFile) { int rc = flashR3SaveToFile(&pThis->Flash, pDevIns, pThisCC->pszNvramFile); if (RT_FAILURE(rc)) LogRel(("EFI: Failed to save flash file to '%s': %Rrc\n", pThisCC->pszNvramFile, rc)); } } /** * Destruct a device instance. * * Most VM resources are freed by the VM. This callback is provided so that any non-VM * resources can be freed correctly. * * @param pDevIns The device instance data. */ static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns) { PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); flashR3Destruct(&pThis->Flash, pDevIns); if (pThisCC->pszNvramFile) { PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszNvramFile); pThisCC->pszNvramFile = NULL; } if (pThisCC->pu8EfiRomFree) { RTFileReadAllFree(pThisCC->pu8EfiRomFree, (size_t)pThisCC->cbEfiRom + pThisCC->offEfiRom); pThisCC->pu8EfiRomFree = NULL; } /* * Free MM heap pointers (waste of time, but whatever). */ if (pThisCC->pszEfiRomFile) { PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszEfiRomFile); pThisCC->pszEfiRomFile = NULL; } if (pThisCC->pu8EfiThunk) { PDMDevHlpMMHeapFree(pDevIns, pThisCC->pu8EfiThunk); pThisCC->pu8EfiThunk = NULL; } if (pThisCC->pbDeviceProps) { PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbDeviceProps); pThisCC->pbDeviceProps = NULL; pThisCC->cbDeviceProps = 0; } return VINF_SUCCESS; } #if 0 /* unused */ /** * Helper that searches for a FFS file of a given type. * * @returns Pointer to the FFS file header if found, NULL if not. * * @param pFfsFile Pointer to the FFS file header to start searching at. * @param pbEnd The end of the firmware volume. * @param FileType The file type to look for. * @param pcbFfsFile Where to store the FFS file size (includes header). */ DECLINLINE(EFI_FFS_FILE_HEADER const *) efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile) { # define FFS_SIZE(hdr) RT_MAKE_U32_FROM_U8((hdr)->Size[0], (hdr)->Size[1], (hdr)->Size[2], 0) while ((uintptr_t)pFfsFile < (uintptr_t)pbEnd) { if (pFfsFile->Type == FileType) { *pcbFile = FFS_SIZE(pFfsFile); LogFunc(("Found %RTuuid of type:%d\n", &pFfsFile->Name, FileType)); return pFfsFile; } pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8)); } # undef FFS_SIZE return NULL; } #endif /* unused */ /** * Parse EFI ROM headers and find entry points. * * @returns VBox status code. * @param pDevIns The device instance. * @param pThis The shared device state. * @param pThisCC The device state for the current context. */ static int efiParseFirmware(PPDMDEVINS pDevIns, PDEVEFI pThis, PDEVEFIR3 pThisCC) { EFI_FIRMWARE_VOLUME_HEADER const *pFwVolHdr = (EFI_FIRMWARE_VOLUME_HEADER const *)pThisCC->pu8EfiRom; /* * Validate firmware volume header. */ AssertLogRelMsgReturn(pFwVolHdr->Signature == RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H'), ("%#x, expected %#x\n", pFwVolHdr->Signature, RT_MAKE_U32_FROM_U8('_', 'F', 'V', 'H')), VERR_INVALID_MAGIC); AssertLogRelMsgReturn(pFwVolHdr->Revision == EFI_FVH_REVISION, ("%#x, expected %#x\n", pFwVolHdr->Signature, EFI_FVH_REVISION), VERR_VERSION_MISMATCH); /** @todo check checksum, see PE spec vol. 3 */ AssertLogRelMsgReturn(pFwVolHdr->FvLength <= pThisCC->cbEfiRom, ("%#llx, expected %#llx\n", pFwVolHdr->FvLength, pThisCC->cbEfiRom), VERR_INVALID_PARAMETER); AssertLogRelMsgReturn( pFwVolHdr->BlockMap[0].Length > 0 && pFwVolHdr->BlockMap[0].NumBlocks > 0, ("%#x, %x\n", pFwVolHdr->BlockMap[0].Length, pFwVolHdr->BlockMap[0].NumBlocks), VERR_INVALID_PARAMETER); AssertLogRelMsgReturn(!(pThisCC->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThisCC->cbEfiRom), VERR_INVALID_PARAMETER); LogRel(("Found EFI FW Volume, %u bytes (%u %u-byte blocks)\n", pFwVolHdr->FvLength, pFwVolHdr->BlockMap[0].NumBlocks, pFwVolHdr->BlockMap[0].Length)); /** @todo Make this more dynamic, this assumes that the NV storage area comes first (always the case for our builds). */ AssertLogRelMsgReturn(!memcmp(&pFwVolHdr->FileSystemGuid, &g_UuidNvDataFv, sizeof(g_UuidNvDataFv)), ("Expected EFI_SYSTEM_NV_DATA_FV_GUID as an identifier"), VERR_INVALID_MAGIC); /* Found NVRAM storage, configure flash device. */ pThisCC->offEfiRom = pFwVolHdr->FvLength; pThisCC->cbNvram = pFwVolHdr->FvLength; pThisCC->GCPhysNvram = UINT32_C(0xfffff000) - pThisCC->cbEfiRom + PAGE_SIZE; pThisCC->cbEfiRom -= pThisCC->cbNvram; int rc = flashR3Init(&pThis->Flash, pThisCC->pDevIns, 0xA289 /*Intel*/, pThisCC->cbNvram, pFwVolHdr->BlockMap[0].Length); if (RT_FAILURE(rc)) return rc; if (pThisCC->Lun0.pDrvVfs) { rc = flashR3LoadFromVfs(&pThis->Flash, pDevIns, pThisCC->Lun0.pDrvVfs, pDevIns->pReg->szName, "nvram"); if (rc == VERR_NOT_FOUND) { /* Initialize the NVRAM content from the loaded ROM file as the NVRAM wasn't initialized yet. */ rc = flashR3LoadFromBuf(&pThis->Flash, pThisCC->pu8EfiRom, pThisCC->cbNvram); } else if (RT_FAILURE(rc)) return rc; } else { /* If the file does not exist we initialize the NVRAM from the loaded ROM file. */ if (!pThisCC->pszNvramFile || !RTPathExists(pThisCC->pszNvramFile)) rc = flashR3LoadFromBuf(&pThis->Flash, pThisCC->pu8EfiRom, pThisCC->cbNvram); else rc = flashR3LoadFromFile(&pThis->Flash, pDevIns, pThisCC->pszNvramFile); if (RT_FAILURE(rc)) return rc; } pThisCC->GCLoadAddress = pThisCC->GCPhysNvram + pThisCC->cbNvram; return VINF_SUCCESS; } /** * Load EFI ROM file into the memory. * * @returns VBox status code. * @param pDevIns The device instance. * @param pThis The shared Efi state. * @param pThisCC The device state for the current context. * @param pCfg Configuration node handle for the device. */ static int efiLoadRom(PPDMDEVINS pDevIns, PDEVEFI pThis, PDEVEFIR3 pThisCC, PCFGMNODE pCfg) { RT_NOREF(pCfg); /* * Read the entire firmware volume into memory. */ int rc; #ifdef VBOX_WITH_EFI_IN_DD2 if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltin32) == 0) { pThisCC->pu8EfiRomFree = NULL; pThisCC->pu8EfiRom = g_abEfiFirmware32; pThisCC->cbEfiRom = g_cbEfiFirmware32; } else if (RTStrCmp(pThisCC->pszEfiRomFile, g_szEfiBuiltin64) == 0) { pThisCC->pu8EfiRomFree = NULL; pThisCC->pu8EfiRom = g_abEfiFirmware64; pThisCC->cbEfiRom = g_cbEfiFirmware64; } else #endif { void *pvFile; size_t cbFile; rc = RTFileReadAllEx(pThisCC->pszEfiRomFile, 0 /*off*/, RTFOFF_MAX /*cbMax*/, RTFILE_RDALL_O_DENY_WRITE, &pvFile, &cbFile); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"), pThisCC->pszEfiRomFile, rc); pThisCC->pu8EfiRomFree = (uint8_t *)pvFile; pThisCC->pu8EfiRom = (uint8_t *)pvFile; pThisCC->cbEfiRom = cbFile; } /* * Validate firmware volume and figure out the load address as well as the SEC entry point. */ rc = efiParseFirmware(pDevIns, pThis, pThisCC); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"), pThisCC->pszEfiRomFile, rc); /* * Map the firmware volume into memory as shadowed ROM. * * This is a little complicated due to saved state legacy. We used to have a * 2MB image w/o any flash portion, divided into four 512KB mappings. * * We've now increased the size of the firmware to 4MB, but for saved state * compatibility reasons need to use the same mappings and names (!!) for the * top 2MB. */ /** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */ #if 1 static const char * const s_apszNames[16] = { "EFI Firmware Volume", "EFI Firmware Volume (Part 2)", "EFI Firmware Volume (Part 3)", "EFI Firmware Volume (Part 4)", "EFI Firmware Volume (Part 5)", "EFI Firmware Volume (Part 6)", "EFI Firmware Volume (Part 7)", "EFI Firmware Volume (Part 8)", "EFI Firmware Volume (Part 9)", "EFI Firmware Volume (Part 10)", "EFI Firmware Volume (Part 11)", "EFI Firmware Volume (Part 12)", "EFI Firmware Volume (Part 13)", "EFI Firmware Volume (Part 14)", "EFI Firmware Volume (Part 15)", "EFI Firmware Volume (Part 16)", }; AssertLogRelMsgReturn(pThisCC->cbEfiRom < RT_ELEMENTS(s_apszNames) * _512K, ("EFI firmware image too big: %#RX64, max %#zx\n", pThisCC->cbEfiRom, RT_ELEMENTS(s_apszNames) * _512K), VERR_IMAGE_TOO_BIG); uint32_t const cbChunk = pThisCC->cbNvram + pThisCC->cbEfiRom >= _2M ? _512K : (uint32_t)RT_ALIGN_64((pThisCC->cbNvram + pThisCC->cbEfiRom) / 4, PAGE_SIZE); uint32_t cbLeft = pThisCC->cbEfiRom; /* ASSUMES NVRAM comes first! */ uint32_t off = pThisCC->offEfiRom + cbLeft; /* ASSUMES NVRAM comes first! */ RTGCPHYS64 GCPhys = pThisCC->GCLoadAddress + cbLeft; AssertLogRelMsg(GCPhys == _4G, ("%RGp\n", GCPhys)); /* Compatibility mappings at the top (note that this isn't entirely the same algorithm, but it will produce the same results for a power of two sized image): */ unsigned i = 4; while (i-- > 0) { uint32_t const cb = RT_MIN(cbLeft, cbChunk); cbLeft -= cb; GCPhys -= cb; off -= cb; rc = PDMDevHlpROMRegister(pDevIns, GCPhys, cb, pThisCC->pu8EfiRom + off, cb, PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, s_apszNames[i]); AssertRCReturn(rc, rc); } /* The rest (if any) is mapped in descending order of address and increasing name order: */ if (cbLeft > 0) { Assert(cbChunk == _512K); for (i = 4; cbLeft > 0; i++) { uint32_t const cb = RT_MIN(cbLeft, cbChunk); cbLeft -= cb; GCPhys -= cb; off -= cb; /** @todo Add flag to prevent saved state loading from bitching about these regions. */ rc = PDMDevHlpROMRegister(pDevIns, GCPhys, cb, pThisCC->pu8EfiRom + off, cb, PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY | PGMPHYS_ROM_FLAGS_MAYBE_MISSING_FROM_STATE, s_apszNames[i]); AssertRCReturn(rc, rc); } Assert(i <= RT_ELEMENTS(s_apszNames)); } /* Not sure what the purpose of this one is... */ rc = PDMDevHlpROMProtectShadow(pDevIns, pThisCC->GCLoadAddress, (uint32_t)cbChunk, PGMROMPROT_READ_RAM_WRITE_IGNORE); AssertRCReturn(rc, rc); #else RTGCPHYS cbQuart = RT_ALIGN_64(pThisCC->cbEfiRom / 4, PAGE_SIZE); rc = PDMDevHlpROMRegister(pDevIns, pThisCC->GCLoadAddress, cbQuart, pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs, cbQuart, PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Firmware Volume"); AssertRCReturn(rc, rc); rc = PDMDevHlpROMProtectShadow(pDevIns, pThisCC->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE); AssertRCReturn(rc, rc); rc = PDMDevHlpROMRegister(pDevIns, pThisCC->GCLoadAddress + cbQuart, cbQuart, pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart, cbQuart, PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Firmware Volume (Part 2)"); if (RT_FAILURE(rc)) return rc; rc = PDMDevHlpROMRegister(pDevIns, pThisCC->GCLoadAddress + cbQuart * 2, cbQuart, pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart * 2, cbQuart, PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Firmware Volume (Part 3)"); if (RT_FAILURE(rc)) return rc; rc = PDMDevHlpROMRegister(pDevIns, pThisCC->GCLoadAddress + cbQuart * 3, pThisCC->cbEfiRom - cbQuart * 3, pThisCC->pu8EfiRom + pThisCC->uEfiRomOfs + cbQuart * 3, pThisCC->cbEfiRom - cbQuart * 3, PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Firmware Volume (Part 4)"); if (RT_FAILURE(rc)) return rc; #endif /* * Register MMIO region for flash device. */ rc = PDMDevHlpMmioCreateEx(pDevIns, pThisCC->cbNvram, IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, NULL /*pPciDev*/, UINT32_MAX, efiR3NvMmioWrite, efiR3NvMmioRead, NULL, NULL /*pvUser*/, "Flash Memory", &pThis->hMmioFlash); AssertRCReturn(rc, rc); rc = PDMDevHlpMmioMap(pDevIns, pThis->hMmioFlash, pThisCC->GCPhysNvram); AssertRCReturn(rc, rc); LogRel(("EFI: Registered %uKB flash at %RGp\n", pThisCC->cbNvram / _1K, pThisCC->GCPhysNvram)); return VINF_SUCCESS; } static uint8_t efiGetHalfByte(char ch) { uint8_t val; if (ch >= '0' && ch <= '9') val = ch - '0'; else if (ch >= 'A' && ch <= 'F') val = ch - 'A' + 10; else if(ch >= 'a' && ch <= 'f') val = ch - 'a' + 10; else val = 0xff; return val; } /** * Converts a hex string into a binary data blob located at * pThisCC->pbDeviceProps, size returned as pThisCC->cbDeviceProps. * * @returns VERR_NO_MEMORY or VINF_SUCCESS. * @param pThisCC The device state for the current context. * @param pszDeviceProps The device property hex string to decode. */ static int efiParseDeviceString(PDEVEFIR3 pThisCC, const char *pszDeviceProps) { uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1; pThisCC->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThisCC->pDevIns, cbOut); if (!pThisCC->pbDeviceProps) return VERR_NO_MEMORY; uint32_t iHex = 0; bool fUpper = true; uint8_t u8Value = 0; /* (shut up gcc) */ for (uint32_t iStr = 0; pszDeviceProps[iStr]; iStr++) { uint8_t u8Hb = efiGetHalfByte(pszDeviceProps[iStr]); if (u8Hb > 0xf) continue; if (fUpper) u8Value = u8Hb << 4; else pThisCC->pbDeviceProps[iHex++] = u8Hb | u8Value; Assert(iHex < cbOut); fUpper = !fUpper; } Assert(iHex == 0 || fUpper); pThisCC->cbDeviceProps = iHex; return VINF_SUCCESS; } /** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) { PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); PDEVEFIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVEFIR3); PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3; int rc; RT_NOREF(iInstance); Assert(iInstance == 0); /* * Initalize the basic variables so that the destructor always works. */ pThisCC->pDevIns = pDevIns; pThisCC->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface; /* * Validate and read the configuration. */ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "EfiRom|" "NumCPUs|" "McfgBase|" "McfgLength|" "UUID|" "UuidLe|" "IOAPIC|" "APIC|" "DmiBIOSFirmwareMajor|" "DmiBIOSFirmwareMinor|" "DmiBIOSReleaseDate|" "DmiBIOSReleaseMajor|" "DmiBIOSReleaseMinor|" "DmiBIOSVendor|" "DmiBIOSVersion|" "DmiSystemFamily|" "DmiSystemProduct|" "DmiSystemSerial|" "DmiSystemSKU|" "DmiSystemUuid|" "DmiSystemVendor|" "DmiSystemVersion|" "DmiBoardAssetTag|" "DmiBoardBoardType|" "DmiBoardLocInChass|" "DmiBoardProduct|" "DmiBoardSerial|" "DmiBoardVendor|" "DmiBoardVersion|" "DmiChassisAssetTag|" "DmiChassisSerial|" "DmiChassisType|" "DmiChassisVendor|" "DmiChassisVersion|" "DmiProcManufacturer|" "DmiProcVersion|" "DmiOEMVBoxVer|" "DmiOEMVBoxRev|" "DmiUseHostInfo|" "DmiExposeMemoryTable|" "DmiExposeProcInf|" "64BitEntry|" "BootArgs|" "DeviceProps|" "GopMode|" // legacy "GraphicsMode|" "UgaHorizontalResolution|" // legacy "UgaVerticalResolution|" // legacy "GraphicsResolution|" "NvramFile", ""); /* CPU count (optional). */ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "NumCPUs", &pThisCC->cCpus, 1); AssertLogRelRCReturn(rc, rc); rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThisCC->u64McfgBase, 0); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"\" as integer failed")); rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThisCC->cbMcfgLength, 0); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"McfgLength\" as integer failed")); rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThisCC->u8IOAPIC, 1); if (RT_FAILURE (rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"IOAPIC\"")); rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThisCC->u8APIC, 1); if (RT_FAILURE (rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"APIC\"")); /* * Query the machine's UUID for SMBIOS/DMI use. */ RTUUID uuid; rc = pHlp->pfnCFGMQueryBytes(pCfg, "UUID", &uuid, sizeof(uuid)); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"UUID\" failed")); bool fUuidLe; rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UuidLe", &fUuidLe, false); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"UuidLe\" failed")); if (!fUuidLe) { /* * UUIDs are stored little endian actually (see chapter 7.2.1 System — UUID * of the DMI/SMBIOS spec) but to not force reactivation of existing guests we have * to carry this bug along... (see also DevPcBios.cpp when changing this) * * Convert the UUID to network byte order. Not entirely straightforward as * parts are MSB already... */ uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow); uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid); uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion); } memcpy(&pThisCC->aUuid, &uuid, sizeof pThisCC->aUuid); /* * Get the system EFI ROM file name. */ #ifdef VBOX_WITH_EFI_IN_DD2 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "EfiRom", &pThisCC->pszEfiRomFile, g_szEfiBuiltin32); if (RT_FAILURE(rc)) #else rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "EfiRom", &pThisCC->pszEfiRomFile); if (rc == VERR_CFGM_VALUE_NOT_FOUND) { pThisCC->pszEfiRomFile = (char *)PDMDevHlpMMHeapAlloc(pDevIns, RTPATH_MAX); AssertReturn(pThisCC->pszEfiRomFile, VERR_NO_MEMORY); rc = RTPathAppPrivateArchTop(pThisCC->pszEfiRomFile, RTPATH_MAX); AssertRCReturn(rc, rc); rc = RTPathAppend(pThisCC->pszEfiRomFile, RTPATH_MAX, "VBoxEFI32.fd"); AssertRCReturn(rc, rc); } else if (RT_FAILURE(rc)) #endif return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"EfiRom\" as a string failed")); /* * Saved State handling. */ rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThisCC), efiSaveExec, efiLoadExec); AssertRCReturn(rc, rc); /* * NVRAM storage. */ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->Lun0.IBase, &pThisCC->Lun0.pDrvBase, "NvramStorage"); if (RT_SUCCESS(rc)) { pThisCC->Lun0.pDrvVfs = PDMIBASE_QUERY_INTERFACE(pThisCC->Lun0.pDrvBase, PDMIVFSCONNECTOR); if (!pThisCC->Lun0.pDrvVfs) return PDMDevHlpVMSetError(pDevIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("NVRAM storage driver is missing VFS interface below")); } else if (rc == VERR_PDM_NO_ATTACHED_DRIVER) rc = VINF_SUCCESS; /* Missing driver is no error condition. */ else return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver")); /* * Get boot args. */ rc = pHlp->pfnCFGMQueryStringDef(pCfg, "BootArgs", pThisCC->szBootArgs, sizeof(pThisCC->szBootArgs), ""); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"BootArgs\" as a string failed")); //strcpy(pThisCC->szBootArgs, "-v keepsyms=1 io=0xf debug=0x2a"); //strcpy(pThisCC->szBootArgs, "-v keepsyms=1 debug=0x2a"); LogRel(("EFI: boot args = %s\n", pThisCC->szBootArgs)); /* * Get device props. */ char *pszDeviceProps; rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DeviceProps", &pszDeviceProps, NULL); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"DeviceProps\" as a string failed")); if (pszDeviceProps) { LogRel(("EFI: device props = %s\n", pszDeviceProps)); rc = efiParseDeviceString(pThisCC, pszDeviceProps); PDMDevHlpMMHeapFree(pDevIns, pszDeviceProps); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Cannot parse device properties")); } else { pThisCC->pbDeviceProps = NULL; pThisCC->cbDeviceProps = 0; } /* * CPU frequencies. */ pThisCC->u64TscFrequency = PDMDevHlpTMCpuTicksPerSecond(pDevIns); pThisCC->u64CpuFrequency = pThisCC->u64TscFrequency; pThisCC->u64FsbFrequency = PDMDevHlpCpuGetGuestScalableBusFrequency(pDevIns); /* * EFI graphics mode (with new EFI VGA code used only as a fallback, for * old EFI VGA code the only way to select the GOP mode). */ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "GraphicsMode", &pThisCC->u32GraphicsMode, UINT32_MAX); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"GraphicsMode\" as a 32-bit int failed")); if (pThisCC->u32GraphicsMode == UINT32_MAX) { /* get the legacy value if nothing else was specified */ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "GopMode", &pThisCC->u32GraphicsMode, UINT32_MAX); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed")); } if (pThisCC->u32GraphicsMode == UINT32_MAX) pThisCC->u32GraphicsMode = 2; /* 1024x768, at least typically */ /* * EFI graphics resolution, defaults to 1024x768 (used to be UGA only, now * is the main config setting as the mode number is so hard to predict). */ char szResolution[16]; rc = pHlp->pfnCFGMQueryStringDef(pCfg, "GraphicsResolution", szResolution, sizeof(szResolution), ""); if (RT_FAILURE(rc)) return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"GraphicsResolution\" as a string failed")); if (szResolution[0]) { const char *pszX = RTStrStr(szResolution, "x"); if (pszX) { pThisCC->u32HorizontalResolution = RTStrToUInt32(szResolution); pThisCC->u32VerticalResolution = RTStrToUInt32(pszX + 1); } } else { /* get the legacy values if nothing else was specified */ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UgaHorizontalResolution", &pThisCC->u32HorizontalResolution, 0); AssertRCReturn(rc, rc); rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UgaVerticalResolution", &pThisCC->u32VerticalResolution, 0); AssertRCReturn(rc, rc); } if (pThisCC->u32HorizontalResolution == 0 || pThisCC->u32VerticalResolution == 0) { pThisCC->u32HorizontalResolution = 1024; pThisCC->u32VerticalResolution = 768; } pThisCC->pszNvramFile = NULL; rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "NvramFile", &pThisCC->pszNvramFile); if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"NvramFile\" as a string failed")); /* * Load firmware volume and thunk ROM. */ rc = efiLoadRom(pDevIns, pThis, pThisCC, pCfg); if (RT_FAILURE(rc)) return rc; /* * Register our I/O ports. */ rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, IOM_IOPORT_F_ABS, efiR3IoPortWrite, efiR3IoPortRead, "EFI communication ports", NULL /*paExtDescs*/, &pThis->hIoPorts); AssertRCReturn(rc, rc); /* * Plant DMI and MPS tables in the ROM region. */ rc = FwCommonPlantDMITable(pDevIns, pThisCC->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThisCC->aUuid, pDevIns->pCfg, pThisCC->cCpus, &pThisCC->cbDmiTables, &pThisCC->cNumDmiTables, true /*fUefi*/); AssertRCReturn(rc, rc); /* * NB: VBox/Devices/EFI/Firmware/VBoxPkg/VBoxSysTables/VBoxSysTables.c scans memory for * the SMBIOS header. The header must be placed in a range that EFI will scan. */ FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThisCC->au8DMIPage + VBOX_DMI_TABLE_SIZE, pThisCC->cbDmiTables, pThisCC->cNumDmiTables); if (pThisCC->u8IOAPIC) { FwCommonPlantMpsTable(pDevIns, pThisCC->au8DMIPage /* aka VBOX_DMI_TABLE_BASE */ + VBOX_DMI_TABLE_SIZE + VBOX_DMI_HDR_SIZE, _4K - VBOX_DMI_TABLE_SIZE - VBOX_DMI_HDR_SIZE, pThisCC->cCpus); FwCommonPlantMpsFloatPtr(pDevIns, VBOX_DMI_TABLE_BASE + VBOX_DMI_TABLE_SIZE + VBOX_DMI_HDR_SIZE); } rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThisCC->au8DMIPage, _4K, PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables"); AssertRCReturn(rc, rc); /* * Call reset to set things up. */ efiReset(pDevIns); return VINF_SUCCESS; } #else /* IN_RING3 */ /** * @callback_method_impl{PDMDEVREGR0,pfnConstruct} */ static DECLCALLBACK(int) efiRZConstruct(PPDMDEVINS pDevIns) { PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); PDEVEFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVEFI); # if 1 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioFlash, efiR3NvMmioWrite, efiR3NvMmioRead, NULL /*pvUser*/); AssertRCReturn(rc, rc); # else RT_NOREF(pDevIns, pThis); (void)&efiR3NvMmioRead; (void)&efiR3NvMmioWrite; # endif return VINF_SUCCESS; } #endif /* IN_RING3 */ /** * The device registration structure. */ const PDMDEVREG g_DeviceEFI = { /* .u32Version = */ PDM_DEVREG_VERSION, /* .uReserved0 = */ 0, /* .szName = */ "efi", /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE, /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS, /* .cMaxInstances = */ 1, /* .uSharedVersion = */ 42, /* .cbInstanceShared = */ sizeof(DEVEFI), /* .cbInstanceCC = */ sizeof(DEVEFICC), /* .cbInstanceRC = */ sizeof(DEVEFIRC), /* .cMaxPciDevices = */ 0, /* .cMaxMsixVectors = */ 0, /* .pszDescription = */ "Extensible Firmware Interface Device.\n" "LUN#0 - NVRAM port", #if defined(IN_RING3) /* .pszRCMod = */ "VBoxDDRC.rc", /* .pszR0Mod = */ "VBoxDDR0.r0", /* .pfnConstruct = */ efiConstruct, /* .pfnDestruct = */ efiDestruct, /* .pfnRelocate = */ NULL, /* .pfnMemSetup = */ efiMemSetup, /* .pfnPowerOn = */ NULL, /* .pfnReset = */ efiReset, /* .pfnSuspend = */ NULL, /* .pfnResume = */ NULL, /* .pfnAttach = */ NULL, /* .pfnDetach = */ NULL, /* .pfnQueryInterface = */ NULL, /* .pfnInitComplete = */ efiInitComplete, /* .pfnPowerOff = */ efiPowerOff, /* .pfnSoftReset = */ NULL, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, /* .pfnReserved2 = */ NULL, /* .pfnReserved3 = */ NULL, /* .pfnReserved4 = */ NULL, /* .pfnReserved5 = */ NULL, /* .pfnReserved6 = */ NULL, /* .pfnReserved7 = */ NULL, #elif defined(IN_RING0) /* .pfnEarlyConstruct = */ NULL, /* .pfnConstruct = */ efiRZConstruct, /* .pfnDestruct = */ NULL, /* .pfnFinalDestruct = */ NULL, /* .pfnRequest = */ NULL, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, /* .pfnReserved2 = */ NULL, /* .pfnReserved3 = */ NULL, /* .pfnReserved4 = */ NULL, /* .pfnReserved5 = */ NULL, /* .pfnReserved6 = */ NULL, /* .pfnReserved7 = */ NULL, #elif defined(IN_RC) /* .pfnConstruct = */ efiRZConstruct, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, /* .pfnReserved2 = */ NULL, /* .pfnReserved3 = */ NULL, /* .pfnReserved4 = */ NULL, /* .pfnReserved5 = */ NULL, /* .pfnReserved6 = */ NULL, /* .pfnReserved7 = */ NULL, #else # error "Not in IN_RING3, IN_RING0 or IN_RC!" #endif /* .u32VersionEnd = */ PDM_DEVREG_VERSION };