VirtualBox

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

Last change on this file since 105627 was 105627, checked in by vboxsync, 6 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.

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

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