VirtualBox

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

Last change on this file since 105392 was 105383, checked in by vboxsync, 10 months ago

Main/NvramStore: Don't make the NVRAM map part of the backupable data as these get shared between snapshots and the copy is only shallow so references to RTVFSFILE entries are shared. Instead when a new NvramStore object is created and the path is set it will be loaded again when required. Fixes restoring snapshots for VMs which have NVRAM data. [build fix]

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