VirtualBox

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

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

Runtime/common/efi,Main: Fix creating NVRAM files for ARM VMs, the previous ones where incompatible to what the UEFI firmware expected causing a hang during boot under certain circumstances, bugref:10872

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