VirtualBox

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

Last change on this file since 101698 was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use