VirtualBox

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

Last change on this file since 105654 was 105628, checked in by vboxsync, 9 months ago

Main/NvramStore: Don't (debug) assert when getting the UEFI NVRAM store for non-UEFI VMs; some IPRT APIs being used don't like empty strings (paths) and assert in such cases. Be a bit more information wrt returning errors via the API about what's going on [build fix].

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