VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 3 years ago

scm copyright and license note update

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

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