summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-all/NvramStoreImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-all/NvramStoreImpl.cpp')
-rw-r--r--src/VBox/Main/src-all/NvramStoreImpl.cpp1645
1 files changed, 1645 insertions, 0 deletions
diff --git a/src/VBox/Main/src-all/NvramStoreImpl.cpp b/src/VBox/Main/src-all/NvramStoreImpl.cpp
new file mode 100644
index 00000000..d81c9651
--- /dev/null
+++ b/src/VBox/Main/src-all/NvramStoreImpl.cpp
@@ -0,0 +1,1645 @@
+/* $Id: NvramStoreImpl.cpp $ */
+/** @file
+ * VirtualBox COM NVRAM store class implementation
+ */
+
+/*
+ * Copyright (C) 2021-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_NVRAMSTORE
+#include "LoggingNew.h"
+
+#include "NvramStoreImpl.h"
+#ifdef VBOX_COM_INPROC
+# include "ConsoleImpl.h"
+#else
+# include "MachineImpl.h"
+# include "GuestOSTypeImpl.h"
+# include "AutoStateDep.h"
+#endif
+#include "UefiVariableStoreImpl.h"
+#include "VirtualBoxImpl.h"
+
+#include "AutoCaller.h"
+
+#include <VBox/com/array.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/err.h>
+
+#include <iprt/cpp/utils.h>
+#include <iprt/efi.h>
+#include <iprt/file.h>
+#include <iprt/vfs.h>
+#include <iprt/zip.h>
+
+
+// defines
+////////////////////////////////////////////////////////////////////////////////
+
+/** Version of the NVRAM saved state unit. */
+#define NVRAM_STORE_SAVED_STATE_VERSION 1
+
+
+// globals
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * NVRAM store driver instance data.
+ */
+typedef struct DRVMAINNVRAMSTORE
+{
+ /** Pointer to the keyboard object. */
+ NvramStore *pNvramStore;
+ /** Pointer to the driver instance structure. */
+ PPDMDRVINS pDrvIns;
+ /** Our VFS connector interface. */
+ PDMIVFSCONNECTOR IVfs;
+} DRVMAINNVRAMSTORE, *PDRVMAINNVRAMSTORE;
+
+/** The NVRAM store map keyed by namespace/entity. */
+typedef std::map<Utf8Str, RTVFSFILE> NvramStoreMap;
+/** The NVRAM store map iterator. */
+typedef std::map<Utf8Str, RTVFSFILE>::iterator NvramStoreIter;
+
+struct BackupableNvramStoreData
+{
+ BackupableNvramStoreData()
+ { }
+
+ /** The NVRAM file path. */
+ com::Utf8Str strNvramPath;
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ /** The key id used for encrypting the NVRAM file */
+ com::Utf8Str strKeyId;
+ /** The key store containing the encrypting DEK */
+ com::Utf8Str strKeyStore;
+#endif
+ /** The NVRAM store. */
+ NvramStoreMap mapNvram;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// NvramStore::Data structure
+/////////////////////////////////////////////////////////////////////////////
+
+struct NvramStore::Data
+{
+ Data()
+ : pParent(NULL)
+#ifdef VBOX_COM_INPROC
+ , cRefs(0)
+ , fSsmSaved(false)
+#endif
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ , mpKeyStore(NULL)
+#endif
+ { }
+
+#ifdef VBOX_COM_INPROC
+ /** The Console owning this NVRAM store. */
+ Console * const pParent;
+ /** Number of references held to this NVRAM store from the various devices/drivers. */
+ volatile uint32_t cRefs;
+ /** Flag whether the NVRAM data was saved during a save state operation
+ * preventing it from getting written to the backing file. */
+ bool fSsmSaved;
+#else
+ /** The Machine object owning this NVRAM store. */
+ Machine * const pParent;
+ /** The peer NVRAM store object. */
+ ComObjPtr<NvramStore> pPeer;
+ /** The UEFI variable store. */
+ const ComObjPtr<UefiVariableStore> pUefiVarStore;
+#endif
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ /* Store for secret keys. */
+ SecretKeyStore *mpKeyStore;
+#endif
+
+ Backupable<BackupableNvramStoreData> bd;
+};
+
+// constructor / destructor
+////////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(NvramStore)
+
+HRESULT NvramStore::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void NvramStore::FinalRelease()
+{
+ uninit();
+ BaseFinalRelease();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Initialization stuff shared across the different methods.
+ *
+ * @returns COM result indicator
+ */
+int NvramStore::initImpl()
+{
+ m = new Data();
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+# ifdef VBOX_COM_INPROC
+ bool fNonPageable = true;
+# else
+ /* Non-pageable memory is not accessible for non-VM process */
+ bool fNonPageable = false;
+# endif
+
+ m->mpKeyStore = new SecretKeyStore(fNonPageable /* fKeyBufNonPageable */);
+ AssertReturn(m->mpKeyStore, VERR_NO_MEMORY);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+#if !defined(VBOX_COM_INPROC)
+/**
+ * Initializes the NVRAM store object.
+ *
+ * @returns COM result indicator
+ */
+HRESULT NvramStore::init(Machine *aParent)
+{
+ LogFlowThisFuncEnter();
+ LogFlowThisFunc(("aParent: %p\n", aParent));
+
+ ComAssertRet(aParent, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ int vrc = initImpl();
+ if (RT_FAILURE(vrc))
+ return E_FAIL;
+
+ /* share the parent weakly */
+ unconst(m->pParent) = aParent;
+
+ m->bd.allocate();
+
+ autoInitSpan.setSucceeded();
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+/**
+ * Initializes the NVRAM store object given another NVRAM store object
+ * (a kind of copy constructor). This object shares data with
+ * the object passed as an argument.
+ *
+ * @note This object must be destroyed before the original object
+ * it shares data with is destroyed.
+ */
+HRESULT NvramStore::init(Machine *aParent, NvramStore *that)
+{
+ LogFlowThisFuncEnter();
+ LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
+
+ ComAssertRet(aParent && that, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ initImpl();
+
+ unconst(m->pParent) = aParent;
+ m->pPeer = that;
+
+ AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
+ m->bd.share(that->m->bd);
+
+ autoInitSpan.setSucceeded();
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+/**
+ * Initializes the guest object given another guest object
+ * (a kind of copy constructor). This object makes a private copy of data
+ * of the original object passed as an argument.
+ */
+HRESULT NvramStore::initCopy(Machine *aParent, NvramStore *that)
+{
+ LogFlowThisFuncEnter();
+ LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
+
+ ComAssertRet(aParent && that, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ initImpl();
+
+ unconst(m->pParent) = aParent;
+ // mPeer is left null
+
+ AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
+ m->bd.attachCopy(that->m->bd);
+
+ autoInitSpan.setSucceeded();
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+#else
+
+/**
+ * Initializes the NVRAM store object.
+ *
+ * @returns COM result indicator
+ * @param aParent Handle of our parent object
+ * @param strNonVolatileStorageFile The NVRAM file path.
+ */
+HRESULT NvramStore::init(Console *aParent, const com::Utf8Str &strNonVolatileStorageFile)
+{
+ LogFlowThisFunc(("aParent=%p\n", aParent));
+
+ ComAssertRet(aParent, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ initImpl();
+
+ unconst(m->pParent) = aParent;
+
+ m->bd.allocate();
+ m->bd->strNvramPath = strNonVolatileStorageFile;
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+#endif /* VBOX_COM_INPROC */
+
+
+/**
+ * Uninitializes the instance and sets the ready flag to FALSE.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void NvramStore::uninit()
+{
+ LogFlowThisFuncEnter();
+
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ unconst(m->pParent) = NULL;
+#ifndef VBOX_COM_INPROC
+ unconst(m->pUefiVarStore) = NULL;
+#endif
+
+ /* Delete the NVRAM content. */
+ NvramStoreIter it = m->bd->mapNvram.begin();
+ while (it != m->bd->mapNvram.end())
+ {
+ RTVfsFileRelease(it->second);
+ it++;
+ }
+
+ m->bd->mapNvram.clear();
+ m->bd.free();
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ if (m->mpKeyStore != NULL)
+ delete m->mpKeyStore;
+#endif
+
+ delete m;
+ m = NULL;
+
+ LogFlowThisFuncLeave();
+}
+
+
+HRESULT NvramStore::getNonVolatileStorageFile(com::Utf8Str &aNonVolatileStorageFile)
+{
+#ifndef VBOX_COM_INPROC
+ Utf8Str strTmp;
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ strTmp = m->bd->strNvramPath;
+ }
+
+ AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
+ if (strTmp.isEmpty())
+ strTmp = m->pParent->i_getDefaultNVRAMFilename();
+ if (strTmp.isNotEmpty())
+ m->pParent->i_calculateFullPath(strTmp, aNonVolatileStorageFile);
+#else
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aNonVolatileStorageFile = m->bd->strNvramPath;
+#endif
+
+ return S_OK;
+}
+
+
+HRESULT NvramStore::getUefiVariableStore(ComPtr<IUefiVariableStore> &aUefiVarStore)
+{
+#ifndef VBOX_COM_INPROC
+ /* the machine needs to be mutable */
+ AutoMutableStateDependency adep(m->pParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ Utf8Str strPath;
+ NvramStore::getNonVolatileStorageFile(strPath);
+
+ /* We need a write lock because of the lazy initialization. */
+ AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* Check if we have to create the UEFI variable store object */
+ HRESULT hrc = S_OK;
+ if (!m->pUefiVarStore)
+ {
+ /* Load the NVRAM file first if it isn't already. */
+ if (!m->bd->mapNvram.size())
+ {
+ int vrc = i_loadStore(strPath.c_str());
+ if (RT_FAILURE(vrc))
+ hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
+ }
+
+ if (SUCCEEDED(hrc))
+ {
+ NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
+ if (it != m->bd->mapNvram.end())
+ {
+ unconst(m->pUefiVarStore).createObject();
+ m->pUefiVarStore->init(this, m->pParent);
+ }
+ else
+ hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
+ }
+ }
+
+ if (SUCCEEDED(hrc))
+ {
+ m->pUefiVarStore.queryInterfaceTo(aUefiVarStore.asOutParam());
+
+ /* Mark the NVRAM store as potentially modified. */
+ m->pParent->i_setModified(Machine::IsModified_NvramStore);
+ }
+
+ return hrc;
+#else
+ NOREF(aUefiVarStore);
+ return E_NOTIMPL;
+#endif
+}
+
+
+HRESULT NvramStore::getKeyId(com::Utf8Str &aKeyId)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ aKeyId = m->bd->strKeyId;
+#else
+ aKeyId = com::Utf8Str::Empty;
+#endif
+
+ return S_OK;
+}
+
+
+HRESULT NvramStore::getKeyStore(com::Utf8Str &aKeyStore)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ aKeyStore = m->bd->strKeyStore;
+#else
+ aKeyStore = com::Utf8Str::Empty;
+#endif
+
+ return S_OK;
+}
+
+
+HRESULT NvramStore::initUefiVariableStore(ULONG aSize)
+{
+#ifndef VBOX_COM_INPROC
+ if (aSize != 0)
+ return setError(E_NOTIMPL, tr("Supporting another NVRAM size apart from the default one is not supported right now"));
+
+ /* the machine needs to be mutable */
+ AutoMutableStateDependency adep(m->pParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ Utf8Str strPath;
+ NvramStore::getNonVolatileStorageFile(strPath);
+
+ /* We need a write lock because of the lazy initialization. */
+ AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (m->pParent->i_getFirmwareType() == FirmwareType_BIOS)
+ return setError(VBOX_E_NOT_SUPPORTED, tr("The selected firmware type doesn't support a UEFI variable store"));
+
+ /* Load the NVRAM file first if it isn't already. */
+ HRESULT hrc = S_OK;
+ if (!m->bd->mapNvram.size())
+ {
+ int vrc = i_loadStore(strPath.c_str());
+ if (RT_FAILURE(vrc))
+ hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
+ }
+
+ if (SUCCEEDED(hrc))
+ {
+ int vrc = VINF_SUCCESS;
+ RTVFSFILE hVfsUefiVarStore = NIL_RTVFSFILE;
+ NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
+ if (it != m->bd->mapNvram.end())
+ hVfsUefiVarStore = it->second;
+ else
+ {
+ /* Create a new file. */
+ vrc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsUefiVarStore);
+ if (RT_SUCCESS(vrc))
+ {
+ /** @todo The size is hardcoded to match what the firmware image uses right now which is a gross hack... */
+ vrc = RTVfsFileSetSize(hVfsUefiVarStore, 540672, RTVFSFILE_SIZE_F_NORMAL);
+ if (RT_SUCCESS(vrc))
+ m->bd->mapNvram["efi/nvram"] = hVfsUefiVarStore;
+ else
+ RTVfsFileRelease(hVfsUefiVarStore);
+ }
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTEfiVarStoreCreate(hVfsUefiVarStore, 0 /*offStore*/, 0 /*cbStore*/, RTEFIVARSTORE_CREATE_F_DEFAULT, 0 /*cbBlock*/,
+ NULL /*pErrInfo*/);
+ if (RT_FAILURE(vrc))
+ return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
+ }
+ else
+ return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
+
+ m->pParent->i_setModified(Machine::IsModified_NvramStore);
+ }
+
+ return hrc;
+#else
+ NOREF(aSize);
+ return E_NOTIMPL;
+#endif
+}
+
+
+Utf8Str NvramStore::i_getNonVolatileStorageFile()
+{
+ AutoCaller autoCaller(this);
+ AssertReturn(autoCaller.isOk(), Utf8Str::Empty);
+
+ Utf8Str strTmp;
+ NvramStore::getNonVolatileStorageFile(strTmp);
+ return strTmp;
+}
+
+
+/**
+ * Loads the NVRAM store from the given TAR filesystem stream.
+ *
+ * @returns IPRT status code.
+ * @param hVfsFssTar Handle to the tar filesystem stream.
+ */
+int NvramStore::i_loadStoreFromTar(RTVFSFSSTREAM hVfsFssTar)
+{
+ int vrc = VINF_SUCCESS;
+
+ /*
+ * Process the stream.
+ */
+ for (;;)
+ {
+ /*
+ * Retrieve the next object.
+ */
+ char *pszName;
+ RTVFSOBJ hVfsObj;
+ vrc = RTVfsFsStrmNext(hVfsFssTar, &pszName, NULL, &hVfsObj);
+ if (RT_FAILURE(vrc))
+ {
+ if (vrc == VERR_EOF)
+ vrc = VINF_SUCCESS;
+ break;
+ }
+
+ RTFSOBJINFO UnixInfo;
+ vrc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
+ if (RT_SUCCESS(vrc))
+ {
+ switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FILE:
+ {
+ LogRel(("NvramStore: Loading '%s' from archive\n", pszName));
+ RTVFSIOSTREAM hVfsIosEntry = RTVfsObjToIoStream(hVfsObj);
+ Assert(hVfsIosEntry != NIL_RTVFSIOSTREAM);
+
+ RTVFSFILE hVfsFileEntry;
+ vrc = RTVfsMemorizeIoStreamAsFile(hVfsIosEntry, RTFILE_O_READ | RTFILE_O_WRITE, &hVfsFileEntry);
+ if (RT_FAILURE(vrc))
+ break;
+ RTVfsIoStrmRelease(hVfsIosEntry);
+
+ m->bd->mapNvram[Utf8Str(pszName)] = hVfsFileEntry;
+ break;
+ }
+ case RTFS_TYPE_DIRECTORY:
+ break;
+ default:
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ /*
+ * Release the current object and string.
+ */
+ RTVfsObjRelease(hVfsObj);
+ RTStrFree(pszName);
+
+ if (RT_FAILURE(vrc))
+ break;
+ }
+
+ return vrc;
+}
+
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+/**
+ * Sets up the encryption or decryption machinery.
+ *
+ * @returns VBox status code.
+ * @param hVfsIosInOut Handle to the input stream to be decrypted or the destination to the encrypted
+ * output is written to.
+ * @param fEncrypt Flag whether to setup encryption or decryption.
+ * @param ppCryptoIf Where to store the pointer to the cryptographic interface which needs to be released
+ * when done.
+ * @param ppKey Where to store the pointer to the secret key buffer which needs to be released when done.
+ * @param phVfsIos Where to store the handle to the plaintext I/O stream (either input or output) on success.
+ */
+int NvramStore::i_setupEncryptionOrDecryption(RTVFSIOSTREAM hVfsIosInOut, bool fEncrypt,
+ PCVBOXCRYPTOIF *ppCryptoIf, SecretKey **ppKey,
+ PRTVFSIOSTREAM phVfsIos)
+{
+ int vrc = VINF_SUCCESS;
+ PCVBOXCRYPTOIF pCryptoIf = NULL;
+ SecretKey *pKey = NULL;
+ const char *pszPassword = NULL;
+
+ vrc = i_retainCryptoIf(&pCryptoIf);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = m->mpKeyStore->retainSecretKey(m->bd->strKeyId, &pKey);
+ if (RT_SUCCESS(vrc))
+ {
+ pszPassword = (const char *)pKey->getKeyBuffer();
+ if (fEncrypt)
+ vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmEncrypt(hVfsIosInOut, m->bd->strKeyStore.c_str(), pszPassword,
+ phVfsIos);
+ else
+ vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosInOut, m->bd->strKeyStore.c_str(), pszPassword,
+ phVfsIos);
+ if (RT_SUCCESS(vrc))
+ {
+ *ppCryptoIf = pCryptoIf;
+ *ppKey = pKey;
+ return VINF_SUCCESS;
+ }
+ else
+ LogRelMax(10, ("Failed to decrypt the NVRAM store using secret key ID '%s' with %Rrc\n",
+ m->bd->strKeyId.c_str(), vrc));
+
+ m->mpKeyStore->releaseSecretKey(m->bd->strKeyId);
+ }
+ else
+ LogRelMax(10, ("Failed to retain the secret key ID '%s' with %Rrc\n",
+ m->bd->strKeyId.c_str(), vrc));
+
+ i_releaseCryptoIf(pCryptoIf);
+ }
+ else
+ LogRelMax(10, ("Failed to retain the cryptographic interface with %Rrc\n", vrc));
+
+ return vrc;
+}
+
+/**
+ * Releases all resources acquired in NvramStore::i_setupEncryptionOrDecryption().
+ *
+ * @returns nothing.
+ * @param hVfsIos Handle to the I/O stream previously created.
+ * @param pCryptoIf Pointer to the cryptographic interface being released.
+ * @param pKey Pointer to the key buffer being released.
+ */
+void NvramStore::i_releaseEncryptionOrDecryptionResources(RTVFSIOSTREAM hVfsIos, PCVBOXCRYPTOIF pCryptoIf,
+ SecretKey *pKey)
+{
+ Assert(hVfsIos != NIL_RTVFSIOSTREAM);
+ AssertPtr(pCryptoIf);
+ AssertPtr(pKey);
+
+ i_releaseCryptoIf(pCryptoIf);
+ pKey->release();
+ RTVfsIoStrmRelease(hVfsIos);
+}
+#endif
+
+
+/**
+ * Loads the NVRAM store.
+ *
+ * @returns IPRT status code.
+ */
+int NvramStore::i_loadStore(const char *pszPath)
+{
+ uint64_t cbStore = 0;
+ int vrc = RTFileQuerySizeByPath(pszPath, &cbStore);
+ if (RT_SUCCESS(vrc))
+ {
+ if (cbStore <= _1M) /* Arbitrary limit to fend off bogus files because the file will be read into memory completely. */
+ {
+ /*
+ * Old NVRAM files just consist of the EFI variable store whereas starting
+ * with VirtualBox 7.0 and the introduction of the TPM the need to handle multiple
+ * independent NVRAM files came up. For those scenarios all NVRAM states are collected
+ * in a tar archive.
+ *
+ * Here we detect whether the file is the new tar archive format or whether it is just
+ * the plain EFI variable store file.
+ */
+ RTVFSIOSTREAM hVfsIosNvram;
+ vrc = RTVfsIoStrmOpenNormal(pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE,
+ &hVfsIosNvram);
+ if (RT_SUCCESS(vrc))
+ {
+ RTVFSIOSTREAM hVfsIosDecrypted = NIL_RTVFSIOSTREAM;
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ PCVBOXCRYPTOIF pCryptoIf = NULL;
+ SecretKey *pKey = NULL;
+
+ if ( m->bd->strKeyId.isNotEmpty()
+ && m->bd->strKeyStore.isNotEmpty())
+ vrc = i_setupEncryptionOrDecryption(hVfsIosNvram, false /*fEncrypt*/,
+ &pCryptoIf, &pKey, &hVfsIosDecrypted);
+#endif
+ if (RT_SUCCESS(vrc))
+ {
+ /* Read the content. */
+ RTVFSFILE hVfsFileNvram;
+ vrc = RTVfsMemorizeIoStreamAsFile( hVfsIosDecrypted != NIL_RTVFSIOSTREAM
+ ? hVfsIosDecrypted
+ : hVfsIosNvram,
+ RTFILE_O_READ, &hVfsFileNvram);
+ if (RT_SUCCESS(vrc))
+ {
+ if (RT_SUCCESS(vrc))
+ {
+ /* Try to parse it as an EFI variable store. */
+ RTVFS hVfsEfiVarStore;
+ vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, RTVFSMNT_F_READ_ONLY, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
+ NULL /*pErrInfo*/);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+ AssertRC(vrc);
+
+ RTVfsFileRetain(hVfsFileNvram); /* Retain a new reference for the map. */
+ m->bd->mapNvram[Utf8Str("efi/nvram")] = hVfsFileNvram;
+
+ RTVfsRelease(hVfsEfiVarStore);
+ }
+ else if (vrc == VERR_VFS_UNKNOWN_FORMAT)
+ {
+ /* Check for the new style tar archive. */
+ vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+ AssertRC(vrc);
+
+ RTVFSIOSTREAM hVfsIosTar = RTVfsFileToIoStream(hVfsFileNvram);
+ Assert(hVfsIosTar != NIL_RTVFSIOSTREAM);
+
+ RTVFSFSSTREAM hVfsFssTar;
+ vrc = RTZipTarFsStreamFromIoStream(hVfsIosTar, 0 /*fFlags*/, &hVfsFssTar);
+ RTVfsIoStrmRelease(hVfsIosTar);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = i_loadStoreFromTar(hVfsFssTar);
+ RTVfsFsStrmRelease(hVfsFssTar);
+ }
+ else
+ LogRel(("The given NVRAM file is neither a raw UEFI variable store nor a tar archive (opening failed with %Rrc)\n", vrc));
+ }
+ else
+ LogRel(("Opening the UEFI variable store '%s' failed with %Rrc\n", pszPath, vrc));
+
+ RTVfsFileRelease(hVfsFileNvram);
+ }
+ else
+ LogRel(("Failed to memorize NVRAM store '%s' with %Rrc\n", pszPath, vrc));
+ }
+ }
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ if (hVfsIosDecrypted != NIL_RTVFSIOSTREAM)
+ i_releaseEncryptionOrDecryptionResources(hVfsIosDecrypted, pCryptoIf, pKey);
+#endif
+
+ RTVfsIoStrmRelease(hVfsIosNvram);
+ }
+ else
+ LogRelMax(10, ("NVRAM store '%s' couldn't be opened with %Rrc\n", pszPath, vrc));
+ }
+ else
+ LogRelMax(10, ("NVRAM store '%s' exceeds limit of %u bytes, actual size is %u\n",
+ pszPath, _1M, cbStore));
+ }
+ else if (vrc == VERR_FILE_NOT_FOUND) /* Valid for the first run where no NVRAM file is there. */
+ vrc = VINF_SUCCESS;
+
+ return vrc;
+}
+
+
+/**
+ * Saves the NVRAM store as a tar archive.
+ */
+int NvramStore::i_saveStoreAsTar(const char *pszPath)
+{
+ uint32_t offError = 0;
+ RTERRINFOSTATIC ErrInfo;
+ RTVFSIOSTREAM hVfsIos;
+
+ int vrc = RTVfsChainOpenIoStream(pszPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
+ &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(vrc))
+ {
+ RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ PCVBOXCRYPTOIF pCryptoIf = NULL;
+ SecretKey *pKey = NULL;
+
+ if ( m->bd->strKeyId.isNotEmpty()
+ && m->bd->strKeyStore.isNotEmpty())
+ vrc = i_setupEncryptionOrDecryption(hVfsIos, true /*fEncrypt*/,
+ &pCryptoIf, &pKey, &hVfsIosEncrypted);
+#endif
+
+ if (RT_SUCCESS(vrc))
+ {
+ RTVFSFSSTREAM hVfsFss;
+ vrc = RTZipTarFsStreamToIoStream( hVfsIosEncrypted != NIL_RTVFSIOSTREAM
+ ? hVfsIosEncrypted
+ : hVfsIos,
+ RTZIPTARFORMAT_GNU, 0 /*fFlags*/, &hVfsFss);
+ if (RT_SUCCESS(vrc))
+ {
+ NvramStoreIter it = m->bd->mapNvram.begin();
+
+ while (it != m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFile = it->second;
+
+ vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+ AssertRC(vrc);
+
+ RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile);
+ vrc = RTVfsFsStrmAdd(hVfsFss, it->first.c_str(), hVfsObj, 0 /*fFlags*/);
+ RTVfsObjRelease(hVfsObj);
+ if (RT_FAILURE(vrc))
+ break;
+
+ it++;
+ }
+
+ RTVfsFsStrmRelease(hVfsFss);
+ }
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
+ i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
+#endif
+ }
+
+ RTVfsIoStrmRelease(hVfsIos);
+ }
+
+ return vrc;
+}
+
+
+int NvramStore::i_retainCryptoIf(PCVBOXCRYPTOIF *ppCryptoIf)
+{
+#ifdef VBOX_COM_INPROC
+ return m->pParent->i_retainCryptoIf(ppCryptoIf);
+#else
+ HRESULT hrc = m->pParent->i_getVirtualBox()->i_retainCryptoIf(ppCryptoIf);
+ if (SUCCEEDED(hrc))
+ return VINF_SUCCESS;
+
+ return VERR_COM_IPRT_ERROR;
+#endif
+}
+
+
+int NvramStore::i_releaseCryptoIf(PCVBOXCRYPTOIF pCryptoIf)
+{
+#ifdef VBOX_COM_INPROC
+ return m->pParent->i_releaseCryptoIf(pCryptoIf);
+#else
+ HRESULT hrc = m->pParent->i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
+ if (SUCCEEDED(hrc))
+ return VINF_SUCCESS;
+
+ return VERR_COM_IPRT_ERROR;
+#endif
+}
+
+
+/**
+ * Saves the NVRAM store.
+ *
+ * @returns IPRT status code.
+ */
+int NvramStore::i_saveStore(void)
+{
+ int vrc = VINF_SUCCESS;
+
+ Utf8Str strTmp;
+ NvramStore::getNonVolatileStorageFile(strTmp);
+
+ /*
+ * Only store the NVRAM content if the path is not empty, if it is
+ * this means the VM was just created and the store was nnot saved yet,
+ * see @bugref{10191}.
+ */
+ if (strTmp.isNotEmpty())
+ {
+ /*
+ * Skip creating the tar archive if only the UEFI NVRAM content is available in order
+ * to maintain backwards compatibility. As soon as there is more than one entry or
+ * it doesn't belong to the UEFI the tar archive will be created.
+ */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if ( m->bd->mapNvram.size() == 1
+ && m->bd->mapNvram.find(Utf8Str("efi/nvram")) != m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFileNvram = m->bd->mapNvram[Utf8Str("efi/nvram")];
+
+ vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+ AssertRC(vrc); RT_NOREF(vrc);
+
+ RTVFSIOSTREAM hVfsIosDst;
+ vrc = RTVfsIoStrmOpenNormal(strTmp.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
+ &hVfsIosDst);
+ if (RT_SUCCESS(vrc))
+ {
+ RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsFileNvram);
+ Assert(hVfsIosSrc != NIL_RTVFSIOSTREAM);
+
+ RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ PCVBOXCRYPTOIF pCryptoIf = NULL;
+ SecretKey *pKey = NULL;
+
+ if ( m->bd->strKeyId.isNotEmpty()
+ && m->bd->strKeyStore.isNotEmpty())
+ vrc = i_setupEncryptionOrDecryption(hVfsIosDst, true /*fEncrypt*/,
+ &pCryptoIf, &pKey, &hVfsIosEncrypted);
+#endif
+
+ vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc,
+ hVfsIosEncrypted != NIL_RTVFSIOSTREAM
+ ? hVfsIosEncrypted
+ : hVfsIosDst
+ , 0 /*cbBufHint*/);
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
+ i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
+#endif
+
+ RTVfsIoStrmRelease(hVfsIosSrc);
+ RTVfsIoStrmRelease(hVfsIosDst);
+ }
+ }
+ else if (m->bd->mapNvram.size())
+ vrc = i_saveStoreAsTar(strTmp.c_str());
+ /* else: No NVRAM content to store so we are done here. */
+ }
+
+ return vrc;
+}
+
+
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+HRESULT NvramStore::i_updateEncryptionSettings(const com::Utf8Str &strKeyId,
+ const com::Utf8Str &strKeyStore)
+{
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ m->bd.backup();
+ m->bd->strKeyId = strKeyId;
+ m->bd->strKeyStore = strKeyStore;
+
+ /* clear all passwords because they are invalid now */
+ m->mpKeyStore->deleteAllSecretKeys(false, true);
+
+ alock.release();
+ AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
+#ifndef VBOX_COM_INPROC
+ m->pParent->i_setModified(Machine::IsModified_NvramStore);
+#endif
+ return S_OK;
+}
+
+
+HRESULT NvramStore::i_getEncryptionSettings(com::Utf8Str &strKeyId,
+ com::Utf8Str &strKeyStore)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ strKeyId = m->bd->strKeyId;
+ strKeyStore = m->bd->strKeyStore;
+
+ return S_OK;
+}
+
+
+int NvramStore::i_addPassword(const Utf8Str &strKeyId, const Utf8Str &strPassword)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), VERR_INVALID_STATE);
+
+ /* keep only required password */
+ if (strKeyId != m->bd->strKeyId)
+ return VINF_SUCCESS;
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ return m->mpKeyStore->addSecretKey(strKeyId, (const uint8_t *)strPassword.c_str(), strPassword.length() + 1);
+}
+
+
+int NvramStore::i_removePassword(const Utf8Str &strKeyId)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), VERR_INVALID_STATE);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ return m->mpKeyStore->deleteSecretKey(strKeyId);
+}
+
+
+int NvramStore::i_removeAllPasswords()
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), VERR_INVALID_STATE);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ m->mpKeyStore->deleteAllSecretKeys(false, true);
+ return VINF_SUCCESS;
+}
+#endif
+
+
+#ifndef VBOX_COM_INPROC
+HRESULT NvramStore::i_retainUefiVarStore(PRTVFS phVfs, bool fReadonly)
+{
+ /* the machine needs to be mutable */
+ AutoMutableStateDependency adep(m->pParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = S_OK;
+ NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
+ if (it != m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFileNvram = it->second;
+ RTVFS hVfsEfiVarStore;
+ uint32_t fMntFlags = fReadonly ? RTVFSMNT_F_READ_ONLY : 0;
+
+ int vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, fMntFlags, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
+ NULL /*pErrInfo*/);
+ if (RT_SUCCESS(vrc))
+ {
+ *phVfs = hVfsEfiVarStore;
+ if (!fReadonly)
+ m->pParent->i_setModified(Machine::IsModified_NvramStore);
+ }
+ else
+ hrc = setError(E_FAIL, tr("Opening the UEFI variable store failed (%Rrc)."), vrc);
+ }
+ else
+ hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
+
+ return hrc;
+}
+
+
+HRESULT NvramStore::i_releaseUefiVarStore(RTVFS hVfs)
+{
+ RTVfsRelease(hVfs);
+ return S_OK;
+}
+
+
+/**
+ * Loads settings from the given machine node.
+ * May be called once right after this object creation.
+ *
+ * @param data Configuration settings.
+ *
+ * @note Locks this object for writing.
+ */
+HRESULT NvramStore::i_loadSettings(const settings::NvramSettings &data)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ m->bd->strNvramPath = data.strNvramPath;
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ m->bd->strKeyId = data.strKeyId;
+ m->bd->strKeyStore = data.strKeyStore;
+#endif
+
+ Utf8Str strTmp(m->bd->strNvramPath);
+ if (strTmp.isNotEmpty())
+ m->pParent->i_copyPathRelativeToMachine(strTmp, m->bd->strNvramPath);
+ if ( m->pParent->i_getFirmwareType() == FirmwareType_BIOS
+ || m->bd->strNvramPath == m->pParent->i_getDefaultNVRAMFilename())
+ m->bd->strNvramPath.setNull();
+
+ return S_OK;
+}
+
+/**
+ * Saves settings to the given machine node.
+ *
+ * @param data Configuration settings.
+ *
+ * @note Locks this object for writing.
+ */
+HRESULT NvramStore::i_saveSettings(settings::NvramSettings &data)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
+
+ data.strNvramPath = m->bd->strNvramPath;
+#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
+ data.strKeyId = m->bd->strKeyId;
+ data.strKeyStore = m->bd->strKeyStore;
+#endif
+
+ int vrc = i_saveStore();
+ if (RT_FAILURE(vrc))
+ return setError(E_FAIL, tr("Failed to save the NVRAM content to disk (%Rrc)"), vrc);
+
+ return S_OK;
+}
+
+void NvramStore::i_rollback()
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ m->bd.rollback();
+}
+
+void NvramStore::i_commit()
+{
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertReturnVoid(autoCaller.isOk());
+
+ /* sanity too */
+ AutoCaller peerCaller(m->pPeer);
+ AssertReturnVoid(peerCaller.isOk());
+
+ /* lock both for writing since we modify both (mPeer is "master" so locked
+ * first) */
+ AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
+
+ if (m->bd.isBackedUp())
+ {
+ m->bd.commit();
+ if (m->pPeer)
+ {
+ /* attach new data to the peer and reshare it */
+ AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
+ m->pPeer->m->bd.attach(m->bd);
+ }
+ }
+}
+
+void NvramStore::i_copyFrom(NvramStore *aThat)
+{
+ AssertReturnVoid(aThat != NULL);
+
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertReturnVoid(autoCaller.isOk());
+
+ /* sanity too */
+ AutoCaller thatCaller(aThat);
+ AssertReturnVoid(thatCaller.isOk());
+
+ /* peer is not modified, lock it for reading (aThat is "master" so locked
+ * first) */
+ AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
+
+ /* this will back up current data */
+ m->bd.assignCopy(aThat->m->bd);
+
+ // Intentionally "forget" the NVRAM file since it must be unique and set
+ // to the correct value before the copy of the settings makes sense.
+ m->bd->strNvramPath.setNull();
+}
+
+HRESULT NvramStore::i_applyDefaults(GuestOSType *aOSType)
+{
+ HRESULT hrc = S_OK;
+
+ if (aOSType->i_recommendedEFISecureBoot())
+ {
+ /* Initialize the UEFI variable store and enroll default keys. */
+ hrc = initUefiVariableStore(0 /*aSize*/);
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<IUefiVariableStore> pVarStore;
+
+ hrc = getUefiVariableStore(pVarStore);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = pVarStore->EnrollOraclePlatformKey();
+ if (SUCCEEDED(hrc))
+ hrc = pVarStore->EnrollDefaultMsSignatures();
+ }
+ }
+ }
+
+ return hrc;
+}
+
+void NvramStore::i_updateNonVolatileStorageFile(const Utf8Str &aNonVolatileStorageFile)
+{
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Utf8Str strTmp(aNonVolatileStorageFile);
+ if (strTmp == m->pParent->i_getDefaultNVRAMFilename())
+ strTmp.setNull();
+
+ if (strTmp == m->bd->strNvramPath)
+ return;
+
+ m->bd.backup();
+ m->bd->strNvramPath = strTmp;
+}
+
+#else
+//
+// private methods
+//
+/*static*/
+DECLCALLBACK(int) NvramStore::i_nvramStoreQuerySize(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
+ uint64_t *pcb)
+{
+ PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
+
+ AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
+ NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
+ if (it != pThis->pNvramStore->m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFile = it->second;
+ return RTVfsFileQuerySize(hVfsFile, pcb);
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/*static*/
+DECLCALLBACK(int) NvramStore::i_nvramStoreReadAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
+ void *pvBuf, size_t cbRead)
+{
+ PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
+
+ AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
+ NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
+ if (it != pThis->pNvramStore->m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFile = it->second;
+
+ int vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+ AssertRC(vrc); RT_NOREF(vrc);
+
+ return RTVfsFileRead(hVfsFile, pvBuf, cbRead, NULL /*pcbRead*/);
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/*static*/
+DECLCALLBACK(int) NvramStore::i_nvramStoreWriteAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
+ const void *pvBuf, size_t cbWrite)
+{
+ PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
+
+ AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
+
+ int vrc = VINF_SUCCESS;
+ NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
+ if (it != pThis->pNvramStore->m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFile = it->second;
+
+ vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+ AssertRC(vrc);
+ vrc = RTVfsFileSetSize(hVfsFile, cbWrite, RTVFSFILE_SIZE_F_NORMAL);
+ if (RT_SUCCESS(vrc))
+ vrc = RTVfsFileWrite(hVfsFile, pvBuf, cbWrite, NULL /*pcbWritten*/);
+ }
+ else
+ {
+ /* Create a new entry. */
+ RTVFSFILE hVfsFile = NIL_RTVFSFILE;
+ vrc = RTVfsFileFromBuffer(RTFILE_O_READ | RTFILE_O_WRITE, pvBuf, cbWrite, &hVfsFile);
+ if (RT_SUCCESS(vrc))
+ pThis->pNvramStore->m->bd->mapNvram[Utf8StrFmt("%s/%s", pszNamespace, pszPath)] = hVfsFile;
+ }
+
+ return vrc;
+}
+
+
+/*static*/
+DECLCALLBACK(int) NvramStore::i_nvramStoreDelete(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath)
+{
+ PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
+
+ AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
+ NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
+ if (it != pThis->pNvramStore->m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFile = it->second;
+ pThis->pNvramStore->m->bd->mapNvram.erase(it);
+ RTVfsFileRelease(hVfsFile);
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/*static*/
+DECLCALLBACK(int) NvramStore::i_SsmSaveExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
+
+ size_t cEntries = pThis->pNvramStore->m->bd->mapNvram.size();
+ AssertReturn(cEntries < 32, VERR_OUT_OF_RANGE); /* Some sanity checking. */
+ pHlp->pfnSSMPutU32(pSSM, (uint32_t)cEntries);
+
+ void *pvData = NULL;
+ size_t cbDataMax = 0;
+ NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.begin();
+
+ while (it != pThis->pNvramStore->m->bd->mapNvram.end())
+ {
+ RTVFSFILE hVfsFile = it->second;
+ uint64_t cbFile;
+
+ int vrc = RTVfsFileQuerySize(hVfsFile, &cbFile);
+ AssertRCReturn(vrc, vrc);
+ AssertReturn(cbFile < _1M, VERR_OUT_OF_RANGE);
+
+ if (cbDataMax < cbFile)
+ {
+ pvData = RTMemRealloc(pvData, cbFile);
+ AssertPtrReturn(pvData, VERR_NO_MEMORY);
+ cbDataMax = cbFile;
+ }
+
+ vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, pvData, cbFile, NULL /*pcbRead*/);
+ AssertRCReturn(vrc, vrc);
+
+ pHlp->pfnSSMPutStrZ(pSSM, it->first.c_str());
+ pHlp->pfnSSMPutU64(pSSM, cbFile);
+ pHlp->pfnSSMPutMem(pSSM, pvData, cbFile);
+ it++;
+ }
+
+ if (pvData)
+ RTMemFree(pvData);
+
+ pThis->pNvramStore->m->fSsmSaved = true;
+ return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
+}
+
+
+/*static*/
+DECLCALLBACK(int) NvramStore::i_SsmLoadExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ AssertMsgReturn(uVersion >= NVRAM_STORE_SAVED_STATE_VERSION, ("%d\n", uVersion),
+ VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
+
+ /* Clear any content first. */
+ NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.begin();
+ while (it != pThis->pNvramStore->m->bd->mapNvram.end())
+ {
+ RTVfsFileRelease(it->second);
+ it++;
+ }
+
+ pThis->pNvramStore->m->bd->mapNvram.clear();
+
+ uint32_t cEntries = 0;
+ int vrc = pHlp->pfnSSMGetU32(pSSM, &cEntries);
+ AssertRCReturn(vrc, vrc);
+ AssertReturn(cEntries < 32, VERR_OUT_OF_RANGE);
+
+ void *pvData = NULL;
+ size_t cbDataMax = 0;
+ while (cEntries--)
+ {
+ char szId[_1K]; /* Lazy developer */
+ uint64_t cbFile = 0;
+
+ vrc = pHlp->pfnSSMGetStrZ(pSSM, &szId[0], sizeof(szId));
+ AssertRCReturn(vrc, vrc);
+
+ vrc = pHlp->pfnSSMGetU64(pSSM, &cbFile);
+ AssertRCReturn(vrc, vrc);
+ AssertReturn(cbFile < _1M, VERR_OUT_OF_RANGE);
+
+ if (cbDataMax < cbFile)
+ {
+ pvData = RTMemRealloc(pvData, cbFile);
+ AssertPtrReturn(pvData, VERR_NO_MEMORY);
+ cbDataMax = cbFile;
+ }
+
+ vrc = pHlp->pfnSSMGetMem(pSSM, pvData, cbFile);
+ AssertRCReturn(vrc, vrc);
+
+ RTVFSFILE hVfsFile;
+ vrc = RTVfsFileFromBuffer(RTFILE_O_READWRITE, pvData, cbFile, &hVfsFile);
+ AssertRCReturn(vrc, vrc);
+
+ pThis->pNvramStore->m->bd->mapNvram[Utf8Str(szId)] = hVfsFile;
+ }
+
+ if (pvData)
+ RTMemFree(pvData);
+
+ /* The marker. */
+ uint32_t u32;
+ vrc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(vrc, vrc);
+ AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+DECLCALLBACK(void *) NvramStore::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVMAINNVRAMSTORE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVFSCONNECTOR, &pDrv->IVfs);
+ return NULL;
+}
+
+
+/**
+ * Destruct a NVRAM store driver instance.
+ *
+ * @returns VBox status code.
+ * @param pDrvIns The driver instance data.
+ */
+DECLCALLBACK(void) NvramStore::i_drvDestruct(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
+ LogFlow(("NvramStore::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
+
+ if (pThis->pNvramStore)
+ {
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
+ if ( !cRefs
+ && !pThis->pNvramStore->m->fSsmSaved)
+ {
+ int vrc = pThis->pNvramStore->i_saveStore();
+ AssertRC(vrc); /** @todo Disk full error? */
+ }
+ }
+}
+
+
+/**
+ * Construct a NVRAM store driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+DECLCALLBACK(int) NvramStore::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ RT_NOREF(fFlags, pCfg);
+ PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
+ LogFlow(("NvramStore::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
+
+ /*
+ * Validate configuration.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", "");
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * IBase.
+ */
+ pDrvIns->IBase.pfnQueryInterface = NvramStore::i_drvQueryInterface;
+
+ pThis->IVfs.pfnQuerySize = NvramStore::i_nvramStoreQuerySize;
+ pThis->IVfs.pfnReadAll = NvramStore::i_nvramStoreReadAll;
+ pThis->IVfs.pfnWriteAll = NvramStore::i_nvramStoreWriteAll;
+ pThis->IVfs.pfnDelete = NvramStore::i_nvramStoreDelete;
+
+ /*
+ * Get the NVRAM store object pointer.
+ */
+ com::Guid uuid(COM_IIDOF(INvramStore));
+ pThis->pNvramStore = (NvramStore *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
+ if (!pThis->pNvramStore)
+ {
+ AssertMsgFailed(("Configuration error: No/bad NVRAM store object!\n"));
+ return VERR_NOT_FOUND;
+ }
+
+ /*
+ * Only the first instance will register the SSM handlers and will do the work on behalf
+ * of all other NVRAM store driver instances when it comes to SSM.
+ */
+ if (pDrvIns->iInstance == 0)
+ {
+ int vrc = PDMDrvHlpSSMRegister(pDrvIns, NVRAM_STORE_SAVED_STATE_VERSION, 0 /*cbGuess*/,
+ NvramStore::i_SsmSaveExec, NvramStore::i_SsmLoadExec);
+ if (RT_FAILURE(vrc))
+ return PDMDrvHlpVMSetError(pDrvIns, vrc, RT_SRC_POS,
+ N_("Failed to register the saved state unit for the NVRAM store"));
+ }
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->pNvramStore->m->cRefs);
+ if (cRefs == 1)
+ {
+ int vrc = pThis->pNvramStore->i_loadStore(pThis->pNvramStore->m->bd->strNvramPath.c_str());
+ if (RT_FAILURE(vrc))
+ {
+ ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
+ return PDMDrvHlpVMSetError(pDrvIns, vrc, RT_SRC_POS,
+ N_("Failed to load the NVRAM store from the file"));
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * NVRAM store driver registration record.
+ */
+const PDMDRVREG NvramStore::DrvReg =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "NvramStore",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "Main NVRAM store driver (Main as in the API).",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_STATUS,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVMAINNVRAMSTORE),
+ /* pfnConstruct */
+ NvramStore::i_drvConstruct,
+ /* pfnDestruct */
+ NvramStore::i_drvDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+#endif /* !VBOX_COM_INPROC */
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */