summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/EFI/DevEFI.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/DevEFI.cpp')
-rw-r--r--src/VBox/Devices/EFI/DevEFI.cpp2086
1 files changed, 1572 insertions, 514 deletions
diff --git a/src/VBox/Devices/EFI/DevEFI.cpp b/src/VBox/Devices/EFI/DevEFI.cpp
index 6251db2e..34b4b24f 100644
--- a/src/VBox/Devices/EFI/DevEFI.cpp
+++ b/src/VBox/Devices/EFI/DevEFI.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2009 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -22,14 +22,17 @@
#include <VBox/vmm/pdmdev.h>
#include <VBox/vmm/pgm.h>
+#include <VBox/vmm/cpum.h>
#include <VBox/vmm/mm.h>
#include <VBox/log.h>
#include <VBox/err.h>
#include <VBox/param.h>
#include <VBox/vmm/dbgf.h>
+#include <VBox/vmm/pdmnvram.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
+#include <iprt/ctype.h>
#include <iprt/file.h>
#include <iprt/mem.h>
#include <iprt/string.h>
@@ -37,12 +40,13 @@
#include <iprt/path.h>
#include <iprt/string.h>
#include <iprt/mp.h>
-#ifdef DEBUG
+#include <iprt/list.h>
+#if defined(DEBUG) && defined(IN_RING3)
# include <iprt/stream.h>
# define DEVEFI_WITH_VBOXDBG_SCRIPT
#endif
-#include "Firmware2/VBoxPkg/Include/DevEFI.h"
+#include "DevEFI.h"
#include "VBoxDD.h"
#include "VBoxDD2.h"
#include "../PC/DevFwCommon.h"
@@ -52,100 +56,1013 @@
#include <Common/UefiBaseTypes.h>
#include <Common/PiFirmwareVolume.h>
#include <Common/PiFirmwareFile.h>
-#include <IndustryStandard/PeImage.h>
+
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
+/**
+ * EFI NVRAM variable.
+ */
+typedef struct EFIVAR
+{
+ /** The list node for the variable. */
+ RTLISTNODE ListNode;
+ /** The unique sequence number of the variable.
+ * This is used to find pCurVar when restoring saved state and therefore only
+ * set when saving. */
+ uint32_t idUniqueSavedState;
+ /** The value attributess. */
+ uint32_t fAttributes;
+ /** The variable name length (not counting the terminator char). */
+ uint32_t cchName;
+ /** The size of the value. This cannot be zero. */
+ uint32_t cbValue;
+ /** The vendor UUID scoping the variable name. */
+ RTUUID uuid;
+ /** The variable name. */
+ char szName[EFI_VARIABLE_NAME_MAX];
+ /** The variable value bytes. */
+ uint8_t abValue[EFI_VARIABLE_VALUE_MAX];
+} EFIVAR;
+/** Pointer to an EFI NVRAM variable. */
+typedef EFIVAR *PEFIVAR;
+/** Pointer to a const EFI NVRAM variable. */
+typedef EFIVAR const *PCEFIVAR;
+/** Pointer to an EFI NVRAM variable pointer. */
+typedef PEFIVAR *PPEFIVAR;
+
+/**
+ * NVRAM state.
+ */
+typedef struct NVRAMDESC
+{
+ /** The current operation. */
+ EFIVAROP enmOp;
+ /** The current status. */
+ uint32_t u32Status;
+ /** The current */
+ uint32_t offOpBuffer;
+ /** The current number of variables. */
+ uint32_t cVariables;
+ /** The list of variables. */
+ RTLISTANCHOR VarList;
+
+ /** The unique variable sequence ID, for the saved state only.
+ * @todo It's part of this structure for hysterical raisins, consider remove it
+ * when changing the saved state format the next time. */
+ uint32_t idUniqueCurVar;
+ /** Variable buffered used both when adding and querying NVRAM variables.
+ * When querying a variable, a copy of it is stored in this buffer and read
+ * from it. When adding, updating or deleting a variable, this buffer is used
+ * to set up the parameters before taking action. */
+ EFIVAR VarOpBuf;
+ /** The current variable. This is only used by EFI_VARIABLE_OP_QUERY_NEXT,
+ * the attribute readers work against the copy in VarOpBuf. */
+ PEFIVAR pCurVar;
+} NVRAMDESC;
+
+
+/**
+ * The EFI device state structure.
+ */
typedef struct DEVEFI
{
/** Pointer back to the device instance. */
- PPDMDEVINS pDevIns;
+ PPDMDEVINS pDevIns;
+
/** EFI message buffer. */
- char szMsg[VBOX_EFI_DEBUG_BUFFER];
+ char szMsg[VBOX_EFI_DEBUG_BUFFER];
/** EFI message buffer index. */
- uint32_t iMsg;
+ uint32_t iMsg;
+
/** EFI panic message buffer. */
- char szPanicMsg[2048];
+ char szPanicMsg[2048];
/** EFI panic message buffer index. */
- uint32_t iPanicMsg;
+ 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 *pu8EfiRom;
+ uint8_t *pu8EfiRom;
/** The size of the system EFI ROM. */
- uint64_t cbEfiRom;
+ uint64_t cbEfiRom;
/** The name of the EFI ROM file. */
- char *pszEfiRomFile;
+ 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 iInfoPosition;
+ 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;
+ uint32_t cCpus;
/** RAM below 4GB (in bytes). (Config) */
- uint32_t cbBelow4GB;
+ uint32_t cbBelow4GB;
/** RAM above 4GB (in bytes). (Config) */
- uint64_t cbAbove4GB;
-
- uint64_t cbRam;
-
- uint64_t cbRamHole;
-
- /** The size of the DMI tables */
- uint16_t cbDmiTables;
+ uint64_t cbAbove4GB;
+ /** The total amount of memory. */
+ uint64_t cbRam;
+ /** The size of the RAM hole below 4GB. */
+ uint64_t cbRamHole;
+
+ /** The size of the DMI tables. */
+ uint16_t cbDmiTables;
+ /** Number of the DMI tables. */
+ uint16_t cNumDmiTables;
/** The DMI tables. */
- uint8_t au8DMIPage[0x1000];
+ uint8_t au8DMIPage[0x1000];
/** I/O-APIC enabled? */
- uint8_t u8IOAPIC;
-
- /* Boot parameters passed to the firmware */
- char szBootArgs[256];
-
- /* Host UUID (for DMI) */
- RTUUID aUuid;
-
- /* Device properties buffer */
- uint8_t* pu8DeviceProps;
- /* Device properties buffer size */
- uint32_t u32DevicePropsLen;
-
- /* 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;
- /* GOP mode */
- uint32_t u32GopMode;
- /* Uga mode resolutions */
- uint32_t u32UgaHorisontal;
- uint32_t u32UgaVertical;
+ uint8_t u8IOAPIC;
+
+ /** 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;
+ /** GOP mode. */
+ uint32_t u32GopMode;
+ /** Uga mode horisontal resolution. */
+ uint32_t cxUgaResolution;
+ /** Uga mode vertical resolution. */
+ uint32_t cyUgaResolution;
+
+
+ /** NVRAM state variables. */
+ NVRAMDESC NVRAM;
+
+ /**
+ * NVRAM port - LUN\#0.
+ */
+ struct
+ {
+ /** The base interface we provide the NVRAM driver. */
+ PDMIBASE IBase;
+ /** The NVRAM driver base interface. */
+ PPDMIBASE pDrvBase;
+ /** The NVRAM interface provided by the driver. */
+ PPDMINVRAMCONNECTOR pNvramDrv;
+ } Lun0;
} DEVEFI;
typedef DEVEFI *PDEVEFI;
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** The saved state version. */
+#define EFI_SSM_VERSION 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 *
+*******************************************************************************/
+/** Saved state NVRAMDESC field descriptors. */
+static SSMFIELD const g_aEfiNvramDescField[] =
+{
+ SSMFIELD_ENTRY( NVRAMDESC, enmOp),
+ SSMFIELD_ENTRY( NVRAMDESC, u32Status),
+ SSMFIELD_ENTRY( NVRAMDESC, offOpBuffer),
+ SSMFIELD_ENTRY_IGNORE(NVRAMDESC, VarOpBuf),
+ SSMFIELD_ENTRY( NVRAMDESC, cVariables),
+ SSMFIELD_ENTRY_OLD( idUnquireLast, 4),
+ SSMFIELD_ENTRY_IGNORE(NVRAMDESC, VarList),
+ SSMFIELD_ENTRY( NVRAMDESC, idUniqueCurVar),
+ SSMFIELD_ENTRY_IGNORE(NVRAMDESC, pCurVar),
+ SSMFIELD_ENTRY_TERM()
+};
+
+/** Saved state EFIVAR field descriptors. */
+static SSMFIELD const g_aEfiVariableDescFields[] =
+{
+ SSMFIELD_ENTRY_IGNORE(EFIVAR, ListNode),
+ SSMFIELD_ENTRY( EFIVAR, idUniqueSavedState),
+ SSMFIELD_ENTRY( EFIVAR, uuid),
+ SSMFIELD_ENTRY( EFIVAR, szName),
+ SSMFIELD_ENTRY_OLD( cchName, 4),
+ SSMFIELD_ENTRY( EFIVAR, abValue),
+ SSMFIELD_ENTRY( EFIVAR, cbValue),
+ SSMFIELD_ENTRY( EFIVAR, fAttributes),
+ SSMFIELD_ENTRY_TERM()
+};
+
+
+
+
/**
- * Write to CMOS memory.
- * This is used by the init complete code.
+ * Flushes the variable list.
+ *
+ * @param pThis The EFI state.
*/
-static void cmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
+static void nvramFlushDeviceVariableList(PDEVEFI pThis)
{
- Assert(off < 128);
- Assert(u32Val < 256);
+ while (!RTListIsEmpty(&pThis->NVRAM.VarList))
+ {
+ PEFIVAR pEfiVar = RTListNodeGetNext(&pThis->NVRAM.VarList, EFIVAR, ListNode);
+ RTListNodeRemove(&pEfiVar->ListNode);
+ RTMemFree(pEfiVar);
+ }
- int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
- AssertRC(rc);
+ pThis->NVRAM.pCurVar = NULL;
+}
+
+/**
+ * This function looks up variable in NVRAM list.
+ */
+static int nvramLookupVariableByUuidAndName(PDEVEFI pThis, char *pszVariableName, PCRTUUID pUuid, PPEFIVAR ppEfiVar)
+{
+ LogFlowFunc(("%RTuuid::'%s'\n", pUuid, pszVariableName));
+ size_t const cchVariableName = strlen(pszVariableName);
+ int rc = VERR_NOT_FOUND;
+
+ /*
+ * Start by checking the last variable queried.
+ */
+ if ( pThis->NVRAM.pCurVar
+ && pThis->NVRAM.pCurVar->cchName == cchVariableName
+ && memcmp(pThis->NVRAM.pCurVar->szName, pszVariableName, cchVariableName + 1) == 0
+ && RTUuidCompare(&pThis->NVRAM.pCurVar->uuid, pUuid) == 0
+ )
+ {
+ *ppEfiVar = pThis->NVRAM.pCurVar;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /*
+ * Linear list search.
+ */
+ PEFIVAR pEfiVar;
+ RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
+ {
+ Assert(strlen(pEfiVar->szName) == pEfiVar->cchName);
+ if ( pEfiVar->cchName == cchVariableName
+ && memcmp(pEfiVar->szName, pszVariableName, cchVariableName + 1) == 0
+ && RTUuidCompare(&pEfiVar->uuid, pUuid) == 0)
+ {
+ *ppEfiVar = pEfiVar;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ LogFlowFunc(("rc=%Rrc pEfiVar=%p\n", rc, *ppEfiVar));
+ return rc;
+}
+
+
+/**
+ * Inserts the EFI variable into the list.
+ *
+ * This enforces the desired list ordering and/or insertion policy.
+ *
+ * @param pThis The EFI state.
+ * @param pEfiVar The variable to insert.
+ */
+static void nvramInsertVariable(PDEVEFI pThis, PEFIVAR pEfiVar)
+{
+#if 1
+ /*
+ * Sorted by UUID and name.
+ */
+ PEFIVAR pCurVar;
+ RTListForEach(&pThis->NVRAM.VarList, pCurVar, EFIVAR, ListNode)
+ {
+ int iDiff = RTUuidCompare(&pEfiVar->uuid, &pCurVar->uuid);
+ if (!iDiff)
+ iDiff = strcmp(pEfiVar->szName, pCurVar->szName);
+ if (iDiff < 0)
+ {
+ RTListNodeInsertBefore(&pCurVar->ListNode, &pEfiVar->ListNode);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Add it at the end.
+ */
+ RTListAppend(&pThis->NVRAM.VarList, &pEfiVar->ListNode);
}
+/**
+ * Creates an device internal list of variables.
+ *
+ * @returns VBox status code.
+ * @param pThis The EFI state.
+ */
+static int nvramLoad(PDEVEFI pThis)
+{
+ int rc;
+ for (uint32_t iVar = 0; iVar < EFI_VARIABLE_MAX; iVar++)
+ {
+ PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
+ AssertReturn(pEfiVar, VERR_NO_MEMORY);
+
+ pEfiVar->cchName = sizeof(pEfiVar->szName);
+ pEfiVar->cbValue = sizeof(pEfiVar->abValue);
+ rc = pThis->Lun0.pNvramDrv->pfnVarQueryByIndex(pThis->Lun0.pNvramDrv, iVar,
+ &pEfiVar->uuid, &pEfiVar->szName[0], &pEfiVar->cchName,
+ &pEfiVar->fAttributes, &pEfiVar->abValue[0], &pEfiVar->cbValue);
+ if (RT_SUCCESS(rc))
+ {
+ /* Some validations. */
+ rc = RTStrValidateEncoding(pEfiVar->szName);
+ size_t cchName = RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
+ if (cchName != pEfiVar->cchName)
+ rc = VERR_INVALID_PARAMETER;
+ if (pEfiVar->cbValue == 0)
+ rc = VERR_NO_DATA;
+ if (RT_FAILURE(rc))
+ LogRel(("EFI/nvramLoad: Bad variable #%u: cbValue=%#x cchName=%#x (strlen=%#x) szName=%.*Rhxs\n",
+ pEfiVar->cbValue, pEfiVar->cchName, cchName, pEfiVar->cchName + 1, pEfiVar->szName));
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pEfiVar);
+ if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ return rc;
+ }
+
+ /* Append it. */
+ nvramInsertVariable(pThis, pEfiVar);
+ pThis->NVRAM.cVariables++;
+ }
+
+ AssertLogRelMsgFailed(("EFI: Too many variables.\n"));
+ return VERR_TOO_MUCH_DATA;
+}
+
+
+/**
+ * Let the NVRAM driver store the internal NVRAM variable list.
+ *
+ * @returns VBox status code.
+ * @param pThis The EFI state.
+ */
+static int nvramStore(PDEVEFI pThis)
+{
+ /*
+ * Count the non-volatile variables and issue the begin call.
+ */
+ PEFIVAR pEfiVar;
+ uint32_t cNonVolatile = 0;
+ RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
+ if (pEfiVar->fAttributes & VBOX_EFI_VARIABLE_NON_VOLATILE)
+ cNonVolatile++;
+ int rc = pThis->Lun0.pNvramDrv->pfnVarStoreSeqBegin(pThis->Lun0.pNvramDrv, cNonVolatile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Store each non-volatile variable.
+ */
+ uint32_t idxVar = 0;
+ RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
+ {
+ /* Skip volatile variables. */
+ if (!(pEfiVar->fAttributes & VBOX_EFI_VARIABLE_NON_VOLATILE))
+ continue;
+
+ int rc2 = pThis->Lun0.pNvramDrv->pfnVarStoreSeqPut(pThis->Lun0.pNvramDrv, idxVar,
+ &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cchName,
+ pEfiVar->fAttributes, pEfiVar->abValue, pEfiVar->cbValue);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rc))
+ {
+ LogRel(("EFI: pfnVarStoreVarByIndex failed: %Rrc\n", rc));
+ rc = rc2;
+ }
+ idxVar++;
+ }
+ Assert(idxVar == cNonVolatile);
+
+ /*
+ * Done.
+ */
+ rc = pThis->Lun0.pNvramDrv->pfnVarStoreSeqEnd(pThis->Lun0.pNvramDrv, rc);
+ }
+ else
+ LogRel(("EFI: pfnVarStoreBegin failed: %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * EFI_VARIABLE_OP_QUERY and EFI_VARIABLE_OP_QUERY_NEXT worker that copies the
+ * variable into the VarOpBuf, set pCurVar and u32Status.
+ *
+ * @param pThis The EFI state.
+ * @param pEfiVar The resulting variable. NULL if not found / end.
+ * @param fEnumQuery Set if enumeration query, clear if specific.
+ */
+static void nvramWriteVariableOpQueryCopyResult(PDEVEFI pThis, PEFIVAR pEfiVar, bool fEnumQuery)
+{
+ RT_ZERO(pThis->NVRAM.VarOpBuf.abValue);
+ if (pEfiVar)
+ {
+ RT_ZERO(pThis->NVRAM.VarOpBuf.szName);
+ pThis->NVRAM.VarOpBuf.uuid = pEfiVar->uuid;
+ pThis->NVRAM.VarOpBuf.cchName = pEfiVar->cchName;
+ memcpy(pThis->NVRAM.VarOpBuf.szName, pEfiVar->szName, pEfiVar->cchName); /* no need for + 1. */
+ pThis->NVRAM.VarOpBuf.fAttributes = pEfiVar->fAttributes;
+ pThis->NVRAM.VarOpBuf.cbValue = pEfiVar->cbValue;
+ memcpy(pThis->NVRAM.VarOpBuf.abValue, pEfiVar->abValue, pEfiVar->cbValue);
+ pThis->NVRAM.pCurVar = pEfiVar;
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
+ LogFlow(("EFI: Variable query -> %RTuuid::'%s' (%d) abValue=%.*Rhxs\n", &pThis->NVRAM.VarOpBuf.uuid,
+ pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.cchName,
+ pThis->NVRAM.VarOpBuf.cbValue, pThis->NVRAM.VarOpBuf.abValue));
+ }
+ else
+ {
+ if (fEnumQuery)
+ LogFlow(("EFI: Variable query -> NOT_FOUND \n"));
+ else
+ LogFlow(("EFI: Variable query %RTuuid::'%s' -> NOT_FOUND \n",
+ &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName));
+ RT_ZERO(pThis->NVRAM.VarOpBuf.szName);
+ pThis->NVRAM.VarOpBuf.fAttributes = 0;
+ pThis->NVRAM.VarOpBuf.cbValue = 0;
+ pThis->NVRAM.VarOpBuf.cchName = 0;
+ pThis->NVRAM.pCurVar = NULL;
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_NOT_FOUND;
+ }
+}
+
+/**
+ * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_QUERY.
+ *
+ * @returns IOM strict status code.
+ * @param pThis The EFI state.
+ */
+static int nvramWriteVariableOpQuery(PDEVEFI pThis)
+{
+ Log(("EFI_VARIABLE_OP_QUERY: %RTuuid::'%s'\n", &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName));
+
+ PEFIVAR pEfiVar;
+ int rc = nvramLookupVariableByUuidAndName(pThis,
+ pThis->NVRAM.VarOpBuf.szName,
+ &pThis->NVRAM.VarOpBuf.uuid,
+ &pEfiVar);
+ nvramWriteVariableOpQueryCopyResult(pThis, RT_SUCCESS(rc) ? pEfiVar : NULL, false /*fEnumQuery*/);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_QUERY_NEXT.
+ *
+ * This simply walks the list.
+ *
+ * @returns IOM strict status code.
+ * @param pThis The EFI state.
+ */
+static int nvramWriteVariableOpQueryNext(PDEVEFI pThis)
+{
+ Log(("EFI_VARIABLE_OP_QUERY_NEXT: pCurVar=%p\n", pThis->NVRAM.pCurVar));
+ PEFIVAR pEfiVar = pThis->NVRAM.pCurVar;
+ if (pEfiVar)
+ pEfiVar = RTListGetNext(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode);
+ else
+ pEfiVar = RTListGetFirst(&pThis->NVRAM.VarList, EFIVAR, ListNode);
+ nvramWriteVariableOpQueryCopyResult(pThis, pEfiVar, true /* fEnumQuery */);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Implements EFI_VARIABLE_PARAM + EFI_VARIABLE_OP_ADD.
+ *
+ * @returns IOM strict status code.
+ * @param pThis The EFI state.
+ */
+static int nvramWriteVariableOpAdd(PDEVEFI pThis)
+{
+ Log(("EFI_VARIABLE_OP_ADD: %RTuuid::'%s' fAttributes=%#x abValue=%.*Rhxs\n",
+ &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes,
+ pThis->NVRAM.VarOpBuf.cbValue, pThis->NVRAM.VarOpBuf.abValue));
+
+ /*
+ * Validate and adjust the input a little before we start.
+ */
+ int rc = RTStrValidateEncoding(pThis->NVRAM.VarOpBuf.szName);
+ if (RT_FAILURE(rc))
+ LogRel(("EFI: Badly encoded variable name: %.*Rhxs\n", pThis->NVRAM.VarOpBuf.cchName + 1, pThis->NVRAM.VarOpBuf.szName));
+ if (RT_FAILURE(rc))
+ {
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ return VINF_SUCCESS;
+ }
+ pThis->NVRAM.VarOpBuf.cchName = (uint32_t)RTStrNLen(pThis->NVRAM.VarOpBuf.szName, sizeof(pThis->NVRAM.VarOpBuf.szName));
+
+ /*
+ * Look it up and see what to do.
+ */
+ PEFIVAR pEfiVar;
+ rc = nvramLookupVariableByUuidAndName(pThis,
+ pThis->NVRAM.VarOpBuf.szName,
+ &pThis->NVRAM.VarOpBuf.uuid,
+ &pEfiVar);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Old abValue=%.*Rhxs\n", pEfiVar->cbValue, pEfiVar->abValue));
+#if 0 /** @todo Implement read-only EFI variables. */
+ if (pEfiVar->fAttributes & EFI_VARIABLE_XXXXXXX)
+ {
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_RO;
+ break;
+ }
+#endif
+
+ if (pThis->NVRAM.VarOpBuf.cbValue == 0)
+ {
+ /*
+ * Delete it.
+ */
+ LogRel(("EFI: Deleting variable %RTuuid::'%s'\n", &pThis->NVRAM.VarOpBuf.uuid, pThis->NVRAM.VarOpBuf.szName));
+ RTListNodeRemove(&pEfiVar->ListNode);
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
+ pThis->NVRAM.cVariables--;
+
+ if (pThis->NVRAM.pCurVar == pEfiVar)
+ pThis->NVRAM.pCurVar = NULL;
+ RTMemFree(pEfiVar);
+ pEfiVar = NULL;
+ }
+ else
+ {
+ /*
+ * Update/replace it. (The name and UUID are unchanged, of course.)
+ */
+ LogRel(("EFI: Replacing variable %RTuuid::'%s' fAttrib=%#x cbValue=%#x\n", &pThis->NVRAM.VarOpBuf.uuid,
+ pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes, pThis->NVRAM.VarOpBuf.cbValue));
+ pEfiVar->fAttributes = pThis->NVRAM.VarOpBuf.fAttributes;
+ pEfiVar->cbValue = pThis->NVRAM.VarOpBuf.cbValue;
+ memcpy(pEfiVar->abValue, pThis->NVRAM.VarOpBuf.abValue, pEfiVar->cbValue);
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
+ }
+ }
+ else if (pThis->NVRAM.VarOpBuf.cbValue == 0)
+ {
+ /* delete operation, but nothing to delete. */
+ LogFlow(("nvramWriteVariableOpAdd: Delete (not found)\n"));
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
+ }
+ else if (pThis->NVRAM.cVariables < EFI_VARIABLE_MAX)
+ {
+ /*
+ * Add a new variable.
+ */
+ LogRel(("EFI: Adding variable %RTuuid::'%s' fAttrib=%#x cbValue=%#x\n", &pThis->NVRAM.VarOpBuf.uuid,
+ pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes, pThis->NVRAM.VarOpBuf.cbValue));
+ pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
+ if (pEfiVar)
+ {
+ pEfiVar->uuid = pThis->NVRAM.VarOpBuf.uuid;
+ pEfiVar->cchName = pThis->NVRAM.VarOpBuf.cchName;
+ memcpy(pEfiVar->szName, pThis->NVRAM.VarOpBuf.szName, pEfiVar->cchName); /* The buffer is zeroed, so skip '\0'. */
+ pEfiVar->fAttributes = pThis->NVRAM.VarOpBuf.fAttributes;
+ pEfiVar->cbValue = pThis->NVRAM.VarOpBuf.cbValue;
+ memcpy(pEfiVar->abValue, pThis->NVRAM.VarOpBuf.abValue, pEfiVar->cbValue);
+
+ nvramInsertVariable(pThis, pEfiVar);
+ pThis->NVRAM.cVariables++;
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
+ }
+ else
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ else
+ {
+ /*
+ * Too many variables.
+ */
+ static unsigned s_cWarnings = 0;
+ if (s_cWarnings++ < 5)
+ LogRel(("EFI: Too many variables (%RTuuid::'%s' fAttrib=%#x cbValue=%#x)\n", &pThis->NVRAM.VarOpBuf.uuid,
+ pThis->NVRAM.VarOpBuf.szName, pThis->NVRAM.VarOpBuf.fAttributes, pThis->NVRAM.VarOpBuf.cbValue));
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ Log(("nvramWriteVariableOpAdd: Too many variabled.\n"));
+ }
+
+ LogFunc(("cVariables=%u u32Status=%#x\n", pThis->NVRAM.cVariables, pThis->NVRAM.u32Status));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Implements EFI_VARIABLE_PARAM writes.
+ *
+ * @returns IOM strict status code.
+ * @param pThis The EFI state.
+ * @param u32Value The value being written.
+ */
+static int nvramWriteVariableParam(PDEVEFI pThis, uint32_t u32Value)
+{
+ int rc = VINF_SUCCESS;
+ switch (pThis->NVRAM.enmOp)
+ {
+ case EFI_VM_VARIABLE_OP_START:
+ switch (u32Value)
+ {
+ case EFI_VARIABLE_OP_QUERY:
+ rc = nvramWriteVariableOpQuery(pThis);
+ break;
+
+ case EFI_VARIABLE_OP_QUERY_NEXT:
+ rc = nvramWriteVariableOpQueryNext(pThis);
+ break;
+
+ case EFI_VARIABLE_OP_QUERY_REWIND:
+ Log2(("EFI_VARIABLE_OP_QUERY_REWIND\n"));
+ pThis->NVRAM.pCurVar = NULL;
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_OK;
+ break;
+
+ case EFI_VARIABLE_OP_ADD:
+ rc = nvramWriteVariableOpAdd(pThis);
+ break;
+
+ default:
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ LogRel(("EFI: Unknown EFI_VM_VARIABLE_OP_START value %#x\n", u32Value));
+ break;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_GUID:
+ Log2(("EFI_VM_VARIABLE_OP_GUID[%#x]=%#x\n", pThis->NVRAM.offOpBuffer, u32Value));
+ if (pThis->NVRAM.offOpBuffer < sizeof(pThis->NVRAM.VarOpBuf.uuid))
+ pThis->NVRAM.VarOpBuf.uuid.au8[pThis->NVRAM.offOpBuffer++] = (uint8_t)u32Value;
+ else
+ {
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_GUID write (%#x).\n", u32Value));
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_ATTRIBUTE:
+ Log2(("EFI_VM_VARIABLE_OP_ATTRIBUTE=%#x\n", u32Value));
+ pThis->NVRAM.VarOpBuf.fAttributes = u32Value;
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME:
+ Log2(("EFI_VM_VARIABLE_OP_NAME[%#x]=%#x\n", pThis->NVRAM.offOpBuffer, u32Value));
+ if (pThis->NVRAM.offOpBuffer < pThis->NVRAM.VarOpBuf.cchName)
+ pThis->NVRAM.VarOpBuf.szName[pThis->NVRAM.offOpBuffer++] = (uint8_t)u32Value;
+ else if (u32Value == 0)
+ Assert(pThis->NVRAM.VarOpBuf.szName[sizeof(pThis->NVRAM.VarOpBuf.szName) - 1] == 0);
+ else
+ {
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME write (%#x).\n", u32Value));
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME_LENGTH:
+ Log2(("EFI_VM_VARIABLE_OP_NAME_LENGTH=%#x\n", u32Value));
+ RT_ZERO(pThis->NVRAM.VarOpBuf.szName);
+ if (u32Value < sizeof(pThis->NVRAM.VarOpBuf.szName))
+ pThis->NVRAM.VarOpBuf.cchName = u32Value;
+ else
+ {
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME_LENGTH write (%#x, max %#x).\n",
+ u32Value, sizeof(pThis->NVRAM.VarOpBuf.szName) - 1));
+ pThis->NVRAM.VarOpBuf.cchName = sizeof(pThis->NVRAM.VarOpBuf.szName) - 1;
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ Assert(pThis->NVRAM.offOpBuffer == 0);
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME_UTF16:
+ {
+ Log2(("EFI_VM_VARIABLE_OP_NAME_UTF16[%#x]=%#x\n", pThis->NVRAM.offOpBuffer, u32Value));
+ /* Currently simplifying this to UCS2, i.e. no surrogates. */
+ if (pThis->NVRAM.offOpBuffer == 0)
+ RT_ZERO(pThis->NVRAM.VarOpBuf.szName);
+ uint32_t cbUtf8 = (uint32_t)RTStrCpSize(u32Value);
+ if (pThis->NVRAM.offOpBuffer + cbUtf8 < sizeof(pThis->NVRAM.VarOpBuf.szName))
+ {
+ RTStrPutCp(&pThis->NVRAM.VarOpBuf.szName[pThis->NVRAM.offOpBuffer], u32Value);
+ pThis->NVRAM.offOpBuffer += cbUtf8;
+ }
+ else if (u32Value == 0)
+ Assert(pThis->NVRAM.VarOpBuf.szName[sizeof(pThis->NVRAM.VarOpBuf.szName) - 1] == 0);
+ else
+ {
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME_UTF16 write (%#x).\n", u32Value));
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ break;
+ }
+
+ case EFI_VM_VARIABLE_OP_VALUE:
+ Log2(("EFI_VM_VARIABLE_OP_VALUE[%#x]=%#x\n", pThis->NVRAM.offOpBuffer, u32Value));
+ if (pThis->NVRAM.offOpBuffer < pThis->NVRAM.VarOpBuf.cbValue)
+ pThis->NVRAM.VarOpBuf.abValue[pThis->NVRAM.offOpBuffer++] = (uint8_t)u32Value;
+ else
+ {
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_VALUE write (%#x).\n", u32Value));
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_VALUE_LENGTH:
+ Log2(("EFI_VM_VARIABLE_OP_VALUE_LENGTH=%#x\n", u32Value));
+ RT_ZERO(pThis->NVRAM.VarOpBuf.abValue);
+ if (u32Value <= sizeof(pThis->NVRAM.VarOpBuf.abValue))
+ pThis->NVRAM.VarOpBuf.cbValue = u32Value;
+ else
+ {
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_VALUE_LENGTH write (%#x, max %#x).\n",
+ u32Value, sizeof(pThis->NVRAM.VarOpBuf.abValue)));
+ pThis->NVRAM.VarOpBuf.cbValue = sizeof(pThis->NVRAM.VarOpBuf.abValue);
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ }
+ Assert(pThis->NVRAM.offOpBuffer == 0);
+ break;
+
+ default:
+ pThis->NVRAM.u32Status = EFI_VARIABLE_OP_STATUS_ERROR;
+ LogRel(("EFI: Unexpected variable operation %#x\n", pThis->NVRAM.enmOp));
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Implements EFI_VARIABLE_OP reads.
+ *
+ * @returns IOM strict status code.
+ * @param pThis The EFI state.
+ * @param u32Value The value being written.
+ */
+static int nvramReadVariableOp(PDEVEFI pThis, uint32_t *pu32, unsigned cb)
+{
+ switch (pThis->NVRAM.enmOp)
+ {
+ case EFI_VM_VARIABLE_OP_START:
+ *pu32 = pThis->NVRAM.u32Status;
+ break;
+
+ case EFI_VM_VARIABLE_OP_GUID:
+ if (pThis->NVRAM.offOpBuffer < sizeof(pThis->NVRAM.VarOpBuf.uuid) && cb == 1)
+ *pu32 = pThis->NVRAM.VarOpBuf.uuid.au8[pThis->NVRAM.offOpBuffer++];
+ else
+ {
+ if (cb == 1)
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_GUID read.\n"));
+ else
+ LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_GUID read size (%d).\n", cb));
+ *pu32 = UINT32_MAX;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_ATTRIBUTE:
+ *pu32 = pThis->NVRAM.VarOpBuf.fAttributes;
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME:
+ /* allow reading terminator char */
+ if (pThis->NVRAM.offOpBuffer <= pThis->NVRAM.VarOpBuf.cchName && cb == 1)
+ *pu32 = pThis->NVRAM.VarOpBuf.szName[pThis->NVRAM.offOpBuffer++];
+ else
+ {
+ if (cb == 1)
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME read.\n"));
+ else
+ LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_NAME read size (%d).\n", cb));
+ *pu32 = UINT32_MAX;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME_LENGTH:
+ *pu32 = pThis->NVRAM.VarOpBuf.cchName;
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME_UTF16:
+ /* Lazy bird: ASSUME no surrogate pairs. */
+ if (pThis->NVRAM.offOpBuffer <= pThis->NVRAM.VarOpBuf.cchName && cb == 2)
+ {
+ char const *psz1 = &pThis->NVRAM.VarOpBuf.szName[pThis->NVRAM.offOpBuffer];
+ char const *psz2 = psz1;
+ RTUNICP Cp;
+ RTStrGetCpEx(&psz2, &Cp);
+ *pu32 = Cp;
+ Log2(("EFI_VM_VARIABLE_OP_NAME_UTF16[%u] => %#x (+%d)\n", pThis->NVRAM.offOpBuffer, *pu32, psz2 - psz1));
+ pThis->NVRAM.offOpBuffer += psz2 - psz1;
+ }
+ else
+ {
+ if (cb == 2)
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_NAME_UTF16 read.\n"));
+ else
+ LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_NAME_UTF16 read size (%d).\n", cb));
+ *pu32 = UINT32_MAX;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_NAME_LENGTH_UTF16:
+ /* Lazy bird: ASSUME no surrogate pairs. */
+ *pu32 = (uint32_t)RTStrUniLen(pThis->NVRAM.VarOpBuf.szName);
+ break;
+
+ case EFI_VM_VARIABLE_OP_VALUE:
+ if (pThis->NVRAM.offOpBuffer < pThis->NVRAM.VarOpBuf.cbValue && cb == 1)
+ *pu32 = pThis->NVRAM.VarOpBuf.abValue[pThis->NVRAM.offOpBuffer++];
+ else
+ {
+ if (cb == 1)
+ LogRel(("EFI: Out of bounds EFI_VM_VARIABLE_OP_VALUE read.\n"));
+ else
+ LogRel(("EFI: Invalid EFI_VM_VARIABLE_OP_VALUE read size (%d).\n", cb));
+ *pu32 = UINT32_MAX;
+ }
+ break;
+
+ case EFI_VM_VARIABLE_OP_VALUE_LENGTH:
+ *pu32 = pThis->NVRAM.VarOpBuf.cbValue;
+ break;
+
+ default:
+ *pu32 = UINT32_MAX;
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the EFI variable value looks like a printable UTF-8 string.
+ *
+ * @returns true if it is, false if not.
+ * @param pEfiVar The variable.
+ * @param pfZeroTerm Where to return whether the string is zero
+ * terminated.
+ */
+static bool efiInfoNvramIsUtf8(PCEFIVAR pEfiVar, bool *pfZeroTerm)
+{
+ if (pEfiVar->cbValue < 2)
+ return false;
+ const char *pachValue = (const char *)&pEfiVar->abValue[0];
+ *pfZeroTerm = pachValue[pEfiVar->cbValue - 1] == 0;
+
+ /* Check the length. */
+ size_t cchValue = RTStrNLen((const char *)pEfiVar->abValue, pEfiVar->cbValue);
+ if (cchValue != pEfiVar->cbValue - *pfZeroTerm)
+ return false; /* stray zeros in the value, forget it. */
+
+ /* Check that the string is valid UTF-8 and printable. */
+ const char *pchCur = pachValue;
+ while ((uintptr_t)(pchCur - pachValue) < cchValue)
+ {
+ RTUNICP uc;
+ int rc = RTStrGetCpEx(&pachValue, &uc);
+ if (RT_FAILURE(rc))
+ return false;
+ /** @todo Missing RTUniCpIsPrintable. */
+ if (uc < 128 && !RT_C_IS_PRINT(uc))
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks if the EFI variable value looks like a printable UTF-16 string.
+ *
+ * @returns true if it is, false if not.
+ * @param pEfiVar The variable.
+ * @param pfZeroTerm Where to return whether the string is zero
+ * terminated.
+ */
+static bool efiInfoNvramIsUtf16(PCEFIVAR pEfiVar, bool *pfZeroTerm)
+{
+ if (pEfiVar->cbValue < 4 || (pEfiVar->cbValue & 1))
+ return false;
+
+ PCRTUTF16 pwcValue = (PCRTUTF16)&pEfiVar->abValue[0];
+ size_t cwcValue = pEfiVar->cbValue / sizeof(RTUTF16);
+ *pfZeroTerm = pwcValue[cwcValue - 1] == 0;
+ if (!*pfZeroTerm && RTUtf16IsHighSurrogate(pwcValue[cwcValue - 1]))
+ return false; /* Catch bad string early, before reading a char too many. */
+ cwcValue -= *pfZeroTerm;
+ if (cwcValue < 2)
+ return false;
+
+ /* Check that the string is valid UTF-16, printable and spans the whole
+ value length. */
+ size_t cAscii = 0;
+ PCRTUTF16 pwcCur = pwcValue;
+ while ((uintptr_t)(pwcCur - pwcValue) < cwcValue)
+ {
+ RTUNICP uc;
+ int rc = RTUtf16GetCpEx(&pwcCur, &uc);
+ if (RT_FAILURE(rc))
+ return false;
+ /** @todo Missing RTUniCpIsPrintable. */
+ if (uc < 128 && !RT_C_IS_PRINT(uc))
+ return false;
+ cAscii += uc < 128;
+ }
+ if (cAscii < 2)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * @implement_callback_method{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) efiInfoNvram(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
+ PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
+
+ pHlp->pfnPrintf(pHlp, "NVRAM variables: %u\n", pThis->NVRAM.cVariables);
+ PCEFIVAR pEfiVar;
+ RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
+ {
+ /* Detect UTF-8 and UTF-16 strings. */
+ bool fZeroTerm = false;
+ if (efiInfoNvramIsUtf8(pEfiVar, &fZeroTerm))
+ pHlp->pfnPrintf(pHlp,
+ "Variable - fAttr=%#04x - '%RTuuid:%s' - cb=%#04x\n"
+ "String value (UTF-8%s): \"%.*s\"\n",
+ pEfiVar->fAttributes, &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cbValue,
+ fZeroTerm ? "" : ",nz", pEfiVar->cbValue, pEfiVar->abValue);
+ else if (efiInfoNvramIsUtf16(pEfiVar, &fZeroTerm))
+ pHlp->pfnPrintf(pHlp,
+ "Variable - fAttr=%#04x - '%RTuuid:%s' - cb=%#04x\n"
+ "String value (UTF-16%s): \"%.*ls\"\n",
+ pEfiVar->fAttributes, &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cbValue,
+ fZeroTerm ? "" : ",nz", pEfiVar->cbValue, pEfiVar->abValue);
+ else
+ pHlp->pfnPrintf(pHlp,
+ "Variable - fAttr=%#04x - '%RTuuid:%s' - cb=%#04x\n"
+ "%.*Rhxd\n",
+ pEfiVar->fAttributes, &pEfiVar->uuid, pEfiVar->szName, pEfiVar->cbValue,
+ pEfiVar->cbValue, pEfiVar->abValue);
+
+ }
+
+ PDMCritSectLeave(pDevIns->pCritSectRoR3);
+}
+
+
+
+/**
+ * Gets the info item size.
+ *
+ * @returns Size in bytes, UINT32_MAX on error.
+ * @param pThis .
+ */
static uint32_t efiInfoSize(PDEVEFI pThis)
{
switch (pThis->iInfoSelector)
@@ -161,79 +1078,229 @@ static uint32_t efiInfoSize(PDEVEFI pThis)
case EFI_INFO_INDEX_UGA_HORISONTAL_RESOLUTION:
return 4;
case EFI_INFO_INDEX_BOOT_ARGS:
- return (uint32_t)RTStrNLen(pThis->szBootArgs,
- sizeof pThis->szBootArgs) + 1;
+ return (uint32_t)RTStrNLen(pThis->szBootArgs, sizeof(pThis->szBootArgs)) + 1;
case EFI_INFO_INDEX_DEVICE_PROPS:
- return pThis->u32DevicePropsLen;
+ return pThis->cbDeviceProps;
case EFI_INFO_INDEX_FSB_FREQUENCY:
case EFI_INFO_INDEX_CPU_FREQUENCY:
case EFI_INFO_INDEX_TSC_FREQUENCY:
return 8;
}
- Assert(false);
- return 0;
+ return UINT32_MAX;
}
-static uint8_t efiInfoNextByte(PDEVEFI pThis)
+
+/**
+ * efiInfoNextByte for a uint64_t value.
+ *
+ * @returns Next (current) byte.
+ * @param pThis The EFI instance data.
+ * @param u64 The value.
+ */
+static uint8_t efiInfoNextByteU64(PDEVEFI pThis, uint64_t u64)
{
- union
- {
- uint32_t u32;
- uint64_t u64;
- } value;
+ uint64_t off = pThis->offInfo;
+ if (off >= 8)
+ return 0;
+ return (uint8_t)(u64 >> (off * 8));
+}
+
+/**
+ * efiInfoNextByte for a uint32_t value.
+ *
+ * @returns Next (current) byte.
+ * @param pThis The EFI instance data.
+ * @param u32 The value.
+ */
+static uint8_t efiInfoNextByteU32(PDEVEFI pThis, uint32_t u32)
+{
+ uint32_t off = pThis->offInfo;
+ if (off >= 4)
+ return 0;
+ return (uint8_t)(u32 >> (off * 8));
+}
+
+/**
+ * efiInfoNextByte for a buffer.
+ *
+ * @returns Next (current) byte.
+ * @param pThis The EFI instance data.
+ * @param pvBuf The buffer.
+ * @param cbBuf The buffer size.
+ */
+static uint8_t efiInfoNextByteBuf(PDEVEFI pThis, void const *pvBuf, size_t cbBuf)
+{
+ uint32_t off = pThis->offInfo;
+ if (off >= cbBuf)
+ return 0;
+ return ((uint8_t const *)pvBuf)[off];
+}
+/**
+ * Gets the next info byte.
+ *
+ * @returns Next (current) byte.
+ * @param pThis The EFI instance data.
+ */
+static uint8_t efiInfoNextByte(PDEVEFI pThis)
+{
switch (pThis->iInfoSelector)
{
- case EFI_INFO_INDEX_VOLUME_BASE:
- value.u32 = pThis->GCLoadAddress;
- break;
- case EFI_INFO_INDEX_VOLUME_SIZE:
- value.u32 = pThis->cbEfiRom;
- break;
- case EFI_INFO_INDEX_TEMPMEM_BASE:
- value.u32 = VBOX_EFI_TOP_OF_STACK; /* just after stack */
- break;
- case EFI_INFO_INDEX_TEMPMEM_SIZE:
- value.u32 = 512 * 1024; /* 512 K */
- break;
- case EFI_INFO_INDEX_STACK_BASE:
- /* Keep in sync with value in EfiThunk.asm */
- value.u32 = VBOX_EFI_TOP_OF_STACK - 128*1024; /* 2M - 128 K */
- break;
- case EFI_INFO_INDEX_STACK_SIZE:
- value.u32 = 128*1024; /* 128 K */
- break;
- case EFI_INFO_INDEX_FSB_FREQUENCY:
- value.u64 = pThis->u64FsbFrequency;
- break;
- case EFI_INFO_INDEX_TSC_FREQUENCY:
- value.u64 = pThis->u64TscFrequency;
- break;
- case EFI_INFO_INDEX_CPU_FREQUENCY:
- value.u64 = pThis->u64CpuFrequency;
- break;
- case EFI_INFO_INDEX_BOOT_ARGS:
- return pThis->szBootArgs[pThis->iInfoPosition];
- case EFI_INFO_INDEX_DEVICE_PROPS:
- return pThis->pu8DeviceProps[pThis->iInfoPosition];
- case EFI_INFO_INDEX_GOP_MODE:
- value.u32 = pThis->u32GopMode;
- break;
- case EFI_INFO_INDEX_UGA_HORISONTAL_RESOLUTION:
- value.u32 = pThis->u32UgaHorisontal;
- break;
- case EFI_INFO_INDEX_UGA_VERTICAL_RESOLUTION:
- value.u32 = pThis->u32UgaVertical;
- break;
+
+ case EFI_INFO_INDEX_VOLUME_BASE: return efiInfoNextByteU64(pThis, pThis->GCLoadAddress);
+ case EFI_INFO_INDEX_VOLUME_SIZE: return efiInfoNextByteU64(pThis, pThis->cbEfiRom);
+ case EFI_INFO_INDEX_TEMPMEM_BASE: return efiInfoNextByteU32(pThis, VBOX_EFI_TOP_OF_STACK); /* just after stack */
+ case EFI_INFO_INDEX_TEMPMEM_SIZE: return efiInfoNextByteU32(pThis, _512K);
+ case EFI_INFO_INDEX_FSB_FREQUENCY: return efiInfoNextByteU64(pThis, pThis->u64FsbFrequency);
+ case EFI_INFO_INDEX_TSC_FREQUENCY: return efiInfoNextByteU64(pThis, pThis->u64TscFrequency);
+ case EFI_INFO_INDEX_CPU_FREQUENCY: return efiInfoNextByteU64(pThis, pThis->u64CpuFrequency);
+ case EFI_INFO_INDEX_BOOT_ARGS: return efiInfoNextByteBuf(pThis, pThis->szBootArgs, sizeof(pThis->szBootArgs));
+ case EFI_INFO_INDEX_DEVICE_PROPS: return efiInfoNextByteBuf(pThis, pThis->pbDeviceProps, pThis->cbDeviceProps);
+ case EFI_INFO_INDEX_GOP_MODE: return efiInfoNextByteU32(pThis, pThis->u32GopMode);
+ case EFI_INFO_INDEX_UGA_HORISONTAL_RESOLUTION: return efiInfoNextByteU32(pThis, pThis->cxUgaResolution);
+ case EFI_INFO_INDEX_UGA_VERTICAL_RESOLUTION: return efiInfoNextByteU32(pThis, pThis->cyUgaResolution);
+
+ /* Keep in sync with value in EfiThunk.asm */
+ case EFI_INFO_INDEX_STACK_BASE: return efiInfoNextByteU32(pThis, VBOX_EFI_TOP_OF_STACK - _128K); /* 2M - 128 K */
+ case EFI_INFO_INDEX_STACK_SIZE: return efiInfoNextByteU32(pThis, _128K);
+
default:
- Assert(false);
- value.u64 = 0;
- break;
+ PDMDevHlpDBGFStop(pThis->pDevIns, RT_SRC_POS, "%#x", pThis->iInfoSelector);
+ return 0;
+ }
+}
+
+
+#ifdef IN_RING3
+static void efiVBoxDbgScript(PDEVEFI pThis, 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);
+ }
+# endif
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Handles writes to the image event port.
+ *
+ * @returns VBox status suitable for I/O port write handler.
+ *
+ * @param pThis The EFI state.
+ * @param u32 The value being written.
+ * @param cb The size of the value.
+ */
+static int efiPortImageEventWrite(PDEVEFI pThis, uint32_t u32, unsigned 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:
+ AssertBreak(EFI_IMAGE_EVT_GET_PAYLOAD(u32) == 0);
+
+ /* Reset the state. */
+ RT_ZERO(pThis->ImageEvt);
+ pThis->ImageEvt.uEvt = (uint8_t)u32; Assert(pThis->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 (pThis->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",
+ pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->ImageEvt.uAddr0, pThis->ImageEvt.cb0));
+ if (pThis->ImageEvt.offName > 4)
+ efiVBoxDbgScript(pThis, "loadimage32 '%.*s.efi' %#llx\n",
+ pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->ImageEvt.uAddr0);
+ break;
+ case EFI_IMAGE_EVT_CMD_START_LOAD64:
+ LogRel(("EFI: VBoxDbg> loadimage64 '%.*s.efi' %#llx LB %#llx\n",
+ pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->ImageEvt.uAddr0, pThis->ImageEvt.cb0));
+ if (pThis->ImageEvt.offName > 4)
+ efiVBoxDbgScript(pThis, "loadimage64 '%.*s.efi' %#llx\n",
+ pThis->ImageEvt.offName - 4, pThis->ImageEvt.szName, pThis->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",
+ pThis->ImageEvt.offName - 4 - pThis->ImageEvt.offNameLastComponent,
+ &pThis->ImageEvt.szName[pThis->ImageEvt.offNameLastComponent],
+ pThis->ImageEvt.uAddr0, pThis->ImageEvt.cb0));
+ if (pThis->ImageEvt.offName > 4)
+ efiVBoxDbgScript(pThis, "unload '%.*s.efi'\n",
+ pThis->ImageEvt.offName - 4 - pThis->ImageEvt.offNameLastComponent,
+ &pThis->ImageEvt.szName[pThis->ImageEvt.offNameLastComponent]);
+ 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);
+ pThis->ImageEvt.uAddr0 <<= 16;
+ pThis->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);
+ pThis->ImageEvt.uAddr0 <<= 16;
+ pThis->ImageEvt.uAddr0 |= 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);
+ pThis->ImageEvt.cb0 <<= 16;
+ pThis->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 (pThis->ImageEvt.offName < sizeof(pThis->ImageEvt.szName) - 1)
+ {
+ char ch = EFI_IMAGE_EVT_GET_PAYLOAD_U8(u32);
+ if (ch == '\\')
+ ch = '/';
+ pThis->ImageEvt.szName[pThis->ImageEvt.offName++] = ch;
+ if (ch == '/' || ch == ':')
+ pThis->ImageEvt.offNameLastComponent = pThis->ImageEvt.offName;
+ }
+ else
+ Log(("EFI: Image name overflow\n"));
+ return VINF_SUCCESS;
}
- return *((uint8_t*)&value+pThis->iInfoPosition);
+ Log(("EFI: Unknown image event: %#x (cb=%d)\n", u32, cb));
+ return VINF_SUCCESS;
}
+
/**
* Port I/O Handler for IN operations.
*
@@ -253,30 +1320,41 @@ static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPOR
switch (Port)
{
case EFI_INFO_PORT:
- if (pThis->iInfoPosition == -1 && cb == 4)
+ if (pThis->offInfo == -1 && cb == 4)
{
- *pu32 = efiInfoSize(pThis);
- pThis->iInfoPosition = 0;
+ pThis->offInfo = 0;
+ uint32_t cbInfo = *pu32 = efiInfoSize(pThis);
+ if (cbInfo == UINT32_MAX)
+ return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "iInfoSelector=%#x (%d)\n",
+ pThis->iInfoSelector, pThis->iInfoSelector);
}
else
{
- /* So far */
if (cb != 1)
return VERR_IOM_IOPORT_UNUSED;
*pu32 = efiInfoNextByte(pThis);
- pThis->iInfoPosition++;
+ pThis->offInfo++;
}
return VINF_SUCCESS;
- case EFI_PANIC_PORT:
+ case EFI_PANIC_PORT:
#ifdef IN_RING3
- LogRel(("Panic port read!\n"));
+ 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:
+ return nvramReadVariableOp(pThis, pu32, cb);
+
+ 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;
@@ -284,6 +1362,31 @@ static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPOR
/**
+ * 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";
+ }
+}
+
+
+/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
@@ -297,14 +1400,17 @@ static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPOR
static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
+ int rc = VINF_SUCCESS;
Log4(("efi: out %x %x %d\n", Port, u32, cb));
switch (Port)
{
case EFI_INFO_PORT:
+ Log2(("EFI_INFO_PORT: iInfoSelector=%#x\n", u32));
pThis->iInfoSelector = u32;
- pThis->iInfoPosition = -1;
+ pThis->offInfo = -1;
break;
+
case EFI_DEBUG_PORT:
{
/* The raw version. */
@@ -320,25 +1426,7 @@ static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPO
{
pThis->szMsg[pThis->iMsg] = '\0';
if (pThis->iMsg)
- {
Log(("efi: %s\n", pThis->szMsg));
-#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
- const char *pszVBoxDbg = strstr(pThis->szMsg, "VBoxDbg> ");
- if (pszVBoxDbg)
- {
- pszVBoxDbg += sizeof("VBoxDbg> ") - 1;
-
- PRTSTREAM pStrm;
- int rc = RTStrmOpen("./DevEFI.VBoxDbg", "a", &pStrm);
- if (RT_SUCCESS(rc))
- {
- RTStrmPutStr(pStrm, pszVBoxDbg);
- RTStrmPutCh(pStrm, '\n');
- RTStrmClose(pStrm);
- }
- }
-#endif
- }
pThis->iMsg = 0;
}
else
@@ -359,35 +1447,39 @@ static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPO
{
switch (u32)
{
- case EFI_PANIC_CMD_BAD_ORG:
- LogRel(("EFI Panic: You have to fix ORG offset in EfiThunk.asm! Must be 0x%x\n",
- g_cbEfiThunkBinary));
- RTAssertMsg2Weak("Fix ORG offset in EfiThunk.asm: must be 0x%x\n",
- g_cbEfiThunkBinary);
- break;
-
+ case EFI_PANIC_CMD_BAD_ORG: /* Legacy */
case EFI_PANIC_CMD_THUNK_TRAP:
- LogRel(("EFI Panic: Unexpected trap!!\n"));
-#ifdef VBOX_STRICT
+#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
+# else
AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
-#endif
+# endif
break;
+#else
+ return VINF_IOM_R3_IOPORT_WRITE;
+#endif
case EFI_PANIC_CMD_START_MSG:
+ LogRel(("Receiving EFI panic...\n"));
pThis->iPanicMsg = 0;
pThis->szPanicMsg[0] = '\0';
break;
case EFI_PANIC_CMD_END_MSG:
- LogRel(("EFI Panic: %s\n", pThis->szPanicMsg));
-#ifdef VBOX_STRICT
+#ifdef IN_RING3
+ LogRel(("EFI: Panic! %s\n", pThis->szPanicMsg));
+# ifdef VBOX_STRICT
return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: %s\n", pThis->szPanicMsg);
-#else
+# 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)
@@ -413,13 +1505,199 @@ static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPO
break;
}
+ case EFI_PORT_VARIABLE_OP:
+ {
+ /* clear buffer index */
+ if (u32 >= (uint32_t)EFI_VM_VARIABLE_OP_MAX)
+ {
+ Log(("EFI: Invalid variable op %#x\n", u32));
+ u32 = EFI_VM_VARIABLE_OP_ERROR;
+ }
+ pThis->NVRAM.offOpBuffer = 0;
+ pThis->NVRAM.enmOp = (EFIVAROP)u32;
+ Log2(("EFI_VARIABLE_OP: enmOp=%#x (%d)\n", u32));
+ break;
+ }
+
+ case EFI_PORT_VARIABLE_PARAM:
+ rc = nvramWriteVariableParam(pThis, u32);
+ break;
+
+ case EFI_PORT_DEBUG_POINT:
+#ifdef IN_RING3
+ if (u32 > EFIDBGPOINT_INVALID && u32 < EFIDBGPOINT_END)
+ {
+ /* For now, just log it. */
+ static uint64_t s_cDbgPointLogged = 0;
+ if (s_cDbgPointLogged < 1024)
+ {
+ s_cDbgPointLogged++;
+ LogRel(("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(pThis, u32, cb);
+ break;
+
default:
Log(("EFI: Write to reserved port %RTiop: %#x (cb=%d)\n", Port, u32, cb));
break;
}
+ return rc;
+}
+
+static DECLCALLBACK(int) efiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
+ LogFlow(("efiSaveExec:\n"));
+
+ /*
+ * Set variables only used when saving state.
+ */
+ uint32_t idUniqueSavedState = 0;
+ PEFIVAR pEfiVar;
+ RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
+ {
+ pEfiVar->idUniqueSavedState = idUniqueSavedState++;
+ }
+ Assert(idUniqueSavedState == pThis->NVRAM.cVariables);
+
+ pThis->NVRAM.idUniqueCurVar = pThis->NVRAM.pCurVar
+ ? pThis->NVRAM.pCurVar->idUniqueSavedState
+ : UINT32_MAX;
+
+ /*
+ * Save the NVRAM state.
+ */
+ SSMR3PutStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
+ SSMR3PutStructEx(pSSM, &pThis->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
+
+ /*
+ * Save the list variables (we saved the length above).
+ */
+ RTListForEach(&pThis->NVRAM.VarList, pEfiVar, EFIVAR, ListNode)
+ {
+ SSMR3PutStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
+ }
+
+ return VINF_SUCCESS; /* SSM knows */
+}
+
+static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
+ 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_4_2
+ )
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /*
+ * Kill the current variables before loading anything.
+ */
+ nvramFlushDeviceVariableList(pThis);
+
+ /*
+ * Load the NVRAM state.
+ */
+ int rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM, sizeof(NVRAMDESC), 0, g_aEfiNvramDescField, NULL);
+ AssertRCReturn(rc, rc);
+ pThis->NVRAM.pCurVar = NULL;
+
+ rc = SSMR3GetStructEx(pSSM, &pThis->NVRAM.VarOpBuf, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Load variables.
+ */
+ pThis->NVRAM.pCurVar = NULL;
+ Assert(RTListIsEmpty(&pThis->NVRAM.VarList));
+ RTListInit(&pThis->NVRAM.VarList);
+ for (uint32_t i = 0; i < pThis->NVRAM.cVariables; i++)
+ {
+ PEFIVAR pEfiVar = (PEFIVAR)RTMemAllocZ(sizeof(EFIVAR));
+ AssertPtrReturn(pEfiVar, VERR_NO_MEMORY);
+
+ rc = SSMR3GetStructEx(pSSM, pEfiVar, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pEfiVar->cbValue > sizeof(pEfiVar->abValue)
+ || pEfiVar->cbValue == 0)
+ {
+ rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ LogRel(("EFI: Loaded invalid variable value length %#x\n", pEfiVar->cbValue));
+ }
+ uint32_t cchVarName = (uint32_t)RTStrNLen(pEfiVar->szName, sizeof(pEfiVar->szName));
+ if (cchVarName >= sizeof(pEfiVar->szName))
+ {
+ rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ LogRel(("EFI: Loaded variable name is unterminated.\n"));
+ }
+ if (pEfiVar->cchName > cchVarName) /* No check for 0 here, busted load code in 4.2, so now storing 0 here. */
+ {
+ rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ LogRel(("EFI: Loaded invalid variable name length %#x (cchVarName=%#x)\n", pEfiVar->cchName, cchVarName));
+ }
+ if (RT_SUCCESS(rc))
+ pEfiVar->cchName = cchVarName;
+ }
+ AssertRCReturnStmt(rc, RTMemFree(pEfiVar), rc);
+
+ /* Add it (not using nvramInsertVariable to preserve saved order),
+ updating the current variable pointer while we're here. */
+#if 1
+ RTListAppend(&pThis->NVRAM.VarList, &pEfiVar->ListNode);
+#else
+ nvramInsertVariable(pThis, pEfiVar);
+#endif
+ if (pThis->NVRAM.idUniqueCurVar == pEfiVar->idUniqueSavedState)
+ pThis->NVRAM.pCurVar = pEfiVar;
+ }
+
return VINF_SUCCESS;
}
+
+/**
+ * @copydoc(PDMIBASE::pfnQueryInterface)
+ */
+static DECLCALLBACK(void *) devEfiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ LogFlowFunc(("ENTER: pIBase: %p, pszIID:%p\n", __FUNCTION__, pInterface, pszIID));
+ PDEVEFI pThis = RT_FROM_MEMBER(pInterface, DEVEFI, Lun0.IBase);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->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.
*
@@ -428,14 +1706,13 @@ static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPO
*/
static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
{
- /* PC Bios */
PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
- uint32_t u32;
/*
* Memory sizes.
*/
uint64_t const offRamHole = _4G - pThis->cbRamHole;
+ uint32_t u32;
if (pThis->cbRam > 16 * _1M)
u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
else
@@ -451,31 +1728,18 @@ static DECLCALLBACK(int) efiInitComplete(PPDMDEVINS pDevIns)
return VINF_SUCCESS;
}
+
/**
- * Reset notification.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
+ * @interface_method_impl{PDMDEVREG,pfnMemSetup}
*/
-static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) efiMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
{
- PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
- int rc;
-
- LogFlow(("efiReset\n"));
-
- pThis->iInfoSelector = 0;
- pThis->iInfoPosition = -1;
-
- pThis->iMsg = 0;
- pThis->szMsg[0] = '\0';
- pThis->iPanicMsg = 0;
- pThis->szPanicMsg[0] = '\0';
+ PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
/*
* Plan some structures in RAM.
*/
- FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->cbDmiTables);
+ FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->cbDmiTables, pThis->cNumDmiTables);
if (pThis->u8IOAPIC)
FwCommonPlantMpsFloatPtr(pDevIns);
@@ -489,7 +1753,7 @@ static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
uint8_t abPage[PAGE_SIZE];
/* Read the (original) ROM page and write it back to the RAM page. */
- rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
+ int rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
AssertLogRelRC(rc);
rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
@@ -510,6 +1774,33 @@ static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
}
}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
+{
+ PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
+
+ LogFlow(("efiReset\n"));
+
+ pThis->iInfoSelector = 0;
+ pThis->offInfo = -1;
+
+ pThis->iMsg = 0;
+ pThis->szMsg[0] = '\0';
+ pThis->iPanicMsg = 0;
+ pThis->szPanicMsg[0] = '\0';
+
+#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
+ /*
+ * Zap the debugger script
+ */
+ RTFileDelete("./DevEFI.VBoxDbg");
+#endif
+}
+
+
/**
* Destruct a device instance.
*
@@ -521,16 +1812,21 @@ static DECLCALLBACK(void) efiReset(PPDMDEVINS pDevIns)
static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
{
PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+ if (pThis->Lun0.pNvramDrv)
+ nvramStore(pThis);
+ nvramFlushDeviceVariableList(pThis);
- /*
- * Free MM heap pointers.
- */
if (pThis->pu8EfiRom)
{
RTFileReadAllFree(pThis->pu8EfiRom, (size_t)pThis->cbEfiRom);
pThis->pu8EfiRom = NULL;
}
+ /*
+ * Free MM heap pointers (waste of time, but whatever).
+ */
if (pThis->pszEfiRomFile)
{
MMR3HeapFree(pThis->pszEfiRomFile);
@@ -543,11 +1839,11 @@ static DECLCALLBACK(int) efiDestruct(PPDMDEVINS pDevIns)
pThis->pu8EfiThunk = NULL;
}
- if (pThis->pu8DeviceProps)
+ if (pThis->pbDeviceProps)
{
- MMR3HeapFree(pThis->pu8DeviceProps);
- pThis->pu8DeviceProps = NULL;
- pThis->u32DevicePropsLen = 0;
+ MMR3HeapFree(pThis->pbDeviceProps);
+ pThis->pbDeviceProps = NULL;
+ pThis->cbDeviceProps = 0;
}
return VINF_SUCCESS;
@@ -577,136 +1873,10 @@ efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd
}
pFfsFile = (EFI_FFS_FILE_HEADER *)((uintptr_t)pFfsFile + RT_ALIGN(FFS_SIZE(pFfsFile), 8));
}
+#undef FFS_SIZE
return NULL;
}
-static int efiFindRelativeAddressOfEPAndBaseAddressOfModule(EFI_FFS_FILE_HEADER const *pFfsFile, uint32_t cbFfsFile, RTGCPHYS *pImageBase, uint8_t **ppbImage)
-{
- /*
- * Sections headers are lays at the beginning of block it describes,
- * the first section header is located immediately after FFS header.
- */
- EFI_FILE_SECTION_POINTER uSecHdrPtr;
- uint8_t const * const pbFfsFileEnd = (uint8_t *)pFfsFile + cbFfsFile;
- uint8_t const *pbImage = NULL;
- uint8_t const *pbSecHdr = (uint8_t const *)&pFfsFile[1]; /* FFS header has fixed size */
- for (; (uintptr_t)pbSecHdr < (uintptr_t)pbFfsFileEnd;
- pbSecHdr += SECTION_SIZE(uSecHdrPtr.CommonHeader))
- {
- uSecHdrPtr.CommonHeader = (EFI_COMMON_SECTION_HEADER *)pbSecHdr;
- if ( uSecHdrPtr.CommonHeader->Type == EFI_SECTION_PE32
- || uSecHdrPtr.CommonHeader->Type == EFI_SECTION_TE)
- {
- /*Here should be other code containing sections*/
- pbImage = (uint8_t const *)&uSecHdrPtr.Pe32Section[1]; /* the PE/PE+/TE headers begins just after the Section Header */
- break;
- }
- Log2(("EFI: Section of type:%d has been detected\n", uSecHdrPtr.CommonHeader->Type));
- }
- AssertLogRelMsgReturn(pbImage, ("Failed to find PE32 or TE section for the SECURITY_CORE FFS\n"), VERR_INVALID_PARAMETER);
-
- /*
- * Parse the image extracting the ImageBase and the EntryPoint.
- */
- int rc = VINF_SUCCESS;
- union EfiHdrUnion
- {
- EFI_IMAGE_DOS_HEADER Dos;
- EFI_IMAGE_NT_HEADERS32 Nt32;
- EFI_IMAGE_NT_HEADERS64 Nt64;
- EFI_TE_IMAGE_HEADER Te;
- };
- EfiHdrUnion const *pHdr = (EfiHdrUnion const *)pbImage;
-
- /* Skip MZ if found. */
- if (pHdr->Dos.e_magic == RT_MAKE_U16('M', 'Z'))
- {
- uint8_t const *pbNewHdr = (uint8_t const *)pHdr + pHdr->Dos.e_lfanew;
- AssertLogRelMsgReturn( (uintptr_t)pbNewHdr < (uintptr_t)pbFfsFileEnd
- && (uintptr_t)pbNewHdr >= (uintptr_t)&pHdr->Dos.e_lfanew + sizeof(pHdr->Dos.e_lfanew),
- ("%x\n", pHdr->Dos.e_lfanew),
- VERR_BAD_EXE_FORMAT);
- pHdr = (EfiHdrUnion const *)pbNewHdr;
- }
-
- RTGCPHYS ImageBase;
- RTGCPHYS EpRVA;
- if (pHdr->Nt32.Signature == RT_MAKE_U32_FROM_U8('P', 'E', 0, 0))
- {
- AssertLogRelMsgReturn( ( pHdr->Nt32.FileHeader.Machine == EFI_IMAGE_FILE_MACHINE_I386
- && pHdr->Nt32.FileHeader.SizeOfOptionalHeader == sizeof(pHdr->Nt32.OptionalHeader))
- || ( pHdr->Nt32.FileHeader.Machine == EFI_IMAGE_MACHINE_X64
- && pHdr->Nt32.FileHeader.SizeOfOptionalHeader == sizeof(pHdr->Nt64.OptionalHeader)),
- ("%x / %x\n", pHdr->Nt32.FileHeader.Machine, pHdr->Nt32.FileHeader.SizeOfOptionalHeader),
- VERR_LDR_ARCH_MISMATCH);
- EFI_IMAGE_SECTION_HEADER *pSectionsHeaders = NULL;
- int cSectionsHeaders = 0;
- if (pHdr->Nt32.FileHeader.Machine == EFI_IMAGE_FILE_MACHINE_I386)
- {
- Log2(("EFI: PE32/i386\n"));
- AssertLogRelMsgReturn(pHdr->Nt32.OptionalHeader.SizeOfImage < cbFfsFile,
- ("%#x / %#x\n", pHdr->Nt32.OptionalHeader.SizeOfImage, cbFfsFile),
- VERR_BAD_EXE_FORMAT);
- ImageBase = pHdr->Nt32.OptionalHeader.ImageBase;
- EpRVA = pHdr->Nt32.OptionalHeader.AddressOfEntryPoint;
- EpRVA -= pHdr->Nt32.OptionalHeader.BaseOfCode;
- AssertLogRelMsgReturn(EpRVA < pHdr->Nt32.OptionalHeader.SizeOfImage,
- ("%#RGp / %#x\n", EpRVA, pHdr->Nt32.OptionalHeader.SizeOfImage),
- VERR_BAD_EXE_FORMAT);
- pSectionsHeaders = (EFI_IMAGE_SECTION_HEADER *)((uint8_t *)&pHdr->Nt32.OptionalHeader + pHdr->Nt32.FileHeader.SizeOfOptionalHeader);
- cSectionsHeaders = pHdr->Nt32.FileHeader.NumberOfSections;
- }
- else
- {
- Log2(("EFI: PE+/AMD64 %RX16\n", pHdr->Nt32.FileHeader.Machine));
- AssertLogRelMsgReturn(pHdr->Nt64.OptionalHeader.SizeOfImage < cbFfsFile,
- ("%#x / %#x\n", pHdr->Nt64.OptionalHeader.SizeOfImage, cbFfsFile),
- VERR_BAD_EXE_FORMAT);
- ImageBase = pHdr->Nt64.OptionalHeader.ImageBase;
- EpRVA = pHdr->Nt64.OptionalHeader.AddressOfEntryPoint;
- EpRVA -= pHdr->Nt64.OptionalHeader.BaseOfCode;
- AssertLogRelMsgReturn(EpRVA < pHdr->Nt64.OptionalHeader.SizeOfImage,
- ("%#RGp / %#x\n", EpRVA, pHdr->Nt64.OptionalHeader.SizeOfImage),
- VERR_BAD_EXE_FORMAT);
- pSectionsHeaders = (EFI_IMAGE_SECTION_HEADER *)((uint8_t *)&pHdr->Nt64.OptionalHeader + pHdr->Nt64.FileHeader.SizeOfOptionalHeader);
- cSectionsHeaders = pHdr->Nt64.FileHeader.NumberOfSections;
- }
- AssertPtrReturn(pSectionsHeaders, VERR_BAD_EXE_FORMAT);
- int idxSection = 0;
- for (; idxSection < cSectionsHeaders; ++idxSection)
- {
- EFI_IMAGE_SECTION_HEADER *pSection = &pSectionsHeaders[idxSection];
- if (!RTStrCmp((const char *)&pSection->Name[0], ".text"))
- {
- EpRVA += pSection->PointerToRawData;
- break;
- }
- }
- }
- else if (pHdr->Te.Signature == RT_MAKE_U16('V', 'Z'))
- {
- /* TE header */
- Log2(("EFI: TE header\n"));
- AssertLogRelMsgReturn( pHdr->Te.Machine == EFI_IMAGE_FILE_MACHINE_I386
- || pHdr->Te.Machine == EFI_IMAGE_MACHINE_X64,
- ("%x\n", pHdr->Te.Machine),
- VERR_LDR_ARCH_MISMATCH);
- ImageBase = pHdr->Te.ImageBase;
- EpRVA = pHdr->Te.AddressOfEntryPoint;
- AssertLogRelMsgReturn(EpRVA < cbFfsFile,
- ("%#RGp / %#x\n", EpRVA, cbFfsFile),
- VERR_BAD_EXE_FORMAT);
- }
- else
- AssertLogRelMsgFailedReturn(("%#x\n", pHdr->Nt32.Signature), VERR_INVALID_EXE_SIGNATURE);
-
- Log2(("EFI: EpRVA=%RGp ImageBase=%RGp EntryPoint=%RGp\n", EpRVA, ImageBase, EpRVA + ImageBase));
- if (pImageBase != NULL)
- *pImageBase = ImageBase;
- if (ppbImage != NULL)
- *ppbImage = (uint8_t *)pbImage;
- return (EpRVA);
-}
/**
* Parse EFI ROM headers and find entry points.
@@ -739,47 +1909,8 @@ static int efiParseFirmware(PDEVEFI pThis)
AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
uint8_t const * const pbFwVolEnd = pThis->pu8EfiRom + pFwVolHdr->FvLength;
+ pThis->GCLoadAddress = UINT32_C(0xfffff000) - pThis->cbEfiRom + PAGE_SIZE;
- /*
- * Ffs files are stored one by one, so to find SECURITY_CORE we've to
- * search thru every one on the way.
- */
- uint32_t cbFfsFile = 0; /* shut up gcc */
- EFI_FFS_FILE_HEADER const *pFfsFile = (EFI_FFS_FILE_HEADER const *)(pThis->pu8EfiRom + pFwVolHdr->HeaderLength);
- pFfsFile = efiFwVolFindFileByType(pFfsFile, pbFwVolEnd, EFI_FV_FILETYPE_SECURITY_CORE, &cbFfsFile);
- AssertLogRelMsgReturn(pFfsFile, ("No SECURITY_CORE found in the firmware volume\n"), VERR_FILE_NOT_FOUND);
-
- RTGCPHYS ImageBase = NIL_RTGCPHYS;
- uint8_t *pbImage = NULL;
- pThis->GCEntryPoint0 = efiFindRelativeAddressOfEPAndBaseAddressOfModule(pFfsFile, cbFfsFile, &ImageBase, &pbImage);
- pThis->GCEntryPoint0 += pbImage - pThis->pu8EfiRom;
- Assert(pThis->pu8EfiRom <= pbImage);
- Assert(pbImage < pThis->pu8EfiRom + pThis->cbEfiRom);
- /*
- * Calc the firmware load address from the image base and validate it.
- */
- pThis->GCLoadAddress = ImageBase - (pbImage - pThis->pu8EfiRom);
- pThis->GCEntryPoint0 += pThis->GCLoadAddress;
- AssertLogRelMsgReturn(~(pThis->GCLoadAddress & PAGE_OFFSET_MASK),
- ("%RGp\n", pThis->GCLoadAddress),
- VERR_INVALID_PARAMETER);
- AssertLogRelMsgReturn(pThis->GCLoadAddress > UINT32_C(0xf0000000),
- ("%RGp\n", pThis->GCLoadAddress),
- VERR_OUT_OF_RANGE);
- AssertLogRelMsgReturn( pThis->GCLoadAddress + (pThis->cbEfiRom - 1) > UINT32_C(0xf0000000)
- && pThis->GCLoadAddress + (pThis->cbEfiRom - 1) < UINT32_C(0xffffe000),
- ("%RGp + %RX64\n", pThis->GCLoadAddress, pThis->cbEfiRom),
- VERR_OUT_OF_RANGE);
-
- LogRel(("EFI: Firmware volume loading at %RGp, SEC CORE at %RGp with EP at %RGp\n",
- pThis->GCLoadAddress, ImageBase, pThis->GCEntryPoint0));
-
- pFfsFile = efiFwVolFindFileByType(pFfsFile, pbFwVolEnd, EFI_FV_FILETYPE_PEI_CORE, &cbFfsFile);
- pThis->GCEntryPoint1 = efiFindRelativeAddressOfEPAndBaseAddressOfModule(pFfsFile, cbFfsFile, NULL, &pbImage);
- pThis->GCEntryPoint1 += pThis->GCLoadAddress;
- pThis->GCEntryPoint1 += pbImage - pThis->pu8EfiRom;
- LogRel(("EFI: Firmware volume loading at %RGp, PEI CORE at with EP at %RGp\n",
- pThis->GCLoadAddress, pThis->GCEntryPoint1));
return VINF_SUCCESS;
}
@@ -864,86 +1995,6 @@ static int efiLoadRom(PDEVEFI pThis, PCFGMNODE pCfg)
return VINF_SUCCESS;
}
-/**
- * Patches and loads the EfiThunk ROM image.
- *
- * The thunk image is where the CPU starts and will switch it into
- * 32-bit protected or long mode and invoke the SEC CORE image in the
- * firmware volume. It also contains some static VM configuration data
- * at the very beginning of the page, see DEVEFIINFO.
- *
- * @returns VBox status code.
- * @param pThis The device instance data.
- * @param pCfg Configuration node handle for the device.
- */
-static int efiLoadThunk(PDEVEFI pThis, PCFGMNODE pCfg)
-{
- uint8_t f64BitEntry = 0;
- int rc;
-
- rc = CFGMR3QueryU8Def(pCfg, "64BitEntry", &f64BitEntry, 0);
- if (RT_FAILURE (rc))
- return PDMDEV_SET_ERROR(pThis->pDevIns, rc,
- N_("Configuration error: Failed to read \"64BitEntry\""));
-
- /*
- * Make a copy of the page and set the values of the DEVEFIINFO structure
- * found at the beginning of it.
- */
-
- if (f64BitEntry)
- LogRel(("Using 64-bit EFI firmware\n"));
-
- /* Duplicate the page so we can change it. */
- AssertRelease(g_cbEfiThunkBinary == PAGE_SIZE);
- pThis->pu8EfiThunk = (uint8_t *)PDMDevHlpMMHeapAlloc(pThis->pDevIns, PAGE_SIZE);
- if (pThis->pu8EfiThunk == NULL)
- return VERR_NO_MEMORY;
- memcpy(pThis->pu8EfiThunk, &g_abEfiThunkBinary[0], PAGE_SIZE);
-
- /* Fill in the info. */
- PDEVEFIINFO pEfiInfo = (PDEVEFIINFO)pThis->pu8EfiThunk;
- pEfiInfo->pfnFirmwareEP = (uint32_t)pThis->GCEntryPoint0;
- //AssertRelease(pEfiInfo->pfnFirmwareEP == pThis->GCEntryPoint0);
- pEfiInfo->HighEPAddress = 0;
- pEfiInfo->PhysFwVol = pThis->GCLoadAddress;
- pEfiInfo->cbFwVol = (uint32_t)pThis->cbEfiRom;
- AssertRelease(pEfiInfo->cbFwVol == (uint32_t)pThis->cbEfiRom);
- pEfiInfo->cbBelow4GB = pThis->cbBelow4GB;
- pEfiInfo->cbAbove4GB = pThis->cbAbove4GB;
- /* zeroth bit controls use of 64-bit entry point in fw */
- pEfiInfo->fFlags = f64BitEntry ? 1 : 0;
- pEfiInfo->cCpus = pThis->cCpus;
- pEfiInfo->pfnPeiEP = (uint32_t)pThis->GCEntryPoint1;
- pEfiInfo->u32Reserved2 = 0;
-
- /* Register the page as a ROM (data will be copied). */
- rc = PDMDevHlpROMRegister(pThis->pDevIns, UINT32_C(0xfffff000), PAGE_SIZE,
- pThis->pu8EfiThunk, PAGE_SIZE,
- PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Thunk");
- if (RT_FAILURE(rc))
- return rc;
-
-#if 1 /** @todo this is probably not necessary. */
- /*
- * Map thunk page also at low address, so that real->protected mode jump code can
- * store GDT/IDT in code segment in low memory and load them during switch to the
- * protected mode, while being in 16-bits mode.
- *
- * @todo: maybe need to unregister later or place somewhere else (although could
- * be needed during reset)
- */
- rc = PDMDevHlpROMRegister(pThis->pDevIns, 0xff000, PAGE_SIZE,
- pThis->pu8EfiThunk, PAGE_SIZE,
- PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Thunk (2)");
- if (RT_FAILURE(rc))
- return rc;
-#endif
-
- return rc;
-}
-
-
static uint8_t efiGetHalfByte(char ch)
{
uint8_t val;
@@ -962,21 +2013,25 @@ static uint8_t efiGetHalfByte(char ch)
}
-static int efiParseDeviceString(PDEVEFI pThis, char* pszDeviceProps)
+/**
+ * Converts a hex string into a binary data blob located at
+ * pThis->pbDeviceProps, size returned as pThis->cbDeviceProps.
+ *
+ * @returns VERR_NO_MEMORY or VINF_SUCCESS.
+ * @param pThis The EFI instance data.
+ * @param pszDeviceProps The device property hex string to decode.
+ */
+static int efiParseDeviceString(PDEVEFI pThis, const char *pszDeviceProps)
{
- int rc = 0;
- uint32_t iStr, iHex, u32OutLen;
- uint8_t u8Value = 0; /* (shut up gcc) */
- bool fUpper = true;
-
- u32OutLen = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
-
- pThis->pu8DeviceProps =
- (uint8_t*)PDMDevHlpMMHeapAlloc(pThis->pDevIns, u32OutLen);
- if (!pThis->pu8DeviceProps)
+ uint32_t const cbOut = (uint32_t)RTStrNLen(pszDeviceProps, RTSTR_MAX) / 2 + 1;
+ pThis->pbDeviceProps = (uint8_t *)PDMDevHlpMMHeapAlloc(pThis->pDevIns, cbOut);
+ if (!pThis->pbDeviceProps)
return VERR_NO_MEMORY;
- for (iStr=0, iHex = 0; pszDeviceProps[iStr]; iStr++)
+ 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)
@@ -985,18 +2040,19 @@ static int efiParseDeviceString(PDEVEFI pThis, char* pszDeviceProps)
if (fUpper)
u8Value = u8Hb << 4;
else
- pThis->pu8DeviceProps[iHex++] = u8Hb | u8Value;
+ pThis->pbDeviceProps[iHex++] = u8Hb | u8Value;
- Assert(iHex < u32OutLen);
+ Assert(iHex < cbOut);
fUpper = !fUpper;
}
Assert(iHex == 0 || fUpper);
- pThis->u32DevicePropsLen = iHex;
+ pThis->cbDeviceProps = iHex;
- return rc;
+ return VINF_SUCCESS;
}
+
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
@@ -1004,10 +2060,17 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
{
PDEVEFI pThis = PDMINS_2_DATA(pDevIns, PDEVEFI);
int rc;
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
Assert(iInstance == 0);
+ /*
+ * Initalize the basic variables so that the destructor always works.
+ */
pThis->pDevIns = pDevIns;
+ RTListInit(&pThis->NVRAM.VarList);
+ pThis->Lun0.IBase.pfnQueryInterface = devEfiQueryInterface;
+
/*
* Validate and read the configuration.
@@ -1042,6 +2105,7 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
"DmiBoardVersion\0"
"DmiChassisAssetTag\0"
"DmiChassisSerial\0"
+ "DmiChassisType\0"
"DmiChassisVendor\0"
"DmiChassisVersion\0"
"DmiProcManufacturer\0"
@@ -1056,8 +2120,7 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
"DeviceProps\0"
"GopMode\0"
"UgaHorizontalResolution\0"
- "UgaVerticalResolution\0"
- ))
+ "UgaVerticalResolution\0"))
return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Configuration error: Invalid config value(s) for the EFI device"));
@@ -1088,7 +2151,6 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
memcpy(&pThis->aUuid, &uuid, sizeof pThis->aUuid);
-
/*
* RAM sizes
*/
@@ -1124,37 +2186,44 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
}
/*
+ * NVRAM processing.
+ */
+ rc = PDMDevHlpSSMRegister(pDevIns, EFI_SSM_VERSION, sizeof(*pThis), efiSaveExec, efiLoadExec);
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "NvramStorage");
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
+
+ pThis->Lun0.pNvramDrv = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pDrvBase, PDMINVRAMCONNECTOR);
+ AssertPtrReturn(pThis->Lun0.pNvramDrv, VERR_PDM_MISSING_INTERFACE_BELOW);
+
+ rc = nvramLoad(pThis);
+ AssertRCReturn(rc, rc);
+
+ /*
* Get boot args.
*/
- rc = CFGMR3QueryString(pCfg, "BootArgs",
- pThis->szBootArgs, sizeof pThis->szBootArgs);
- if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- {
- strcpy(pThis->szBootArgs, "");
- rc = VINF_SUCCESS;
- }
+ rc = CFGMR3QueryStringDef(pCfg, "BootArgs", pThis->szBootArgs, sizeof(pThis->szBootArgs), "");
if (RT_FAILURE(rc))
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("Configuration error: Querying \"BootArgs\" as a string failed"));
- LogRel(("EFI boot args: %s\n", pThis->szBootArgs));
+ //strcpy(pThis->szBootArgs, "-v keepsyms=1 io=0xf debug=0x2a");
+ //strcpy(pThis->szBootArgs, "-v keepsyms=1 debug=0x2a");
+ LogRel(("EFI: boot args = %s\n", pThis->szBootArgs));
/*
* Get device props.
*/
- char* pszDeviceProps;
- rc = CFGMR3QueryStringAlloc(pCfg, "DeviceProps", &pszDeviceProps);
- if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- {
- pszDeviceProps = NULL;
- rc = VINF_SUCCESS;
- }
+ char *pszDeviceProps;
+ rc = CFGMR3QueryStringAllocDef(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));
+ LogRel(("EFI: device props = %s\n", pszDeviceProps));
rc = efiParseDeviceString(pThis, pszDeviceProps);
MMR3HeapFree(pszDeviceProps);
if (RT_FAILURE(rc))
@@ -1163,54 +2232,40 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
}
else
{
- pThis->pu8DeviceProps = NULL;
- pThis->u32DevicePropsLen = 0;
+ pThis->pbDeviceProps = NULL;
+ pThis->cbDeviceProps = 0;
}
/*
- * CPU frequencies
+ * CPU frequencies.
*/
- // @todo: we need to have VMM API to access TSC increase speed, for now provide reasonable default
- pThis->u64TscFrequency = RTMpGetMaxFrequency(0) * 1024 * 1024;// TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
- if (pThis->u64TscFrequency == 0)
- pThis->u64TscFrequency = UINT64_C(2500000000);
- /* Multiplier is read from MSR_IA32_PERF_STATUS, and now is hardcoded as 4 */
- pThis->u64FsbFrequency = pThis->u64TscFrequency / 4;
+ pThis->u64TscFrequency = TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
pThis->u64CpuFrequency = pThis->u64TscFrequency;
+ pThis->u64FsbFrequency = CPUMGetGuestScalableBusFrequency(PDMDevHlpGetVM(pDevIns));
/*
- * GOP graphics
+ * GOP graphics.
*/
- rc = CFGMR3QueryU32(pCfg, "GopMode", &pThis->u32GopMode);
- AssertRC(rc);
+ rc = CFGMR3QueryU32Def(pCfg, "GopMode", &pThis->u32GopMode, 2 /* 1024x768 */);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("Configuration error: Querying \"GopMode\" as a 32-bit int failed"));
if (pThis->u32GopMode == UINT32_MAX)
- {
pThis->u32GopMode = 2; /* 1024x768 */
- }
/*
- * Uga graphics
+ * Uga graphics, default to 1024x768.
*/
- rc = CFGMR3QueryU32(pCfg, "UgaHorizontalResolution", &pThis->u32UgaHorisontal);
- AssertRC(rc);
- if (pThis->u32UgaHorisontal == 0)
- {
- pThis->u32UgaHorisontal = 1024; /* 1024x768 */
- }
- rc = CFGMR3QueryU32(pCfg, "UgaVerticalResolution", &pThis->u32UgaVertical);
- AssertRC(rc);
- if (pThis->u32UgaVertical == 0)
+ rc = CFGMR3QueryU32Def(pCfg, "UgaHorizontalResolution", &pThis->cxUgaResolution, 0);
+ AssertRCReturn(rc, rc);
+ rc = CFGMR3QueryU32Def(pCfg, "UgaVerticalResolution", &pThis->cyUgaResolution, 0);
+ AssertRCReturn(rc, rc);
+ if (pThis->cxUgaResolution == 0 || pThis->cyUgaResolution == 0)
{
- pThis->u32UgaVertical = 768; /* 1024x768 */
+ pThis->cxUgaResolution = 1024;
+ pThis->cyUgaResolution = 768;
}
-#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
- /*
- * Zap the debugger script
- */
- RTFileDelete("./DevEFI.VBoxDbg");
-#endif
-
/*
* Load firmware volume and thunk ROM.
*/
@@ -1218,12 +2273,8 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
if (RT_FAILURE(rc))
return rc;
- rc = efiLoadThunk(pThis, pCfg);
- if (RT_FAILURE(rc))
- return rc;
-
/*
- * Register our communication ports.
+ * Register our I/O ports.
*/
rc = PDMDevHlpIOPortRegister(pDevIns, EFI_PORT_BASE, EFI_PORT_COUNT, NULL,
efiIOPortWrite, efiIOPortRead,
@@ -1232,11 +2283,11 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
return rc;
/*
- * Plant DMI and MPS tables
- * XXX I wonder if we really need these tables as there is no SMBIOS header...
+ * Plant DMI and MPS tables.
*/
+ /** @todo XXX I wonder if we really need these tables as there is no SMBIOS header... */
rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &pThis->aUuid,
- pDevIns->pCfg, pThis->cCpus, &pThis->cbDmiTables);
+ pDevIns->pCfg, pThis->cCpus, &pThis->cbDmiTables, &pThis->cNumDmiTables);
AssertRCReturn(rc, rc);
if (pThis->u8IOAPIC)
FwCommonPlantMpsTable(pDevIns,
@@ -1248,6 +2299,12 @@ static DECLCALLBACK(int) efiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
AssertRCReturn(rc, rc);
/*
+ * Register info handlers.
+ */
+ rc = PDMDevHlpDBGFInfoRegister(pDevIns, "nvram", "Dumps the NVRAM variables.\n", efiInfoNvram);
+ AssertRCReturn(rc, rc);
+
+ /*
* Call reset to set things up.
*/
efiReset(pDevIns);
@@ -1269,7 +2326,8 @@ const PDMDEVREG g_DeviceEFI =
/* szR0Mod */
"",
/* pszDescription */
- "Extensible Firmware Interface Device",
+ "Extensible Firmware Interface Device. "
+ "LUN#0 - NVRAM port",
/* fFlags */
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
/* fClass */
@@ -1284,8 +2342,8 @@ const PDMDEVREG g_DeviceEFI =
efiDestruct,
/* pfnRelocate */
NULL,
- /* pfnIOCtl */
- NULL,
+ /* pfnMemSetup */
+ efiMemSetup,
/* pfnPowerOn */
NULL,
/* pfnReset */