VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/NvramStoreImpl.cpp@ 108620

Last change on this file since 108620 was 108620, checked in by vboxsync, 8 weeks ago

Main/NvramStore: Changeset r164311 modified the implementation of
NvramStore::getNonVolatileStorageFile() to return failure if no NVRAM
file however the API documents that this attribute may not exist. This
change breaks IMachine::moveTo() if the VM has a snapshot since
snapshots don't have an NVRAM file. Thus restore the behaviour of
getNonVolatileStorageFile() to its previous incarnation where the caller
is required to check for the attribute's existence upon successful
invocation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.2 KB
Line 
1/* $Id: NvramStoreImpl.cpp 108620 2025-03-19 19:29:06Z vboxsync $ */
2/** @file
3 * VirtualBox COM NVRAM store class implementation
4 */
5
6/*
7 * Copyright (C) 2021-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_NVRAMSTORE
29#include "LoggingNew.h"
30
31#include "NvramStoreImpl.h"
32#ifdef VBOX_COM_INPROC
33# include "ConsoleImpl.h"
34#else
35# include "MachineImpl.h"
36# include "GuestOSTypeImpl.h"
37# include "AutoStateDep.h"
38#endif
39#include "UefiVariableStoreImpl.h"
40#include "VirtualBoxImpl.h"
41
42#include "AutoCaller.h"
43
44#include <VBox/com/array.h>
45#include <VBox/vmm/pdmdrv.h>
46#include <VBox/err.h>
47
48#include <iprt/cpp/utils.h>
49#include <iprt/efi.h>
50#include <iprt/file.h>
51#include <iprt/path.h>
52#include <iprt/vfs.h>
53#include <iprt/zip.h>
54
55
56// defines
57////////////////////////////////////////////////////////////////////////////////
58
59/** Version of the NVRAM saved state unit. */
60#define NVRAM_STORE_SAVED_STATE_VERSION 1
61
62
63// globals
64////////////////////////////////////////////////////////////////////////////////
65
66/**
67 * NVRAM store driver instance data.
68 */
69typedef struct DRVMAINNVRAMSTORE
70{
71 /** Pointer to the keyboard object. */
72 NvramStore *pNvramStore;
73 /** Pointer to the driver instance structure. */
74 PPDMDRVINS pDrvIns;
75 /** Our VFS connector interface. */
76 PDMIVFSCONNECTOR IVfs;
77} DRVMAINNVRAMSTORE, *PDRVMAINNVRAMSTORE;
78
79/** The NVRAM store map keyed by namespace/entity. */
80typedef std::map<Utf8Str, RTVFSFILE> NvramStoreMap;
81/** The NVRAM store map iterator. */
82typedef std::map<Utf8Str, RTVFSFILE>::iterator NvramStoreIter;
83
84struct BackupableNvramStoreData
85{
86 BackupableNvramStoreData()
87 { }
88
89 /** The NVRAM file path. */
90 com::Utf8Str strNvramPath;
91#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
92 /** The key id used for encrypting the NVRAM file */
93 com::Utf8Str strKeyId;
94 /** The key store containing the encrypting DEK */
95 com::Utf8Str strKeyStore;
96#endif
97};
98
99/////////////////////////////////////////////////////////////////////////////
100// NvramStore::Data structure
101/////////////////////////////////////////////////////////////////////////////
102
103struct NvramStore::Data
104{
105 Data()
106 : pParent(NULL)
107#ifdef VBOX_COM_INPROC
108 , cRefs(0)
109 , fSsmSaved(false)
110#endif
111#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
112 , mpKeyStore(NULL)
113#endif
114 { }
115
116#ifdef VBOX_COM_INPROC
117 /** The Console owning this NVRAM store. */
118 Console * const pParent;
119 /** Number of references held to this NVRAM store from the various devices/drivers. */
120 volatile uint32_t cRefs;
121 /** Flag whether the NVRAM data was saved during a save state operation
122 * preventing it from getting written to the backing file. */
123 bool fSsmSaved;
124#else
125 /** The Machine object owning this NVRAM store. */
126 Machine * const pParent;
127 /** The peer NVRAM store object. */
128 ComObjPtr<NvramStore> pPeer;
129 /** The UEFI variable store. */
130 const ComObjPtr<UefiVariableStore> pUefiVarStore;
131#endif
132
133#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
134 /* Store for secret keys. */
135 SecretKeyStore *mpKeyStore;
136#endif
137
138 Backupable<BackupableNvramStoreData> bd;
139
140 /** The NVRAM store. */
141 NvramStoreMap mapNvram;
142};
143
144// constructor / destructor
145////////////////////////////////////////////////////////////////////////////////
146
147DEFINE_EMPTY_CTOR_DTOR(NvramStore)
148
149HRESULT NvramStore::FinalConstruct()
150{
151 return BaseFinalConstruct();
152}
153
154void NvramStore::FinalRelease()
155{
156 uninit();
157 BaseFinalRelease();
158}
159
160// public initializer/uninitializer for internal purposes only
161/////////////////////////////////////////////////////////////////////////////
162
163/**
164 * Initialization stuff shared across the different methods.
165 *
166 * @returns COM result indicator
167 */
168int NvramStore::initImpl()
169{
170 m = new Data();
171
172#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
173# ifdef VBOX_COM_INPROC
174 bool fNonPageable = true;
175# else
176 /* Non-pageable memory is not accessible for non-VM process */
177 bool fNonPageable = false;
178# endif
179
180 m->mpKeyStore = new SecretKeyStore(fNonPageable /* fKeyBufNonPageable */);
181 AssertReturn(m->mpKeyStore, VERR_NO_MEMORY);
182#endif
183
184 return VINF_SUCCESS;
185}
186
187
188#if !defined(VBOX_COM_INPROC)
189/**
190 * Initializes the NVRAM store object.
191 *
192 * @returns COM result indicator
193 */
194HRESULT NvramStore::init(Machine *aParent)
195{
196 LogFlowThisFuncEnter();
197 LogFlowThisFunc(("aParent: %p\n", aParent));
198
199 ComAssertRet(aParent, E_INVALIDARG);
200
201 /* Enclose the state transition NotReady->InInit->Ready */
202 AutoInitSpan autoInitSpan(this);
203 AssertReturn(autoInitSpan.isOk(), E_FAIL);
204
205 int vrc = initImpl();
206 if (RT_FAILURE(vrc))
207 return E_FAIL;
208
209 /* share the parent weakly */
210 unconst(m->pParent) = aParent;
211
212 m->bd.allocate();
213
214 autoInitSpan.setSucceeded();
215
216 LogFlowThisFuncLeave();
217 return S_OK;
218}
219
220/**
221 * Initializes the NVRAM store object given another NVRAM store object
222 * (a kind of copy constructor). This object shares data with
223 * the object passed as an argument.
224 *
225 * @note This object must be destroyed before the original object
226 * it shares data with is destroyed.
227 */
228HRESULT NvramStore::init(Machine *aParent, NvramStore *that)
229{
230 LogFlowThisFuncEnter();
231 LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
232
233 ComAssertRet(aParent && that, E_INVALIDARG);
234
235 /* Enclose the state transition NotReady->InInit->Ready */
236 AutoInitSpan autoInitSpan(this);
237 AssertReturn(autoInitSpan.isOk(), E_FAIL);
238
239 initImpl();
240
241 unconst(m->pParent) = aParent;
242 m->pPeer = that;
243
244 AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
245 m->bd.share(that->m->bd);
246
247 autoInitSpan.setSucceeded();
248
249 LogFlowThisFuncLeave();
250 return S_OK;
251}
252
253/**
254 * Initializes the guest object given another guest object
255 * (a kind of copy constructor). This object makes a private copy of data
256 * of the original object passed as an argument.
257 */
258HRESULT NvramStore::initCopy(Machine *aParent, NvramStore *that)
259{
260 LogFlowThisFuncEnter();
261 LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
262
263 ComAssertRet(aParent && that, E_INVALIDARG);
264
265 /* Enclose the state transition NotReady->InInit->Ready */
266 AutoInitSpan autoInitSpan(this);
267 AssertReturn(autoInitSpan.isOk(), E_FAIL);
268
269 initImpl();
270
271 unconst(m->pParent) = aParent;
272 // mPeer is left null
273
274 AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
275 m->bd.attachCopy(that->m->bd);
276
277 autoInitSpan.setSucceeded();
278
279 LogFlowThisFuncLeave();
280 return S_OK;
281}
282
283#else
284
285/**
286 * Initializes the NVRAM store object.
287 *
288 * @returns COM result indicator
289 * @param aParent Handle of our parent object
290 * @param strNonVolatileStorageFile The NVRAM file path.
291 */
292HRESULT NvramStore::init(Console *aParent, const com::Utf8Str &strNonVolatileStorageFile)
293{
294 LogFlowThisFunc(("aParent=%p\n", aParent));
295
296 ComAssertRet(aParent, E_INVALIDARG);
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 initImpl();
303
304 unconst(m->pParent) = aParent;
305
306 m->bd.allocate();
307 m->bd->strNvramPath = strNonVolatileStorageFile;
308
309 /* Confirm a successful initialization */
310 autoInitSpan.setSucceeded();
311
312 return S_OK;
313}
314#endif /* VBOX_COM_INPROC */
315
316
317/**
318 * Uninitializes the instance and sets the ready flag to FALSE.
319 * Called either from FinalRelease() or by the parent when it gets destroyed.
320 */
321void NvramStore::uninit()
322{
323 LogFlowThisFuncEnter();
324
325 /* Enclose the state transition Ready->InUninit->NotReady */
326 AutoUninitSpan autoUninitSpan(this);
327 if (autoUninitSpan.uninitDone())
328 return;
329
330 unconst(m->pParent) = NULL;
331#ifndef VBOX_COM_INPROC
332 unconst(m->pUefiVarStore) = NULL;
333#endif
334
335 /* Delete the NVRAM content. */
336 NvramStoreIter it = m->mapNvram.begin();
337 while (it != m->mapNvram.end())
338 {
339 RTVfsFileRelease(it->second);
340 it++;
341 }
342
343 m->mapNvram.clear();
344 m->bd.free();
345
346#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
347 if (m->mpKeyStore != NULL)
348 delete m->mpKeyStore;
349#endif
350
351 delete m;
352 m = NULL;
353
354 LogFlowThisFuncLeave();
355}
356
357HRESULT NvramStore::getNonVolatileStorageFile(com::Utf8Str &aNonVolatileStorageFile)
358{
359#ifndef VBOX_COM_INPROC
360 Utf8Str strTmp;
361 {
362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
363 strTmp = m->bd->strNvramPath;
364 }
365
366 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
367 if (strTmp.isEmpty())
368 strTmp = m->pParent->i_getDefaultNVRAMFilename();
369 if (strTmp.isNotEmpty())
370 m->pParent->i_calculateFullPath(strTmp, aNonVolatileStorageFile);
371#else
372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
373 aNonVolatileStorageFile = m->bd->strNvramPath;
374#endif
375
376 return S_OK;
377}
378
379
380HRESULT NvramStore::getUefiVariableStore(ComPtr<IUefiVariableStore> &aUefiVarStore)
381{
382#ifndef VBOX_COM_INPROC
383 Utf8Str strPath;
384 NvramStore::getNonVolatileStorageFile(strPath);
385 if (strPath.isEmpty())
386 return setError(E_FAIL, tr("No NVRAM store file found"));
387
388 /* We need a write lock because of the lazy initialization. */
389 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
390
391 /* Check if we have to create the UEFI variable store object */
392 HRESULT hrc = S_OK;
393 if (!m->pUefiVarStore)
394 {
395 /* Load the NVRAM file first if it isn't already. */
396 if (!m->mapNvram.size())
397 {
398 int vrc = i_loadStore(strPath.c_str());
399 if (RT_FAILURE(vrc))
400 hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
401 }
402
403 if (SUCCEEDED(hrc))
404 {
405 NvramStoreIter it = m->mapNvram.find("efi/nvram");
406 if (it != m->mapNvram.end())
407 {
408 unconst(m->pUefiVarStore).createObject();
409 m->pUefiVarStore->init(this, m->pParent);
410 }
411 else
412 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine"));
413 }
414 }
415
416 if (SUCCEEDED(hrc))
417 {
418 m->pUefiVarStore.queryInterfaceTo(aUefiVarStore.asOutParam());
419 /* The "modified" state is handled by i_retainUefiVarStore. */
420 }
421
422 return hrc;
423#else
424 NOREF(aUefiVarStore);
425 return E_NOTIMPL;
426#endif
427}
428
429
430HRESULT NvramStore::getKeyId(com::Utf8Str &aKeyId)
431{
432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
433
434#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
435 aKeyId = m->bd->strKeyId;
436#else
437 aKeyId = com::Utf8Str::Empty;
438#endif
439
440 return S_OK;
441}
442
443
444HRESULT NvramStore::getKeyStore(com::Utf8Str &aKeyStore)
445{
446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
447
448#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
449 aKeyStore = m->bd->strKeyStore;
450#else
451 aKeyStore = com::Utf8Str::Empty;
452#endif
453
454 return S_OK;
455}
456
457
458HRESULT NvramStore::initUefiVariableStore(ULONG aSize)
459{
460#ifndef VBOX_COM_INPROC
461 if (aSize != 0)
462 return setError(E_NOTIMPL, tr("Supporting another NVRAM size apart from the default one is not supported right now"));
463
464 /* the machine needs to be mutable */
465 AutoMutableStateDependency adep(m->pParent);
466 if (FAILED(adep.hrc())) return adep.hrc();
467
468 Utf8Str strPath;
469 NvramStore::getNonVolatileStorageFile(strPath);
470
471 /* We need a write lock because of the lazy initialization. */
472 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
473 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
474
475 if (m->pParent->i_getFirmwareType() == FirmwareType_BIOS)
476 return setError(VBOX_E_NOT_SUPPORTED, tr("The selected firmware type doesn't support a UEFI variable store"));
477
478 /* Load the NVRAM file first if it isn't already. */
479 HRESULT hrc = S_OK;
480 if (!m->mapNvram.size())
481 {
482 int vrc = i_loadStore(strPath.c_str());
483 if (RT_FAILURE(vrc))
484 hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
485 }
486
487 PlatformArchitecture_T const enmArch = m->pParent->i_getPlatform()->i_getArchitecture();
488 if (SUCCEEDED(hrc))
489 {
490 int vrc = VINF_SUCCESS;
491 RTVFSFILE hVfsUefiVarStore = NIL_RTVFSFILE;
492 NvramStoreIter it = m->mapNvram.find("efi/nvram");
493 if (it != m->mapNvram.end())
494 hVfsUefiVarStore = it->second;
495 else
496 {
497 /* Create a new file. */
498 vrc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsUefiVarStore);
499 if (RT_SUCCESS(vrc))
500 {
501 /** @todo The size is hardcoded to match what the firmware image uses right now which is a gross hack... */
502 uint64_t cbUefi = enmArch == PlatformArchitecture_ARM ? 3 * _256K : 132 * _4K;
503 vrc = RTVfsFileSetSize(hVfsUefiVarStore, cbUefi, RTVFSFILE_SIZE_F_NORMAL);
504 if (RT_SUCCESS(vrc))
505 m->mapNvram["efi/nvram"] = hVfsUefiVarStore;
506 else
507 RTVfsFileRelease(hVfsUefiVarStore);
508 }
509 }
510
511 if (RT_SUCCESS(vrc))
512 { /* We want to create NVRAM files matching the default QEMU_VARS.fd for now, see https://github.com/tianocore/edk2/commit/bf57a42a0e2cf1c68e9db2f61c82ce93c806ab07. */
513 static const RTEFIVARSTORECFG s_EfiNvramCfgArm = { 3 * _256K /*cbFv*/, _256K /*cbBlock*/, _256K /*cbVarStore*/, 0 /*cbNvEventLog*/, 2 * _256K /*cbFtw*/, 0x3ffe0 /*cbWriteQueue*/};
514 /* We want to create NVRAM files matching the default OVMF_VARS.fd for now, see https://github.com/tianocore/edk2/commit/b24fca05751f8222acf264853709012e0ab7bf49. */
515 static const RTEFIVARSTORECFG s_EfiNvramCfgX86 = { 132 * _4K /*cbFv*/, _4K /*cbBlock*/, _256K /*cbVarStore*/, _4K /*cbNvEventLog*/, 67 * _4K /*cbFtw*/, 0x00fe0 /*cbWriteQueue*/};
516
517 vrc = RTEfiVarStoreCreate(hVfsUefiVarStore, 0 /*offStore*/, enmArch == PlatformArchitecture_ARM ? &s_EfiNvramCfgArm : &s_EfiNvramCfgX86,
518 NULL /*pErrInfo*/);
519 if (RT_FAILURE(vrc))
520 return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
521 }
522 else
523 return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
524
525 m->pParent->i_setModified(Machine::IsModified_NvramStore);
526 }
527
528 return hrc;
529#else
530 NOREF(aSize);
531 return E_NOTIMPL;
532#endif
533}
534
535
536/**
537 * Returns the path of the non-volatile stroage file.
538 *
539 * @returns Path to non-volatile stroage file. Empty if not supported / found.
540 *
541 * @note Convenience function for machine object or other callers.
542 */
543Utf8Str NvramStore::i_getNonVolatileStorageFile()
544{
545 AutoCaller autoCaller(this);
546 AssertReturn(autoCaller.isOk(), Utf8Str::Empty);
547
548 Utf8Str strTmp;
549 NvramStore::getNonVolatileStorageFile(strTmp);
550 return strTmp;
551}
552
553
554/**
555 * Loads the NVRAM store from the given TAR filesystem stream.
556 *
557 * @returns IPRT status code.
558 * @param hVfsFssTar Handle to the tar filesystem stream.
559 */
560int NvramStore::i_loadStoreFromTar(RTVFSFSSTREAM hVfsFssTar)
561{
562 int vrc = VINF_SUCCESS;
563
564 /*
565 * Process the stream.
566 */
567 for (;;)
568 {
569 /*
570 * Retrieve the next object.
571 */
572 char *pszName;
573 RTVFSOBJ hVfsObj;
574 vrc = RTVfsFsStrmNext(hVfsFssTar, &pszName, NULL, &hVfsObj);
575 if (RT_FAILURE(vrc))
576 {
577 if (vrc == VERR_EOF)
578 vrc = VINF_SUCCESS;
579 break;
580 }
581
582 RTFSOBJINFO UnixInfo;
583 vrc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
584 if (RT_SUCCESS(vrc))
585 {
586 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
587 {
588 case RTFS_TYPE_FILE:
589 {
590 LogRel(("NvramStore: Loading '%s' from archive\n", pszName));
591 RTVFSIOSTREAM hVfsIosEntry = RTVfsObjToIoStream(hVfsObj);
592 Assert(hVfsIosEntry != NIL_RTVFSIOSTREAM);
593
594 RTVFSFILE hVfsFileEntry;
595 vrc = RTVfsMemorizeIoStreamAsFile(hVfsIosEntry, RTFILE_O_READ | RTFILE_O_WRITE, &hVfsFileEntry);
596 if (RT_FAILURE(vrc))
597 break;
598 RTVfsIoStrmRelease(hVfsIosEntry);
599
600 m->mapNvram[Utf8Str(pszName)] = hVfsFileEntry;
601 break;
602 }
603 case RTFS_TYPE_DIRECTORY:
604 break;
605 default:
606 vrc = VERR_NOT_SUPPORTED;
607 break;
608 }
609 }
610
611 /*
612 * Release the current object and string.
613 */
614 RTVfsObjRelease(hVfsObj);
615 RTStrFree(pszName);
616
617 if (RT_FAILURE(vrc))
618 break;
619 }
620
621 return vrc;
622}
623
624#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
625
626/**
627 * Sets up the encryption or decryption machinery.
628 *
629 * @returns VBox status code.
630 * @param hVfsIosInOut Handle to the input stream to be decrypted or the destination to the encrypted
631 * output is written to.
632 * @param fEncrypt Flag whether to setup encryption or decryption.
633 * @param ppCryptoIf Where to store the pointer to the cryptographic interface which needs to be released
634 * when done.
635 * @param ppKey Where to store the pointer to the secret key buffer which needs to be released when done.
636 * @param phVfsIos Where to store the handle to the plaintext I/O stream (either input or output) on success.
637 */
638int NvramStore::i_setupEncryptionOrDecryption(RTVFSIOSTREAM hVfsIosInOut, bool fEncrypt,
639 PCVBOXCRYPTOIF *ppCryptoIf, SecretKey **ppKey,
640 PRTVFSIOSTREAM phVfsIos)
641{
642 int vrc = VINF_SUCCESS;
643 PCVBOXCRYPTOIF pCryptoIf = NULL;
644 SecretKey *pKey = NULL;
645 const char *pszPassword = NULL;
646
647 vrc = i_retainCryptoIf(&pCryptoIf);
648 if (RT_SUCCESS(vrc))
649 {
650 vrc = m->mpKeyStore->retainSecretKey(m->bd->strKeyId, &pKey);
651 if (RT_SUCCESS(vrc))
652 {
653 pszPassword = (const char *)pKey->getKeyBuffer();
654 if (fEncrypt)
655 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmEncrypt(hVfsIosInOut, m->bd->strKeyStore.c_str(), pszPassword,
656 phVfsIos);
657 else
658 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosInOut, m->bd->strKeyStore.c_str(), pszPassword,
659 phVfsIos);
660 if (RT_SUCCESS(vrc))
661 {
662 *ppCryptoIf = pCryptoIf;
663 *ppKey = pKey;
664 return VINF_SUCCESS;
665 }
666 else
667 LogRelMax(10, ("Failed to decrypt the NVRAM store using secret key ID '%s' with %Rrc\n",
668 m->bd->strKeyId.c_str(), vrc));
669
670 m->mpKeyStore->releaseSecretKey(m->bd->strKeyId);
671 }
672 else
673 LogRelMax(10, ("Failed to retain the secret key ID '%s' with %Rrc\n",
674 m->bd->strKeyId.c_str(), vrc));
675
676 i_releaseCryptoIf(pCryptoIf);
677 }
678 else
679 LogRelMax(10, ("Failed to retain the cryptographic interface with %Rrc\n", vrc));
680
681 return vrc;
682}
683
684/**
685 * Releases all resources acquired in NvramStore::i_setupEncryptionOrDecryption().
686 *
687 * @param hVfsIos Handle to the I/O stream previously created.
688 * @param pCryptoIf Pointer to the cryptographic interface being released.
689 * @param pKey Pointer to the key buffer being released.
690 */
691void NvramStore::i_releaseEncryptionOrDecryptionResources(RTVFSIOSTREAM hVfsIos, PCVBOXCRYPTOIF pCryptoIf,
692 SecretKey *pKey)
693{
694 Assert(hVfsIos != NIL_RTVFSIOSTREAM);
695 AssertPtr(pCryptoIf);
696 AssertPtr(pKey);
697
698 i_releaseCryptoIf(pCryptoIf);
699 pKey->release();
700 RTVfsIoStrmRelease(hVfsIos);
701}
702
703#endif /* VBOX_WITH_FULL_VM_ENCRYPTION */
704
705/**
706 * Loads the NVRAM store from the given VFS directory handle.
707 *
708 * @returns IPRT status code.
709 * @param hVfsDir Handle to the NVRAM root VFS directory.
710 * @param pszNamespace The namespace to load the content for.
711 */
712int NvramStore::i_loadStoreFromDir(RTVFSDIR hVfsDir, const char *pszNamespace)
713{
714 int vrc = VINF_SUCCESS;
715
716 RTVFSDIR hNamespaceDir = NIL_RTVFSDIR;
717 vrc = RTVfsDirOpenDir(hVfsDir, pszNamespace, 0 /*fFlags*/, &hNamespaceDir);
718 if (RT_SUCCESS(vrc))
719 {
720 for (;;)
721 {
722 RTDIRENTRYEX DirEntry; /* ASSUMES that no entry has a longer name than what RTDIRENTRYEX provides by default. */
723 size_t cbDir = sizeof(DirEntry);
724 vrc = RTVfsDirReadEx(hNamespaceDir, &DirEntry, &cbDir, RTFSOBJATTRADD_NOTHING);
725 if (RT_FAILURE(vrc))
726 {
727 if (vrc == VERR_NO_MORE_FILES)
728 vrc = VINF_SUCCESS;
729 break;
730 }
731
732 if (RT_SUCCESS(vrc))
733 {
734 switch (DirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
735 {
736 case RTFS_TYPE_FILE:
737 {
738 LogRel(("NvramStore: Loading '%s' from directory '%s'\n", DirEntry.szName, pszNamespace));
739
740 RTVFSIOSTREAM hVfsIosEntry;
741 vrc = RTVfsDirOpenFileAsIoStream(hNamespaceDir, DirEntry.szName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosEntry);
742 if (RT_SUCCESS(vrc))
743 {
744 RTVFSIOSTREAM hVfsIosDecrypted = NIL_RTVFSIOSTREAM;
745
746#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
747 PCVBOXCRYPTOIF pCryptoIf = NULL;
748 SecretKey *pKey = NULL;
749
750 if ( m->bd->strKeyId.isNotEmpty()
751 && m->bd->strKeyStore.isNotEmpty())
752 vrc = i_setupEncryptionOrDecryption(hVfsIosEntry, false /*fEncrypt*/,
753 &pCryptoIf, &pKey, &hVfsIosDecrypted);
754#endif
755 if (RT_SUCCESS(vrc))
756 {
757 RTVFSFILE hVfsFileEntry;
758 vrc = RTVfsMemorizeIoStreamAsFile(hVfsIosDecrypted != NIL_RTVFSIOSTREAM
759 ? hVfsIosDecrypted
760 : hVfsIosEntry,
761 RTFILE_O_READ | RTFILE_O_WRITE, &hVfsFileEntry);
762 if (RT_SUCCESS(vrc))
763 m->mapNvram[Utf8StrFmt("%s/%s", pszNamespace, DirEntry.szName)] = hVfsFileEntry;
764 }
765
766#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
767 if (hVfsIosDecrypted != NIL_RTVFSIOSTREAM)
768 i_releaseEncryptionOrDecryptionResources(hVfsIosDecrypted, pCryptoIf, pKey);
769#endif
770
771 RTVfsIoStrmRelease(hVfsIosEntry);
772 }
773 else
774 LogRel(("Failed to open '%s' in NVRAM store '%s', vrc=%Rrc\n", DirEntry.szName, pszNamespace, vrc));
775
776 break;
777 }
778 case RTFS_TYPE_DIRECTORY:
779 break;
780 default:
781 vrc = VERR_NOT_SUPPORTED;
782 break;
783 }
784 }
785
786 if (RT_FAILURE(vrc))
787 break;
788 }
789
790 RTVfsDirRelease(hNamespaceDir);
791 }
792
793 return vrc;
794}
795
796
797/**
798 * Loads the NVRAM store.
799 *
800 * @returns IPRT status code.
801 */
802int NvramStore::i_loadStore(const char *pszPath)
803{
804 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
805 AssertReturn(*pszPath, VERR_PATH_ZERO_LENGTH); /* IPRT below doesn't like empty strings. */
806
807 uint64_t cbStore = 0;
808 int vrc = RTFileQuerySizeByPath(pszPath, &cbStore);
809 if (RT_SUCCESS(vrc))
810 {
811 if (cbStore <= _1M) /* Arbitrary limit to fend off bogus files because the file will be read into memory completely. */
812 {
813 /*
814 * Old NVRAM files just consist of the EFI variable store whereas starting
815 * with VirtualBox 7.0 and the introduction of the TPM the need to handle multiple
816 * independent NVRAM files came up. For those scenarios all NVRAM states are collected
817 * in a tar archive.
818 *
819 * Here we detect whether the file is the new tar archive format or whether it is just
820 * the plain EFI variable store file.
821 */
822 RTVFSIOSTREAM hVfsIosNvram;
823 vrc = RTVfsIoStrmOpenNormal(pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE,
824 &hVfsIosNvram);
825 if (RT_SUCCESS(vrc))
826 {
827 RTVFSIOSTREAM hVfsIosDecrypted = NIL_RTVFSIOSTREAM;
828
829#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
830 PCVBOXCRYPTOIF pCryptoIf = NULL;
831 SecretKey *pKey = NULL;
832
833 if ( m->bd->strKeyId.isNotEmpty()
834 && m->bd->strKeyStore.isNotEmpty())
835 vrc = i_setupEncryptionOrDecryption(hVfsIosNvram, false /*fEncrypt*/,
836 &pCryptoIf, &pKey, &hVfsIosDecrypted);
837#endif
838 if (RT_SUCCESS(vrc))
839 {
840 /* Read the content. */
841 RTVFSFILE hVfsFileNvram;
842 vrc = RTVfsMemorizeIoStreamAsFile( hVfsIosDecrypted != NIL_RTVFSIOSTREAM
843 ? hVfsIosDecrypted
844 : hVfsIosNvram,
845 RTFILE_O_READ, &hVfsFileNvram);
846 if (RT_SUCCESS(vrc))
847 {
848 if (RT_SUCCESS(vrc))
849 {
850 /* Try to parse it as an EFI variable store. */
851 RTERRINFOSTATIC ErrInfo;
852 RTVFS hVfsEfiVarStore;
853 vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, RTVFSMNT_F_READ_ONLY, 0 /*fVarStoreFlags*/,
854 &hVfsEfiVarStore, RTErrInfoInitStatic(&ErrInfo));
855 if (RT_SUCCESS(vrc))
856 {
857 vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
858 AssertRC(vrc);
859
860 RTVfsFileRetain(hVfsFileNvram); /* Retain a new reference for the map. */
861 m->mapNvram[Utf8Str("efi/nvram")] = hVfsFileNvram;
862
863 RTVfsRelease(hVfsEfiVarStore);
864 }
865 else if (vrc == VERR_VFS_UNKNOWN_FORMAT)
866 {
867 /* Check for the new style tar archive. */
868 vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
869 AssertRC(vrc);
870
871 RTVFSIOSTREAM hVfsIosTar = RTVfsFileToIoStream(hVfsFileNvram);
872 Assert(hVfsIosTar != NIL_RTVFSIOSTREAM);
873
874 RTVFSFSSTREAM hVfsFssTar;
875 vrc = RTZipTarFsStreamFromIoStream(hVfsIosTar, 0 /*fFlags*/, &hVfsFssTar);
876 RTVfsIoStrmRelease(hVfsIosTar);
877 if (RT_SUCCESS(vrc))
878 {
879 vrc = i_loadStoreFromTar(hVfsFssTar);
880 RTVfsFsStrmRelease(hVfsFssTar);
881 }
882 else
883 LogRel(("The given NVRAM file is neither a raw UEFI variable store nor a tar archive (opening failed with %Rrc)\n", vrc));
884 }
885 else
886 LogRel(("Opening the UEFI variable store '%s' failed with %Rrc%RTeim\n", pszPath, vrc, &ErrInfo.Core));
887
888 RTVfsFileRelease(hVfsFileNvram);
889 }
890 else
891 LogRel(("Failed to memorize NVRAM store '%s' with %Rrc\n", pszPath, vrc));
892 }
893 }
894
895#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
896 if (hVfsIosDecrypted != NIL_RTVFSIOSTREAM)
897 i_releaseEncryptionOrDecryptionResources(hVfsIosDecrypted, pCryptoIf, pKey);
898#endif
899
900 RTVfsIoStrmRelease(hVfsIosNvram);
901 }
902 else
903 LogRelMax(10, ("NVRAM store '%s' couldn't be opened with %Rrc\n", pszPath, vrc));
904 }
905 else
906 {
907 LogRelMax(10, ("NVRAM store '%s' exceeds limit of %u bytes, actual size is %u\n",
908 pszPath, _1M, cbStore));
909 vrc = VERR_OUT_OF_RANGE;
910 }
911 }
912 else if (vrc == VERR_IS_A_DIRECTORY) /* Valid if the NVRAM was saved with VBoxInternal2/SaveNvramContentAsDirectory 1. */
913 {
914 RTVFSDIR hNvramDir = NIL_RTVFSDIR;
915 vrc = RTVfsDirOpenNormal(pszPath, 0 /*fFlags*/, &hNvramDir);
916 if (RT_SUCCESS(vrc))
917 {
918 for (;;)
919 {
920 RTDIRENTRYEX DirEntry; /* ASSUMES that no entry has a longer name than what RTDIRENTRYEX provides by default. */
921 size_t cbDir = sizeof(DirEntry);
922
923 vrc = RTVfsDirReadEx(hNvramDir, &DirEntry, &cbDir, RTFSOBJATTRADD_NOTHING);
924 if (RT_FAILURE(vrc))
925 {
926 if (vrc == VERR_NO_MORE_FILES)
927 vrc = VINF_SUCCESS;
928 break;
929 }
930
931 /* This ASSUMES that the structure follows the <namespace>/<file> naming scheme. */
932 if (RT_SUCCESS(vrc))
933 {
934 switch (DirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
935 {
936 case RTFS_TYPE_FILE:
937 break;
938 case RTFS_TYPE_DIRECTORY:
939 {
940 if ( (DirEntry.szName[0] == '.' && DirEntry.szName[1] == '\0')
941 || (DirEntry.szName[0] == '.' && DirEntry.szName[1] == '.' && DirEntry.szName[2] == '\0'))
942 break;
943
944 vrc = i_loadStoreFromDir(hNvramDir, DirEntry.szName);
945 break;
946 }
947 default:
948 vrc = VERR_NOT_SUPPORTED;
949 break;
950 }
951 }
952
953 if (RT_FAILURE(vrc))
954 break;
955 }
956
957 RTVfsDirRelease(hNvramDir);
958 }
959 else
960 LogRelMax(10, ("NVRAM store '%s' couldn't be opened as a directory, vrc=%Rrc\n", pszPath, vrc));
961
962 }
963 else if (vrc == VERR_FILE_NOT_FOUND) /* Valid for the first run where no NVRAM file is there. */
964 vrc = VINF_SUCCESS;
965
966 return vrc;
967}
968
969
970/**
971 * Saves the NVRAM store as a tar archive.
972 */
973int NvramStore::i_saveStoreAsTar(const char *pszPath)
974{
975 uint32_t offError = 0;
976 RTERRINFOSTATIC ErrInfo;
977 RTVFSIOSTREAM hVfsIos;
978
979 int vrc = RTVfsChainOpenIoStream(pszPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
980 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
981 if (RT_SUCCESS(vrc))
982 {
983 RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
984
985#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
986 PCVBOXCRYPTOIF pCryptoIf = NULL;
987 SecretKey *pKey = NULL;
988
989 if ( m->bd->strKeyId.isNotEmpty()
990 && m->bd->strKeyStore.isNotEmpty())
991 vrc = i_setupEncryptionOrDecryption(hVfsIos, true /*fEncrypt*/,
992 &pCryptoIf, &pKey, &hVfsIosEncrypted);
993#endif
994
995 if (RT_SUCCESS(vrc))
996 {
997 RTVFSFSSTREAM hVfsFss;
998 vrc = RTZipTarFsStreamToIoStream( hVfsIosEncrypted != NIL_RTVFSIOSTREAM
999 ? hVfsIosEncrypted
1000 : hVfsIos,
1001 RTZIPTARFORMAT_GNU, 0 /*fFlags*/, &hVfsFss);
1002 if (RT_SUCCESS(vrc))
1003 {
1004 NvramStoreIter it = m->mapNvram.begin();
1005
1006 while (it != m->mapNvram.end())
1007 {
1008 RTVFSFILE hVfsFile = it->second;
1009
1010 vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1011 AssertRC(vrc);
1012
1013 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile);
1014 vrc = RTVfsFsStrmAdd(hVfsFss, it->first.c_str(), hVfsObj, 0 /*fFlags*/);
1015 RTVfsObjRelease(hVfsObj);
1016 if (RT_FAILURE(vrc))
1017 break;
1018
1019 it++;
1020 }
1021
1022 RTVfsFsStrmRelease(hVfsFss);
1023 }
1024
1025#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1026 if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
1027 i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
1028#endif
1029 }
1030
1031 RTVfsIoStrmRelease(hVfsIos);
1032 }
1033
1034 return vrc;
1035}
1036
1037
1038/**
1039 * Saves the NVRAM store as a directory tree.
1040 */
1041int NvramStore::i_saveStoreAsDir(const char *pszPath)
1042{
1043 int vrc = VINF_SUCCESS;
1044 if (RTDirExists(pszPath))
1045 vrc = RTDirRemoveRecursive(pszPath, RTDIRRMREC_F_CONTENT_AND_DIR);
1046 else if (RTPathExists(pszPath))
1047 vrc = RTPathUnlink(pszPath, 0 /*fUnlink*/);
1048 if (RT_FAILURE(vrc))
1049 {
1050 LogRel(("Failed to delete existing NVRAM store '%s': %Rrc\n", pszPath, vrc));
1051 return vrc;
1052 }
1053
1054 vrc = RTDirCreate(pszPath, 0700 /*fMode*/, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1055 if (RT_SUCCESS(vrc))
1056 {
1057 NvramStoreIter it = m->mapNvram.begin();
1058
1059 while (it != m->mapNvram.end())
1060 {
1061 /** @todo r=aeichner This is pretty in-efficient but not called often (not at all by default)
1062 * and there aren't many entries anyway. */
1063 char szPathOut[RTPATH_MAX];
1064 char szPathFile[RTPATH_MAX];
1065
1066 /* Construct the path excluding the filename. */
1067 vrc = RTStrCopy(szPathFile, sizeof(szPathFile), it->first.c_str());
1068 if (RT_FAILURE(vrc))
1069 break;
1070 RTPathStripFilename(szPathFile);
1071 vrc = RTPathJoin(szPathOut, sizeof(szPathOut), pszPath, szPathFile);
1072 if (RT_FAILURE(vrc))
1073 break;
1074
1075 /* Create the directory structure. */
1076 vrc = RTDirCreateFullPathEx(szPathOut, 0700 /*fMode*/, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1077 if (RT_FAILURE(vrc))
1078 break;
1079
1080 vrc = RTVfsFileSeek(it->second, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1081 AssertRC(vrc);
1082
1083 /* Construct path, including the filename now. */
1084 vrc = RTPathJoin(szPathOut, sizeof(szPathOut), pszPath, it->first.c_str());
1085 if (RT_FAILURE(vrc))
1086 break;
1087
1088 /* Write the file, encrypting it if required. */
1089 RTVFSFILE hVfsFile;
1090 vrc = RTVfsFileOpenNormal(szPathOut, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
1091 &hVfsFile);
1092 if (RT_SUCCESS(vrc))
1093 {
1094 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
1095 RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
1096
1097#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1098 PCVBOXCRYPTOIF pCryptoIf = NULL;
1099 SecretKey *pKey = NULL;
1100
1101 if ( m->bd->strKeyId.isNotEmpty()
1102 && m->bd->strKeyStore.isNotEmpty())
1103 vrc = i_setupEncryptionOrDecryption(hVfsIos, true /*fEncrypt*/,
1104 &pCryptoIf, &pKey, &hVfsIosEncrypted);
1105#endif
1106
1107 if (RT_SUCCESS(vrc))
1108 {
1109 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(it->second);
1110 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc,
1111 hVfsIosEncrypted != NIL_RTVFSIOSTREAM
1112 ? hVfsIosEncrypted
1113 : hVfsIos, 0 /*cbBufHint*/);
1114 RTVfsIoStrmRelease(hVfsIosSrc);
1115#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1116 if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
1117 i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
1118#endif
1119 }
1120
1121 RTVfsIoStrmRelease(hVfsIos);
1122 RTVfsFileRelease(hVfsFile);
1123 }
1124
1125 it++;
1126 }
1127
1128 /* Cleanup in case of error. */
1129 if (RT_FAILURE(vrc))
1130 {
1131 int vrc2 = RTDirRemoveRecursive(pszPath, RTDIRRMREC_F_CONTENT_AND_DIR);
1132 if (RT_FAILURE(vrc2))
1133 LogRel(("Cleaning up NVRAM store '%s' failed with %Rrc (after creation failed with %Rrc)\n", pszPath, vrc2, vrc));
1134 }
1135 }
1136 else
1137 LogRel(("NVRAM store '%s' directory creation failed: %Rrc\n", pszPath, vrc));
1138
1139 return vrc;
1140}
1141
1142
1143int NvramStore::i_retainCryptoIf(PCVBOXCRYPTOIF *ppCryptoIf)
1144{
1145#ifdef VBOX_COM_INPROC
1146 return m->pParent->i_retainCryptoIf(ppCryptoIf);
1147#else
1148 HRESULT hrc = m->pParent->i_getVirtualBox()->i_retainCryptoIf(ppCryptoIf);
1149 if (SUCCEEDED(hrc))
1150 return VINF_SUCCESS;
1151
1152 return VERR_COM_IPRT_ERROR;
1153#endif
1154}
1155
1156
1157int NvramStore::i_releaseCryptoIf(PCVBOXCRYPTOIF pCryptoIf)
1158{
1159#ifdef VBOX_COM_INPROC
1160 return m->pParent->i_releaseCryptoIf(pCryptoIf);
1161#else
1162 HRESULT hrc = m->pParent->i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
1163 if (SUCCEEDED(hrc))
1164 return VINF_SUCCESS;
1165
1166 return VERR_COM_IPRT_ERROR;
1167#endif
1168}
1169
1170
1171/**
1172 * Saves the NVRAM store.
1173 *
1174 * @returns IPRT status code.
1175 */
1176int NvramStore::i_saveStore(void)
1177{
1178 int vrc = VINF_SUCCESS;
1179
1180 Utf8Str strPath;
1181 NvramStore::getNonVolatileStorageFile(strPath);
1182
1183 /*
1184 * Only store the NVRAM content if the path is not empty, if it is
1185 * this means the VM was just created and the store was not saved yet,
1186 * see @bugref{10191}.
1187 */
1188 if (strPath.isNotEmpty())
1189 {
1190 /*
1191 * Skip creating the tar archive if only the UEFI NVRAM content is available in order
1192 * to maintain backwards compatibility. As soon as there is more than one entry or
1193 * it doesn't belong to the UEFI the tar archive will be created.
1194 */
1195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1196 if ( m->mapNvram.size() == 1
1197 && m->mapNvram.begin()->first == "efi/nvram")
1198 {
1199 RTVFSFILE hVfsFileNvram = m->mapNvram.begin()->second;
1200
1201 vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1202 AssertLogRelRC(vrc);
1203
1204 RTVFSIOSTREAM hVfsIosDst;
1205 vrc = RTVfsIoStrmOpenNormal(strPath.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
1206 &hVfsIosDst);
1207 if (RT_SUCCESS(vrc))
1208 {
1209 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsFileNvram);
1210 Assert(hVfsIosSrc != NIL_RTVFSIOSTREAM);
1211
1212 RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
1213
1214#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1215 PCVBOXCRYPTOIF pCryptoIf = NULL;
1216 SecretKey *pKey = NULL;
1217
1218 if ( m->bd->strKeyId.isNotEmpty()
1219 && m->bd->strKeyStore.isNotEmpty())
1220 vrc = i_setupEncryptionOrDecryption(hVfsIosDst, true /*fEncrypt*/,
1221 &pCryptoIf, &pKey, &hVfsIosEncrypted);
1222#endif
1223 if (RT_SUCCESS(vrc))
1224 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc,
1225 hVfsIosEncrypted != NIL_RTVFSIOSTREAM
1226 ? hVfsIosEncrypted
1227 : hVfsIosDst
1228 , 0 /*cbBufHint*/);
1229#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1230 if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
1231 i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
1232#endif
1233
1234 RTVfsIoStrmRelease(hVfsIosSrc);
1235 RTVfsIoStrmRelease(hVfsIosDst);
1236 }
1237 }
1238 else if (m->mapNvram.size())
1239 {
1240 /* Check whether the NVRAM content is supposed to be saved under a directory. */
1241#ifndef VBOX_COM_INPROC
1242 Machine * const pMachine = m->pParent;
1243#else
1244 const ComPtr<IMachine> &pMachine = m->pParent->i_machine();
1245#endif
1246
1247 Bstr bstrName("VBoxInternal2/SaveNvramContentAsDirectory");
1248 Bstr bstrValue;
1249 HRESULT hrc = pMachine->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
1250 if (FAILED(hrc))
1251 throw hrc;
1252
1253 bool fSaveAsDir = bstrValue == "1";
1254
1255 if (fSaveAsDir)
1256 vrc = i_saveStoreAsDir(strPath.c_str());
1257 else
1258 vrc = i_saveStoreAsTar(strPath.c_str());
1259 }
1260 /* else: No NVRAM content to store so we are done here. */
1261 }
1262
1263 return vrc;
1264}
1265
1266
1267#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1268HRESULT NvramStore::i_updateEncryptionSettings(const com::Utf8Str &strKeyId,
1269 const com::Utf8Str &strKeyStore)
1270{
1271 /* sanity */
1272 AutoCaller autoCaller(this);
1273 AssertComRCReturnRC(autoCaller.hrc());
1274
1275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 m->bd.backup();
1278 m->bd->strKeyId = strKeyId;
1279 m->bd->strKeyStore = strKeyStore;
1280
1281 /* clear all passwords because they are invalid now */
1282 m->mpKeyStore->deleteAllSecretKeys(false, true);
1283
1284 alock.release();
1285 AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
1286#ifndef VBOX_COM_INPROC
1287 m->pParent->i_setModified(Machine::IsModified_NvramStore);
1288#endif
1289 return S_OK;
1290}
1291
1292
1293HRESULT NvramStore::i_getEncryptionSettings(com::Utf8Str &strKeyId,
1294 com::Utf8Str &strKeyStore)
1295{
1296 AutoCaller autoCaller(this);
1297 AssertComRCReturnRC(autoCaller.hrc());
1298
1299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1300
1301 strKeyId = m->bd->strKeyId;
1302 strKeyStore = m->bd->strKeyStore;
1303
1304 return S_OK;
1305}
1306
1307
1308int NvramStore::i_addPassword(const Utf8Str &strKeyId, const Utf8Str &strPassword)
1309{
1310 AutoCaller autoCaller(this);
1311 AssertComRCReturn(autoCaller.hrc(), VERR_INVALID_STATE);
1312
1313 /* keep only required password */
1314 if (strKeyId != m->bd->strKeyId)
1315 return VINF_SUCCESS;
1316
1317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1318 return m->mpKeyStore->addSecretKey(strKeyId, (const uint8_t *)strPassword.c_str(), strPassword.length() + 1);
1319}
1320
1321
1322int NvramStore::i_removePassword(const Utf8Str &strKeyId)
1323{
1324 AutoCaller autoCaller(this);
1325 AssertComRCReturn(autoCaller.hrc(), VERR_INVALID_STATE);
1326
1327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1328 return m->mpKeyStore->deleteSecretKey(strKeyId);
1329}
1330
1331
1332int NvramStore::i_removeAllPasswords()
1333{
1334 AutoCaller autoCaller(this);
1335 AssertComRCReturn(autoCaller.hrc(), VERR_INVALID_STATE);
1336
1337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1338 m->mpKeyStore->deleteAllSecretKeys(false, true);
1339 return VINF_SUCCESS;
1340}
1341#endif
1342
1343
1344#ifndef VBOX_COM_INPROC
1345
1346HRESULT NvramStore::i_retainUefiVarStore(PRTVFS phVfs, bool fReadonly)
1347{
1348 /* the machine needs to be mutable unless fReadonly is set */
1349 AutoMutableStateDependency adep(fReadonly ? NULL : m->pParent);
1350 if (FAILED(adep.hrc())) return adep.hrc();
1351
1352 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 HRESULT hrc = S_OK;
1355 NvramStoreIter it = m->mapNvram.find("efi/nvram");
1356 if (it != m->mapNvram.end())
1357 {
1358 RTVFSFILE hVfsFileNvram = it->second;
1359 RTVFS hVfsEfiVarStore;
1360 uint32_t fMntFlags = fReadonly ? RTVFSMNT_F_READ_ONLY : 0;
1361
1362 int vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, fMntFlags, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
1363 NULL /*pErrInfo*/);
1364 if (RT_SUCCESS(vrc))
1365 {
1366 *phVfs = hVfsEfiVarStore;
1367 if (!fReadonly)
1368 m->pParent->i_setModified(Machine::IsModified_NvramStore);
1369 }
1370 else
1371 hrc = setError(E_FAIL, tr("Opening the UEFI variable store failed (%Rrc)"), vrc);
1372 }
1373 else
1374 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine"));
1375
1376 return hrc;
1377}
1378
1379
1380HRESULT NvramStore::i_releaseUefiVarStore(RTVFS hVfs)
1381{
1382 RTVfsRelease(hVfs);
1383 return S_OK;
1384}
1385
1386
1387/**
1388 * Loads settings from the given machine node.
1389 * May be called once right after this object creation.
1390 *
1391 * @param data Configuration settings.
1392 *
1393 * @note Locks this object for writing.
1394 */
1395HRESULT NvramStore::i_loadSettings(const settings::NvramSettings &data)
1396{
1397 LogFlowThisFuncEnter();
1398
1399 AutoCaller autoCaller(this);
1400 AssertComRCReturnRC(autoCaller.hrc());
1401
1402 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
1403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1404
1405 m->bd->strNvramPath = data.strNvramPath;
1406#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1407 m->bd->strKeyId = data.strKeyId;
1408 m->bd->strKeyStore = data.strKeyStore;
1409#endif
1410
1411 Utf8Str strTmp(m->bd->strNvramPath);
1412 if (strTmp.isNotEmpty())
1413 m->pParent->i_copyPathRelativeToMachine(strTmp, m->bd->strNvramPath);
1414 if ( m->pParent->i_getFirmwareType() == FirmwareType_BIOS
1415 || m->bd->strNvramPath == m->pParent->i_getDefaultNVRAMFilename())
1416 m->bd->strNvramPath.setNull();
1417
1418 LogFlowThisFuncLeave();
1419 return S_OK;
1420}
1421
1422/**
1423 * Saves settings to the given machine node.
1424 *
1425 * @param data Configuration settings.
1426 *
1427 * @note Locks this object for writing.
1428 */
1429HRESULT NvramStore::i_saveSettings(settings::NvramSettings &data)
1430{
1431 AutoCaller autoCaller(this);
1432 AssertComRCReturnRC(autoCaller.hrc());
1433
1434 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
1435
1436 data.strNvramPath = m->bd->strNvramPath;
1437#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1438 data.strKeyId = m->bd->strKeyId;
1439 data.strKeyStore = m->bd->strKeyStore;
1440#endif
1441
1442 int vrc = i_saveStore();
1443 if (RT_FAILURE(vrc))
1444 return setError(E_FAIL, tr("Failed to save the NVRAM content to disk (%Rrc)"), vrc);
1445
1446 return S_OK;
1447}
1448
1449void NvramStore::i_rollback()
1450{
1451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1452 m->bd.rollback();
1453}
1454
1455void NvramStore::i_commit()
1456{
1457 /* sanity */
1458 AutoCaller autoCaller(this);
1459 AssertReturnVoid(autoCaller.isOk());
1460
1461 /* sanity too */
1462 AutoCaller peerCaller(m->pPeer);
1463 AssertReturnVoid(peerCaller.isOk());
1464
1465 /* lock both for writing since we modify both (mPeer is "master" so locked
1466 * first) */
1467 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
1468
1469 if (m->bd.isBackedUp())
1470 {
1471 m->bd.commit();
1472 if (m->pPeer)
1473 {
1474 /* attach new data to the peer and reshare it */
1475 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
1476 m->pPeer->m->bd.attach(m->bd);
1477 }
1478 }
1479}
1480
1481void NvramStore::i_copyFrom(NvramStore *aThat)
1482{
1483 AssertReturnVoid(aThat != NULL);
1484
1485 /* sanity */
1486 AutoCaller autoCaller(this);
1487 AssertReturnVoid(autoCaller.isOk());
1488
1489 /* sanity too */
1490 AutoCaller thatCaller(aThat);
1491 AssertReturnVoid(thatCaller.isOk());
1492
1493 /* peer is not modified, lock it for reading (aThat is "master" so locked
1494 * first) */
1495 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
1496 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
1497
1498 /* this will back up current data */
1499 m->bd.assignCopy(aThat->m->bd);
1500
1501 // Intentionally "forget" the NVRAM file since it must be unique and set
1502 // to the correct value before the copy of the settings makes sense.
1503 m->bd->strNvramPath.setNull();
1504}
1505
1506HRESULT NvramStore::i_applyDefaults(GuestOSType *aOSType)
1507{
1508 HRESULT hrc = S_OK;
1509
1510 if (aOSType->i_recommendedEFISecureBoot())
1511 {
1512 /* Initialize the UEFI variable store and enroll default keys. */
1513 hrc = initUefiVariableStore(0 /*aSize*/);
1514 if (SUCCEEDED(hrc))
1515 {
1516 ComPtr<IUefiVariableStore> pVarStore;
1517
1518 hrc = getUefiVariableStore(pVarStore);
1519 if (SUCCEEDED(hrc))
1520 {
1521 hrc = pVarStore->EnrollOraclePlatformKey();
1522 if (SUCCEEDED(hrc))
1523 hrc = pVarStore->EnrollDefaultMsSignatures();
1524 }
1525 }
1526 }
1527
1528 return hrc;
1529}
1530
1531void NvramStore::i_updateNonVolatileStorageFile(const Utf8Str &aNonVolatileStorageFile)
1532{
1533 /* sanity */
1534 AutoCaller autoCaller(this);
1535 AssertComRCReturnVoid(autoCaller.hrc());
1536
1537 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 Utf8Str strTmp(aNonVolatileStorageFile);
1541 if (strTmp == m->pParent->i_getDefaultNVRAMFilename())
1542 strTmp.setNull();
1543
1544 if (strTmp == m->bd->strNvramPath)
1545 return;
1546
1547 m->bd.backup();
1548 m->bd->strNvramPath = strTmp;
1549}
1550
1551#else /* VBOX_COM_INPROC */
1552
1553//
1554// private methods
1555//
1556/*static*/
1557DECLCALLBACK(int) NvramStore::i_nvramStoreQuerySize(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
1558 uint64_t *pcb)
1559{
1560 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1561
1562 Utf8Str strKey;
1563 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1564 AssertRCReturn(vrc, vrc);
1565
1566 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1567 NvramStoreIter it = pThis->pNvramStore->m->mapNvram.find(strKey);
1568 if (it != pThis->pNvramStore->m->mapNvram.end())
1569 {
1570 RTVFSFILE hVfsFile = it->second;
1571 return RTVfsFileQuerySize(hVfsFile, pcb);
1572 }
1573
1574 return VERR_NOT_FOUND;
1575}
1576
1577
1578/*static*/
1579DECLCALLBACK(int) NvramStore::i_nvramStoreReadAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
1580 void *pvBuf, size_t cbRead)
1581{
1582 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1583
1584 Utf8Str strKey;
1585 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1586 AssertRCReturn(vrc, vrc);
1587
1588 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1589 NvramStoreIter it = pThis->pNvramStore->m->mapNvram.find(strKey);
1590 if (it != pThis->pNvramStore->m->mapNvram.end())
1591 {
1592 RTVFSFILE hVfsFile = it->second;
1593
1594 vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1595 AssertLogRelRC(vrc);
1596
1597 return RTVfsFileRead(hVfsFile, pvBuf, cbRead, NULL /*pcbRead*/);
1598 }
1599
1600 return VERR_NOT_FOUND;
1601}
1602
1603
1604/*static*/
1605DECLCALLBACK(int) NvramStore::i_nvramStoreWriteAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
1606 const void *pvBuf, size_t cbWrite)
1607{
1608 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1609
1610 Utf8Str strKey;
1611 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1612 AssertRCReturn(vrc, vrc);
1613
1614 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1615
1616 NvramStoreIter it = pThis->pNvramStore->m->mapNvram.find(strKey);
1617 if (it != pThis->pNvramStore->m->mapNvram.end())
1618 {
1619 RTVFSFILE hVfsFile = it->second;
1620
1621 vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1622 AssertLogRelRC(vrc);
1623 vrc = RTVfsFileSetSize(hVfsFile, cbWrite, RTVFSFILE_SIZE_F_NORMAL);
1624 if (RT_SUCCESS(vrc))
1625 vrc = RTVfsFileWrite(hVfsFile, pvBuf, cbWrite, NULL /*pcbWritten*/);
1626 }
1627 else
1628 {
1629 /* Create a new entry. */
1630 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1631 vrc = RTVfsFileFromBuffer(RTFILE_O_READ | RTFILE_O_WRITE, pvBuf, cbWrite, &hVfsFile);
1632 if (RT_SUCCESS(vrc))
1633 {
1634 try
1635 {
1636 pThis->pNvramStore->m->mapNvram[strKey] = hVfsFile;
1637 }
1638 catch (...)
1639 {
1640 AssertLogRelFailed();
1641 RTVfsFileRelease(hVfsFile);
1642 vrc = VERR_UNEXPECTED_EXCEPTION;
1643 }
1644 }
1645 }
1646
1647 return vrc;
1648}
1649
1650
1651/*static*/
1652DECLCALLBACK(int) NvramStore::i_nvramStoreDelete(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath)
1653{
1654 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1655
1656 Utf8Str strKey;
1657 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1658 AssertRCReturn(vrc, vrc);
1659
1660 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1661 NvramStoreIter it = pThis->pNvramStore->m->mapNvram.find(strKey);
1662 if (it != pThis->pNvramStore->m->mapNvram.end())
1663 {
1664 RTVFSFILE hVfsFile = it->second;
1665 pThis->pNvramStore->m->mapNvram.erase(it);
1666 RTVfsFileRelease(hVfsFile);
1667 return VINF_SUCCESS;
1668 }
1669
1670 return VERR_NOT_FOUND;
1671}
1672
1673
1674/*static*/
1675DECLCALLBACK(int) NvramStore::i_SsmSaveExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1676{
1677 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1678 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1679 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1680
1681 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1682
1683 size_t cEntries = pThis->pNvramStore->m->mapNvram.size();
1684 AssertReturn(cEntries < 32, VERR_OUT_OF_RANGE); /* Some sanity checking. */
1685 pHlp->pfnSSMPutU32(pSSM, (uint32_t)cEntries);
1686
1687 void *pvData = NULL;
1688 size_t cbDataMax = 0;
1689 int vrc = i_SsmSaveExecInner(pThis, pHlp, pSSM, &pvData, &cbDataMax);
1690 if (pvData)
1691 RTMemFree(pvData);
1692 AssertRCReturn(vrc, vrc);
1693
1694 pThis->pNvramStore->m->fSsmSaved = true;
1695 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
1696}
1697
1698
1699/*static*/
1700int NvramStore::i_SsmSaveExecInner(PDRVMAINNVRAMSTORE pThis, PCPDMDRVHLPR3 pHlp, PSSMHANDLE pSSM,
1701 void **ppvData, size_t *pcbDataMax) RT_NOEXCEPT
1702{
1703 for (NvramStoreIter it = pThis->pNvramStore->m->mapNvram.begin(); it != pThis->pNvramStore->m->mapNvram.end(); ++it)
1704 {
1705 RTVFSFILE hVfsFile = it->second;
1706
1707 uint64_t cbFile;
1708 int vrc = RTVfsFileQuerySize(hVfsFile, &cbFile);
1709 AssertRCReturn(vrc, vrc);
1710 AssertReturn(cbFile < _1M, VERR_OUT_OF_RANGE);
1711
1712 if (*pcbDataMax < cbFile)
1713 {
1714 void *pvNew = RTMemRealloc(*ppvData, cbFile);
1715 AssertPtrReturn(pvNew, VERR_NO_MEMORY);
1716 *ppvData = pvNew;
1717 *pcbDataMax = cbFile;
1718 }
1719
1720 vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, *ppvData, cbFile, NULL /*pcbRead*/);
1721 AssertRCReturn(vrc, vrc);
1722
1723 pHlp->pfnSSMPutStrZ(pSSM, it->first.c_str());
1724 pHlp->pfnSSMPutU64(pSSM, cbFile);
1725 pHlp->pfnSSMPutMem(pSSM, *ppvData, cbFile);
1726 }
1727 return VINF_SUCCESS;
1728}
1729
1730
1731/*static*/
1732DECLCALLBACK(int) NvramStore::i_SsmLoadExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1733{
1734 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1735 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1736 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1737
1738 AssertMsgReturn(uVersion >= NVRAM_STORE_SAVED_STATE_VERSION, ("%d\n", uVersion),
1739 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1740
1741 if (uPass == SSM_PASS_FINAL)
1742 {
1743 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1744
1745 /* Clear any content first. */
1746 NvramStoreIter it = pThis->pNvramStore->m->mapNvram.begin();
1747 while (it != pThis->pNvramStore->m->mapNvram.end())
1748 {
1749 RTVfsFileRelease(it->second);
1750 it++;
1751 }
1752
1753 pThis->pNvramStore->m->mapNvram.clear();
1754
1755 uint32_t cEntries = 0;
1756 int vrc = pHlp->pfnSSMGetU32(pSSM, &cEntries);
1757 AssertRCReturn(vrc, vrc);
1758 AssertReturn(cEntries < 32, VERR_OUT_OF_RANGE);
1759
1760 void *pvData = NULL;
1761 size_t cbDataMax = 0;
1762 vrc = i_SsmLoadExecInner(pThis, pHlp, pSSM, cEntries, &pvData, &cbDataMax);
1763 if (pvData)
1764 RTMemFree(pvData);
1765 AssertRCReturn(vrc, vrc);
1766
1767 /* The marker. */
1768 uint32_t u32;
1769 vrc = pHlp->pfnSSMGetU32(pSSM, &u32);
1770 AssertRCReturn(vrc, vrc);
1771 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
1772 }
1773
1774 return VINF_SUCCESS;
1775}
1776
1777
1778/*static*/
1779int NvramStore::i_SsmLoadExecInner(PDRVMAINNVRAMSTORE pThis, PCPDMDRVHLPR3 pHlp, PSSMHANDLE pSSM,
1780 uint32_t cEntries, void **ppvData, size_t *pcbDataMax) RT_NOEXCEPT
1781{
1782 while (cEntries-- > 0)
1783 {
1784 char szId[_1K]; /* Lazy developer */
1785 int vrc = pHlp->pfnSSMGetStrZ(pSSM, &szId[0], sizeof(szId));
1786 AssertRCReturn(vrc, vrc);
1787
1788 uint64_t cbFile = 0;
1789 vrc = pHlp->pfnSSMGetU64(pSSM, &cbFile);
1790 AssertRCReturn(vrc, vrc);
1791 AssertReturn(cbFile < _1M, VERR_OUT_OF_RANGE);
1792
1793 if (*pcbDataMax < cbFile)
1794 {
1795 void *pvNew = RTMemRealloc(*ppvData, cbFile);
1796 AssertPtrReturn(pvNew, VERR_NO_MEMORY);
1797 *ppvData = pvNew;
1798 *pcbDataMax = cbFile;
1799 }
1800
1801 vrc = pHlp->pfnSSMGetMem(pSSM, *ppvData, cbFile);
1802 AssertRCReturn(vrc, vrc);
1803
1804 RTVFSFILE hVfsFile;
1805 vrc = RTVfsFileFromBuffer(RTFILE_O_READWRITE, *ppvData, cbFile, &hVfsFile);
1806 AssertRCReturn(vrc, vrc);
1807
1808 try
1809 {
1810 pThis->pNvramStore->m->mapNvram[Utf8Str(szId)] = hVfsFile;
1811 }
1812 catch (...)
1813 {
1814 AssertLogRelFailed();
1815 RTVfsFileRelease(hVfsFile);
1816 return VERR_UNEXPECTED_EXCEPTION;
1817 }
1818 }
1819
1820 return VINF_SUCCESS;
1821}
1822
1823
1824/**
1825 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1826 */
1827DECLCALLBACK(void *) NvramStore::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1828{
1829 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1830 PDRVMAINNVRAMSTORE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1831
1832 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1833 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVFSCONNECTOR, &pDrv->IVfs);
1834 return NULL;
1835}
1836
1837
1838/**
1839 * Destruct a NVRAM store driver instance.
1840 *
1841 * @returns VBox status code.
1842 * @param pDrvIns The driver instance data.
1843 */
1844DECLCALLBACK(void) NvramStore::i_drvDestruct(PPDMDRVINS pDrvIns)
1845{
1846 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1847 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1848 LogFlow(("NvramStore::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
1849
1850 if (pThis->pNvramStore)
1851 {
1852 uint32_t cRefs = ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1853 if ( !cRefs
1854 && !pThis->pNvramStore->m->fSsmSaved)
1855 {
1856 try
1857 {
1858 int vrc = pThis->pNvramStore->i_saveStore();
1859 AssertLogRelRC(vrc); /** @todo Disk full error? */
1860 }
1861 catch (...)
1862 {
1863 AssertLogRelFailed();
1864 }
1865 }
1866 }
1867}
1868
1869
1870/**
1871 * Construct a NVRAM store driver instance.
1872 *
1873 * @copydoc FNPDMDRVCONSTRUCT
1874 */
1875DECLCALLBACK(int) NvramStore::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1876{
1877 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1878 RT_NOREF(fFlags, pCfg);
1879 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1880 LogFlow(("NvramStore::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
1881
1882 /*
1883 * Validate configuration.
1884 */
1885 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", "");
1886 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1887 ("Configuration error: Not possible to attach anything to this driver!\n"),
1888 VERR_PDM_DRVINS_NO_ATTACH);
1889
1890 /*
1891 * IBase.
1892 */
1893 pDrvIns->IBase.pfnQueryInterface = NvramStore::i_drvQueryInterface;
1894
1895 pThis->IVfs.pfnQuerySize = NvramStore::i_nvramStoreQuerySize;
1896 pThis->IVfs.pfnReadAll = NvramStore::i_nvramStoreReadAll;
1897 pThis->IVfs.pfnWriteAll = NvramStore::i_nvramStoreWriteAll;
1898 pThis->IVfs.pfnDelete = NvramStore::i_nvramStoreDelete;
1899
1900 /*
1901 * Get the NVRAM store object pointer.
1902 */
1903 com::Guid uuid(COM_IIDOF(INvramStore));
1904 pThis->pNvramStore = (NvramStore *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
1905 if (!pThis->pNvramStore)
1906 {
1907 AssertMsgFailed(("Configuration error: No/bad NVRAM store object!\n"));
1908 return VERR_NOT_FOUND;
1909 }
1910
1911 /*
1912 * Only the first instance will register the SSM handlers and will do the work on behalf
1913 * of all other NVRAM store driver instances when it comes to SSM.
1914 */
1915 if (pDrvIns->iInstance == 0)
1916 {
1917 int vrc = PDMDrvHlpSSMRegister(pDrvIns, NVRAM_STORE_SAVED_STATE_VERSION, 0 /*cbGuess*/,
1918 NvramStore::i_SsmSaveExec, NvramStore::i_SsmLoadExec);
1919 if (RT_FAILURE(vrc))
1920 return PDMDrvHlpVMSetError(pDrvIns, vrc, RT_SRC_POS,
1921 N_("Failed to register the saved state unit for the NVRAM store"));
1922 }
1923
1924 uint32_t cRefs = ASMAtomicIncU32(&pThis->pNvramStore->m->cRefs);
1925 if (cRefs == 1)
1926 {
1927 int vrc;
1928 try
1929 {
1930 vrc = pThis->pNvramStore->i_loadStore(pThis->pNvramStore->m->bd->strNvramPath.c_str());
1931 }
1932 catch (...)
1933 {
1934 vrc = VERR_UNEXPECTED_EXCEPTION;
1935 }
1936 if (RT_FAILURE(vrc))
1937 {
1938 ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1939 return PDMDrvHlpVMSetError(pDrvIns, vrc, RT_SRC_POS,
1940 N_("Failed to load the NVRAM store from the file"));
1941 }
1942 }
1943
1944 return VINF_SUCCESS;
1945}
1946
1947
1948/**
1949 * NVRAM store driver registration record.
1950 */
1951const PDMDRVREG NvramStore::DrvReg =
1952{
1953 /* u32Version */
1954 PDM_DRVREG_VERSION,
1955 /* szName */
1956 "NvramStore",
1957 /* szRCMod */
1958 "",
1959 /* szR0Mod */
1960 "",
1961 /* pszDescription */
1962 "Main NVRAM store driver (Main as in the API).",
1963 /* fFlags */
1964 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1965 /* fClass. */
1966 PDM_DRVREG_CLASS_STATUS,
1967 /* cMaxInstances */
1968 ~0U,
1969 /* cbInstance */
1970 sizeof(DRVMAINNVRAMSTORE),
1971 /* pfnConstruct */
1972 NvramStore::i_drvConstruct,
1973 /* pfnDestruct */
1974 NvramStore::i_drvDestruct,
1975 /* pfnRelocate */
1976 NULL,
1977 /* pfnIOCtl */
1978 NULL,
1979 /* pfnPowerOn */
1980 NULL,
1981 /* pfnReset */
1982 NULL,
1983 /* pfnSuspend */
1984 NULL,
1985 /* pfnResume */
1986 NULL,
1987 /* pfnAttach */
1988 NULL,
1989 /* pfnDetach */
1990 NULL,
1991 /* pfnPowerOff */
1992 NULL,
1993 /* pfnSoftReset */
1994 NULL,
1995 /* u32EndVersion */
1996 PDM_DRVREG_VERSION
1997};
1998
1999#endif /* VBOX_COM_INPROC */
2000
2001/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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