VirtualBox

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

Last change on this file since 99424 was 99424, checked in by vboxsync, 23 months ago

Main/NvramStore: Don't return VINF_SUCCESS for too large files. Get more error info from RTEfiVarStoreOpenAsVfs. bugref:10098

  • 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 99424 2023-04-17 15:20:42Z 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
615#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
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 * @returns nothing.
678 * @param hVfsIos Handle to the I/O stream previously created.
679 * @param pCryptoIf Pointer to the cryptographic interface being released.
680 * @param pKey Pointer to the key buffer being released.
681 */
682void NvramStore::i_releaseEncryptionOrDecryptionResources(RTVFSIOSTREAM hVfsIos, PCVBOXCRYPTOIF pCryptoIf,
683 SecretKey *pKey)
684{
685 Assert(hVfsIos != NIL_RTVFSIOSTREAM);
686 AssertPtr(pCryptoIf);
687 AssertPtr(pKey);
688
689 i_releaseCryptoIf(pCryptoIf);
690 pKey->release();
691 RTVfsIoStrmRelease(hVfsIos);
692}
693#endif
694
695
696/**
697 * Loads the NVRAM store.
698 *
699 * @returns IPRT status code.
700 */
701int NvramStore::i_loadStore(const char *pszPath)
702{
703 uint64_t cbStore = 0;
704 int vrc = RTFileQuerySizeByPath(pszPath, &cbStore);
705 if (RT_SUCCESS(vrc))
706 {
707 if (cbStore <= _1M) /* Arbitrary limit to fend off bogus files because the file will be read into memory completely. */
708 {
709 /*
710 * Old NVRAM files just consist of the EFI variable store whereas starting
711 * with VirtualBox 7.0 and the introduction of the TPM the need to handle multiple
712 * independent NVRAM files came up. For those scenarios all NVRAM states are collected
713 * in a tar archive.
714 *
715 * Here we detect whether the file is the new tar archive format or whether it is just
716 * the plain EFI variable store file.
717 */
718 RTVFSIOSTREAM hVfsIosNvram;
719 vrc = RTVfsIoStrmOpenNormal(pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE,
720 &hVfsIosNvram);
721 if (RT_SUCCESS(vrc))
722 {
723 RTVFSIOSTREAM hVfsIosDecrypted = NIL_RTVFSIOSTREAM;
724
725#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
726 PCVBOXCRYPTOIF pCryptoIf = NULL;
727 SecretKey *pKey = NULL;
728
729 if ( m->bd->strKeyId.isNotEmpty()
730 && m->bd->strKeyStore.isNotEmpty())
731 vrc = i_setupEncryptionOrDecryption(hVfsIosNvram, false /*fEncrypt*/,
732 &pCryptoIf, &pKey, &hVfsIosDecrypted);
733#endif
734 if (RT_SUCCESS(vrc))
735 {
736 /* Read the content. */
737 RTVFSFILE hVfsFileNvram;
738 vrc = RTVfsMemorizeIoStreamAsFile( hVfsIosDecrypted != NIL_RTVFSIOSTREAM
739 ? hVfsIosDecrypted
740 : hVfsIosNvram,
741 RTFILE_O_READ, &hVfsFileNvram);
742 if (RT_SUCCESS(vrc))
743 {
744 if (RT_SUCCESS(vrc))
745 {
746 /* Try to parse it as an EFI variable store. */
747 RTERRINFOSTATIC ErrInfo;
748 RTVFS hVfsEfiVarStore;
749 vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, RTVFSMNT_F_READ_ONLY, 0 /*fVarStoreFlags*/,
750 &hVfsEfiVarStore, RTErrInfoInitStatic(&ErrInfo));
751 if (RT_SUCCESS(vrc))
752 {
753 vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
754 AssertRC(vrc);
755
756 RTVfsFileRetain(hVfsFileNvram); /* Retain a new reference for the map. */
757 m->bd->mapNvram[Utf8Str("efi/nvram")] = hVfsFileNvram;
758
759 RTVfsRelease(hVfsEfiVarStore);
760 }
761 else if (vrc == VERR_VFS_UNKNOWN_FORMAT)
762 {
763 /* Check for the new style tar archive. */
764 vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
765 AssertRC(vrc);
766
767 RTVFSIOSTREAM hVfsIosTar = RTVfsFileToIoStream(hVfsFileNvram);
768 Assert(hVfsIosTar != NIL_RTVFSIOSTREAM);
769
770 RTVFSFSSTREAM hVfsFssTar;
771 vrc = RTZipTarFsStreamFromIoStream(hVfsIosTar, 0 /*fFlags*/, &hVfsFssTar);
772 RTVfsIoStrmRelease(hVfsIosTar);
773 if (RT_SUCCESS(vrc))
774 {
775 vrc = i_loadStoreFromTar(hVfsFssTar);
776 RTVfsFsStrmRelease(hVfsFssTar);
777 }
778 else
779 LogRel(("The given NVRAM file is neither a raw UEFI variable store nor a tar archive (opening failed with %Rrc)\n", vrc));
780 }
781 else
782 LogRel(("Opening the UEFI variable store '%s' failed with %Rrc%RTeim\n", pszPath, vrc, &ErrInfo.Core));
783
784 RTVfsFileRelease(hVfsFileNvram);
785 }
786 else
787 LogRel(("Failed to memorize NVRAM store '%s' with %Rrc\n", pszPath, vrc));
788 }
789 }
790
791#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
792 if (hVfsIosDecrypted != NIL_RTVFSIOSTREAM)
793 i_releaseEncryptionOrDecryptionResources(hVfsIosDecrypted, pCryptoIf, pKey);
794#endif
795
796 RTVfsIoStrmRelease(hVfsIosNvram);
797 }
798 else
799 LogRelMax(10, ("NVRAM store '%s' couldn't be opened with %Rrc\n", pszPath, vrc));
800 }
801 else
802 {
803 LogRelMax(10, ("NVRAM store '%s' exceeds limit of %u bytes, actual size is %u\n",
804 pszPath, _1M, cbStore));
805 vrc = VERR_OUT_OF_RANGE;
806 }
807 }
808 else if (vrc == VERR_FILE_NOT_FOUND) /* Valid for the first run where no NVRAM file is there. */
809 vrc = VINF_SUCCESS;
810
811 return vrc;
812}
813
814
815/**
816 * Saves the NVRAM store as a tar archive.
817 */
818int NvramStore::i_saveStoreAsTar(const char *pszPath)
819{
820 uint32_t offError = 0;
821 RTERRINFOSTATIC ErrInfo;
822 RTVFSIOSTREAM hVfsIos;
823
824 int vrc = RTVfsChainOpenIoStream(pszPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
825 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
826 if (RT_SUCCESS(vrc))
827 {
828 RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
829
830#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
831 PCVBOXCRYPTOIF pCryptoIf = NULL;
832 SecretKey *pKey = NULL;
833
834 if ( m->bd->strKeyId.isNotEmpty()
835 && m->bd->strKeyStore.isNotEmpty())
836 vrc = i_setupEncryptionOrDecryption(hVfsIos, true /*fEncrypt*/,
837 &pCryptoIf, &pKey, &hVfsIosEncrypted);
838#endif
839
840 if (RT_SUCCESS(vrc))
841 {
842 RTVFSFSSTREAM hVfsFss;
843 vrc = RTZipTarFsStreamToIoStream( hVfsIosEncrypted != NIL_RTVFSIOSTREAM
844 ? hVfsIosEncrypted
845 : hVfsIos,
846 RTZIPTARFORMAT_GNU, 0 /*fFlags*/, &hVfsFss);
847 if (RT_SUCCESS(vrc))
848 {
849 NvramStoreIter it = m->bd->mapNvram.begin();
850
851 while (it != m->bd->mapNvram.end())
852 {
853 RTVFSFILE hVfsFile = it->second;
854
855 vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
856 AssertRC(vrc);
857
858 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile);
859 vrc = RTVfsFsStrmAdd(hVfsFss, it->first.c_str(), hVfsObj, 0 /*fFlags*/);
860 RTVfsObjRelease(hVfsObj);
861 if (RT_FAILURE(vrc))
862 break;
863
864 it++;
865 }
866
867 RTVfsFsStrmRelease(hVfsFss);
868 }
869
870#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
871 if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
872 i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
873#endif
874 }
875
876 RTVfsIoStrmRelease(hVfsIos);
877 }
878
879 return vrc;
880}
881
882
883int NvramStore::i_retainCryptoIf(PCVBOXCRYPTOIF *ppCryptoIf)
884{
885#ifdef VBOX_COM_INPROC
886 return m->pParent->i_retainCryptoIf(ppCryptoIf);
887#else
888 HRESULT hrc = m->pParent->i_getVirtualBox()->i_retainCryptoIf(ppCryptoIf);
889 if (SUCCEEDED(hrc))
890 return VINF_SUCCESS;
891
892 return VERR_COM_IPRT_ERROR;
893#endif
894}
895
896
897int NvramStore::i_releaseCryptoIf(PCVBOXCRYPTOIF pCryptoIf)
898{
899#ifdef VBOX_COM_INPROC
900 return m->pParent->i_releaseCryptoIf(pCryptoIf);
901#else
902 HRESULT hrc = m->pParent->i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
903 if (SUCCEEDED(hrc))
904 return VINF_SUCCESS;
905
906 return VERR_COM_IPRT_ERROR;
907#endif
908}
909
910
911/**
912 * Saves the NVRAM store.
913 *
914 * @returns IPRT status code.
915 */
916int NvramStore::i_saveStore(void)
917{
918 int vrc = VINF_SUCCESS;
919
920 Utf8Str strTmp;
921 NvramStore::getNonVolatileStorageFile(strTmp);
922
923 /*
924 * Only store the NVRAM content if the path is not empty, if it is
925 * this means the VM was just created and the store was not saved yet,
926 * see @bugref{10191}.
927 */
928 if (strTmp.isNotEmpty())
929 {
930 /*
931 * Skip creating the tar archive if only the UEFI NVRAM content is available in order
932 * to maintain backwards compatibility. As soon as there is more than one entry or
933 * it doesn't belong to the UEFI the tar archive will be created.
934 */
935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
936 if ( m->bd->mapNvram.size() == 1
937 && m->bd->mapNvram.begin()->first == "efi/nvram")
938 {
939 RTVFSFILE hVfsFileNvram = m->bd->mapNvram.begin()->second;
940
941 vrc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
942 AssertLogRelRC(vrc);
943
944 RTVFSIOSTREAM hVfsIosDst;
945 vrc = RTVfsIoStrmOpenNormal(strTmp.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
946 &hVfsIosDst);
947 if (RT_SUCCESS(vrc))
948 {
949 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsFileNvram);
950 Assert(hVfsIosSrc != NIL_RTVFSIOSTREAM);
951
952 RTVFSIOSTREAM hVfsIosEncrypted = NIL_RTVFSIOSTREAM;
953
954#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
955 PCVBOXCRYPTOIF pCryptoIf = NULL;
956 SecretKey *pKey = NULL;
957
958 if ( m->bd->strKeyId.isNotEmpty()
959 && m->bd->strKeyStore.isNotEmpty())
960 vrc = i_setupEncryptionOrDecryption(hVfsIosDst, true /*fEncrypt*/,
961 &pCryptoIf, &pKey, &hVfsIosEncrypted);
962#endif
963
964 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc,
965 hVfsIosEncrypted != NIL_RTVFSIOSTREAM
966 ? hVfsIosEncrypted
967 : hVfsIosDst
968 , 0 /*cbBufHint*/);
969
970#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
971 if (hVfsIosEncrypted != NIL_RTVFSIOSTREAM)
972 i_releaseEncryptionOrDecryptionResources(hVfsIosEncrypted, pCryptoIf, pKey);
973#endif
974
975 RTVfsIoStrmRelease(hVfsIosSrc);
976 RTVfsIoStrmRelease(hVfsIosDst);
977 }
978 }
979 else if (m->bd->mapNvram.size())
980 vrc = i_saveStoreAsTar(strTmp.c_str());
981 /* else: No NVRAM content to store so we are done here. */
982 }
983
984 return vrc;
985}
986
987
988#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
989HRESULT NvramStore::i_updateEncryptionSettings(const com::Utf8Str &strKeyId,
990 const com::Utf8Str &strKeyStore)
991{
992 /* sanity */
993 AutoCaller autoCaller(this);
994 AssertComRCReturnRC(autoCaller.hrc());
995
996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 m->bd.backup();
999 m->bd->strKeyId = strKeyId;
1000 m->bd->strKeyStore = strKeyStore;
1001
1002 /* clear all passwords because they are invalid now */
1003 m->mpKeyStore->deleteAllSecretKeys(false, true);
1004
1005 alock.release();
1006 AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
1007#ifndef VBOX_COM_INPROC
1008 m->pParent->i_setModified(Machine::IsModified_NvramStore);
1009#endif
1010 return S_OK;
1011}
1012
1013
1014HRESULT NvramStore::i_getEncryptionSettings(com::Utf8Str &strKeyId,
1015 com::Utf8Str &strKeyStore)
1016{
1017 AutoCaller autoCaller(this);
1018 AssertComRCReturnRC(autoCaller.hrc());
1019
1020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 strKeyId = m->bd->strKeyId;
1023 strKeyStore = m->bd->strKeyStore;
1024
1025 return S_OK;
1026}
1027
1028
1029int NvramStore::i_addPassword(const Utf8Str &strKeyId, const Utf8Str &strPassword)
1030{
1031 AutoCaller autoCaller(this);
1032 AssertComRCReturn(autoCaller.hrc(), VERR_INVALID_STATE);
1033
1034 /* keep only required password */
1035 if (strKeyId != m->bd->strKeyId)
1036 return VINF_SUCCESS;
1037
1038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1039 return m->mpKeyStore->addSecretKey(strKeyId, (const uint8_t *)strPassword.c_str(), strPassword.length() + 1);
1040}
1041
1042
1043int NvramStore::i_removePassword(const Utf8Str &strKeyId)
1044{
1045 AutoCaller autoCaller(this);
1046 AssertComRCReturn(autoCaller.hrc(), VERR_INVALID_STATE);
1047
1048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 return m->mpKeyStore->deleteSecretKey(strKeyId);
1050}
1051
1052
1053int NvramStore::i_removeAllPasswords()
1054{
1055 AutoCaller autoCaller(this);
1056 AssertComRCReturn(autoCaller.hrc(), VERR_INVALID_STATE);
1057
1058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1059 m->mpKeyStore->deleteAllSecretKeys(false, true);
1060 return VINF_SUCCESS;
1061}
1062#endif
1063
1064
1065#ifndef VBOX_COM_INPROC
1066
1067HRESULT NvramStore::i_retainUefiVarStore(PRTVFS phVfs, bool fReadonly)
1068{
1069 /* the machine needs to be mutable */
1070 AutoMutableStateDependency adep(m->pParent);
1071 if (FAILED(adep.hrc())) return adep.hrc();
1072
1073 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
1074
1075 HRESULT hrc = S_OK;
1076 NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
1077 if (it != m->bd->mapNvram.end())
1078 {
1079 RTVFSFILE hVfsFileNvram = it->second;
1080 RTVFS hVfsEfiVarStore;
1081 uint32_t fMntFlags = fReadonly ? RTVFSMNT_F_READ_ONLY : 0;
1082
1083 int vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, fMntFlags, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
1084 NULL /*pErrInfo*/);
1085 if (RT_SUCCESS(vrc))
1086 {
1087 *phVfs = hVfsEfiVarStore;
1088 if (!fReadonly)
1089 m->pParent->i_setModified(Machine::IsModified_NvramStore);
1090 }
1091 else
1092 hrc = setError(E_FAIL, tr("Opening the UEFI variable store failed (%Rrc)."), vrc);
1093 }
1094 else
1095 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
1096
1097 return hrc;
1098}
1099
1100
1101HRESULT NvramStore::i_releaseUefiVarStore(RTVFS hVfs)
1102{
1103 RTVfsRelease(hVfs);
1104 return S_OK;
1105}
1106
1107
1108/**
1109 * Loads settings from the given machine node.
1110 * May be called once right after this object creation.
1111 *
1112 * @param data Configuration settings.
1113 *
1114 * @note Locks this object for writing.
1115 */
1116HRESULT NvramStore::i_loadSettings(const settings::NvramSettings &data)
1117{
1118 AutoCaller autoCaller(this);
1119 AssertComRCReturnRC(autoCaller.hrc());
1120
1121 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 m->bd->strNvramPath = data.strNvramPath;
1125#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1126 m->bd->strKeyId = data.strKeyId;
1127 m->bd->strKeyStore = data.strKeyStore;
1128#endif
1129
1130 Utf8Str strTmp(m->bd->strNvramPath);
1131 if (strTmp.isNotEmpty())
1132 m->pParent->i_copyPathRelativeToMachine(strTmp, m->bd->strNvramPath);
1133 if ( m->pParent->i_getFirmwareType() == FirmwareType_BIOS
1134 || m->bd->strNvramPath == m->pParent->i_getDefaultNVRAMFilename())
1135 m->bd->strNvramPath.setNull();
1136
1137 return S_OK;
1138}
1139
1140/**
1141 * Saves settings to the given machine node.
1142 *
1143 * @param data Configuration settings.
1144 *
1145 * @note Locks this object for writing.
1146 */
1147HRESULT NvramStore::i_saveSettings(settings::NvramSettings &data)
1148{
1149 AutoCaller autoCaller(this);
1150 AssertComRCReturnRC(autoCaller.hrc());
1151
1152 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 data.strNvramPath = m->bd->strNvramPath;
1155#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1156 data.strKeyId = m->bd->strKeyId;
1157 data.strKeyStore = m->bd->strKeyStore;
1158#endif
1159
1160 int vrc = i_saveStore();
1161 if (RT_FAILURE(vrc))
1162 return setError(E_FAIL, tr("Failed to save the NVRAM content to disk (%Rrc)"), vrc);
1163
1164 return S_OK;
1165}
1166
1167void NvramStore::i_rollback()
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170 m->bd.rollback();
1171}
1172
1173void NvramStore::i_commit()
1174{
1175 /* sanity */
1176 AutoCaller autoCaller(this);
1177 AssertReturnVoid(autoCaller.isOk());
1178
1179 /* sanity too */
1180 AutoCaller peerCaller(m->pPeer);
1181 AssertReturnVoid(peerCaller.isOk());
1182
1183 /* lock both for writing since we modify both (mPeer is "master" so locked
1184 * first) */
1185 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
1186
1187 if (m->bd.isBackedUp())
1188 {
1189 m->bd.commit();
1190 if (m->pPeer)
1191 {
1192 /* attach new data to the peer and reshare it */
1193 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
1194 m->pPeer->m->bd.attach(m->bd);
1195 }
1196 }
1197}
1198
1199void NvramStore::i_copyFrom(NvramStore *aThat)
1200{
1201 AssertReturnVoid(aThat != NULL);
1202
1203 /* sanity */
1204 AutoCaller autoCaller(this);
1205 AssertReturnVoid(autoCaller.isOk());
1206
1207 /* sanity too */
1208 AutoCaller thatCaller(aThat);
1209 AssertReturnVoid(thatCaller.isOk());
1210
1211 /* peer is not modified, lock it for reading (aThat is "master" so locked
1212 * first) */
1213 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
1214 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
1215
1216 /* this will back up current data */
1217 m->bd.assignCopy(aThat->m->bd);
1218
1219 // Intentionally "forget" the NVRAM file since it must be unique and set
1220 // to the correct value before the copy of the settings makes sense.
1221 m->bd->strNvramPath.setNull();
1222}
1223
1224HRESULT NvramStore::i_applyDefaults(GuestOSType *aOSType)
1225{
1226 HRESULT hrc = S_OK;
1227
1228 if (aOSType->i_recommendedEFISecureBoot())
1229 {
1230 /* Initialize the UEFI variable store and enroll default keys. */
1231 hrc = initUefiVariableStore(0 /*aSize*/);
1232 if (SUCCEEDED(hrc))
1233 {
1234 ComPtr<IUefiVariableStore> pVarStore;
1235
1236 hrc = getUefiVariableStore(pVarStore);
1237 if (SUCCEEDED(hrc))
1238 {
1239 hrc = pVarStore->EnrollOraclePlatformKey();
1240 if (SUCCEEDED(hrc))
1241 hrc = pVarStore->EnrollDefaultMsSignatures();
1242 }
1243 }
1244 }
1245
1246 return hrc;
1247}
1248
1249void NvramStore::i_updateNonVolatileStorageFile(const Utf8Str &aNonVolatileStorageFile)
1250{
1251 /* sanity */
1252 AutoCaller autoCaller(this);
1253 AssertComRCReturnVoid(autoCaller.hrc());
1254
1255 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 Utf8Str strTmp(aNonVolatileStorageFile);
1259 if (strTmp == m->pParent->i_getDefaultNVRAMFilename())
1260 strTmp.setNull();
1261
1262 if (strTmp == m->bd->strNvramPath)
1263 return;
1264
1265 m->bd.backup();
1266 m->bd->strNvramPath = strTmp;
1267}
1268
1269#else /* VBOX_COM_INPROC */
1270
1271//
1272// private methods
1273//
1274/*static*/
1275DECLCALLBACK(int) NvramStore::i_nvramStoreQuerySize(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
1276 uint64_t *pcb)
1277{
1278 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1279
1280 Utf8Str strKey;
1281 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1282 AssertRCReturn(vrc, vrc);
1283
1284 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1285 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(strKey);
1286 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
1287 {
1288 RTVFSFILE hVfsFile = it->second;
1289 return RTVfsFileQuerySize(hVfsFile, pcb);
1290 }
1291
1292 return VERR_NOT_FOUND;
1293}
1294
1295
1296/*static*/
1297DECLCALLBACK(int) NvramStore::i_nvramStoreReadAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
1298 void *pvBuf, size_t cbRead)
1299{
1300 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1301
1302 Utf8Str strKey;
1303 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1304 AssertRCReturn(vrc, vrc);
1305
1306 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1307 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(strKey);
1308 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
1309 {
1310 RTVFSFILE hVfsFile = it->second;
1311
1312 vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1313 AssertLogRelRC(vrc);
1314
1315 return RTVfsFileRead(hVfsFile, pvBuf, cbRead, NULL /*pcbRead*/);
1316 }
1317
1318 return VERR_NOT_FOUND;
1319}
1320
1321
1322/*static*/
1323DECLCALLBACK(int) NvramStore::i_nvramStoreWriteAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
1324 const void *pvBuf, size_t cbWrite)
1325{
1326 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1327
1328 Utf8Str strKey;
1329 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1330 AssertRCReturn(vrc, vrc);
1331
1332 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1333
1334 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(strKey);
1335 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
1336 {
1337 RTVFSFILE hVfsFile = it->second;
1338
1339 vrc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
1340 AssertLogRelRC(vrc);
1341 vrc = RTVfsFileSetSize(hVfsFile, cbWrite, RTVFSFILE_SIZE_F_NORMAL);
1342 if (RT_SUCCESS(vrc))
1343 vrc = RTVfsFileWrite(hVfsFile, pvBuf, cbWrite, NULL /*pcbWritten*/);
1344 }
1345 else
1346 {
1347 /* Create a new entry. */
1348 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1349 vrc = RTVfsFileFromBuffer(RTFILE_O_READ | RTFILE_O_WRITE, pvBuf, cbWrite, &hVfsFile);
1350 if (RT_SUCCESS(vrc))
1351 {
1352 try
1353 {
1354 pThis->pNvramStore->m->bd->mapNvram[strKey] = hVfsFile;
1355 }
1356 catch (...)
1357 {
1358 AssertLogRelFailed();
1359 RTVfsFileRelease(hVfsFile);
1360 vrc = VERR_UNEXPECTED_EXCEPTION;
1361 }
1362 }
1363 }
1364
1365 return vrc;
1366}
1367
1368
1369/*static*/
1370DECLCALLBACK(int) NvramStore::i_nvramStoreDelete(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath)
1371{
1372 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
1373
1374 Utf8Str strKey;
1375 int vrc = strKey.printfNoThrow("%s/%s", pszNamespace, pszPath);
1376 AssertRCReturn(vrc, vrc);
1377
1378 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1379 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(strKey);
1380 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
1381 {
1382 RTVFSFILE hVfsFile = it->second;
1383 pThis->pNvramStore->m->bd->mapNvram.erase(it);
1384 RTVfsFileRelease(hVfsFile);
1385 return VINF_SUCCESS;
1386 }
1387
1388 return VERR_NOT_FOUND;
1389}
1390
1391
1392/*static*/
1393DECLCALLBACK(int) NvramStore::i_SsmSaveExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1394{
1395 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1396 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1397 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1398
1399 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1400
1401 size_t cEntries = pThis->pNvramStore->m->bd->mapNvram.size();
1402 AssertReturn(cEntries < 32, VERR_OUT_OF_RANGE); /* Some sanity checking. */
1403 pHlp->pfnSSMPutU32(pSSM, (uint32_t)cEntries);
1404
1405 void *pvData = NULL;
1406 size_t cbDataMax = 0;
1407 int vrc = i_SsmSaveExecInner(pThis, pHlp, pSSM, &pvData, &cbDataMax);
1408 if (pvData)
1409 RTMemFree(pvData);
1410 AssertRCReturn(vrc, vrc);
1411
1412 pThis->pNvramStore->m->fSsmSaved = true;
1413 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
1414}
1415
1416
1417/*static*/
1418int NvramStore::i_SsmSaveExecInner(PDRVMAINNVRAMSTORE pThis, PCPDMDRVHLPR3 pHlp, PSSMHANDLE pSSM,
1419 void **ppvData, size_t *pcbDataMax) RT_NOEXCEPT
1420{
1421 for (NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.begin(); it != pThis->pNvramStore->m->bd->mapNvram.end(); ++it)
1422 {
1423 RTVFSFILE hVfsFile = it->second;
1424
1425 uint64_t cbFile;
1426 int vrc = RTVfsFileQuerySize(hVfsFile, &cbFile);
1427 AssertRCReturn(vrc, vrc);
1428 AssertReturn(cbFile < _1M, VERR_OUT_OF_RANGE);
1429
1430 if (*pcbDataMax < cbFile)
1431 {
1432 void *pvNew = RTMemRealloc(*ppvData, cbFile);
1433 AssertPtrReturn(pvNew, VERR_NO_MEMORY);
1434 *ppvData = pvNew;
1435 *pcbDataMax = cbFile;
1436 }
1437
1438 vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, *ppvData, cbFile, NULL /*pcbRead*/);
1439 AssertRCReturn(vrc, vrc);
1440
1441 pHlp->pfnSSMPutStrZ(pSSM, it->first.c_str());
1442 pHlp->pfnSSMPutU64(pSSM, cbFile);
1443 pHlp->pfnSSMPutMem(pSSM, *ppvData, cbFile);
1444 }
1445 return VINF_SUCCESS;
1446}
1447
1448
1449/*static*/
1450DECLCALLBACK(int) NvramStore::i_SsmLoadExec(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1451{
1452 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1453 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1454 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1455
1456 AssertMsgReturn(uVersion >= NVRAM_STORE_SAVED_STATE_VERSION, ("%d\n", uVersion),
1457 VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1458
1459 if (uPass == SSM_PASS_FINAL)
1460 {
1461 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
1462
1463 /* Clear any content first. */
1464 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.begin();
1465 while (it != pThis->pNvramStore->m->bd->mapNvram.end())
1466 {
1467 RTVfsFileRelease(it->second);
1468 it++;
1469 }
1470
1471 pThis->pNvramStore->m->bd->mapNvram.clear();
1472
1473 uint32_t cEntries = 0;
1474 int vrc = pHlp->pfnSSMGetU32(pSSM, &cEntries);
1475 AssertRCReturn(vrc, vrc);
1476 AssertReturn(cEntries < 32, VERR_OUT_OF_RANGE);
1477
1478 void *pvData = NULL;
1479 size_t cbDataMax = 0;
1480 vrc = i_SsmLoadExecInner(pThis, pHlp, pSSM, cEntries, &pvData, &cbDataMax);
1481 if (pvData)
1482 RTMemFree(pvData);
1483 AssertRCReturn(vrc, vrc);
1484
1485 /* The marker. */
1486 uint32_t u32;
1487 vrc = pHlp->pfnSSMGetU32(pSSM, &u32);
1488 AssertRCReturn(vrc, vrc);
1489 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
1490 }
1491
1492 return VINF_SUCCESS;
1493}
1494
1495
1496/*static*/
1497int NvramStore::i_SsmLoadExecInner(PDRVMAINNVRAMSTORE pThis, PCPDMDRVHLPR3 pHlp, PSSMHANDLE pSSM,
1498 uint32_t cEntries, void **ppvData, size_t *pcbDataMax) RT_NOEXCEPT
1499{
1500 while (cEntries-- > 0)
1501 {
1502 char szId[_1K]; /* Lazy developer */
1503 int vrc = pHlp->pfnSSMGetStrZ(pSSM, &szId[0], sizeof(szId));
1504 AssertRCReturn(vrc, vrc);
1505
1506 uint64_t cbFile = 0;
1507 vrc = pHlp->pfnSSMGetU64(pSSM, &cbFile);
1508 AssertRCReturn(vrc, vrc);
1509 AssertReturn(cbFile < _1M, VERR_OUT_OF_RANGE);
1510
1511 if (*pcbDataMax < cbFile)
1512 {
1513 void *pvNew = RTMemRealloc(*ppvData, cbFile);
1514 AssertPtrReturn(pvNew, VERR_NO_MEMORY);
1515 *ppvData = pvNew;
1516 *pcbDataMax = cbFile;
1517 }
1518
1519 vrc = pHlp->pfnSSMGetMem(pSSM, *ppvData, cbFile);
1520 AssertRCReturn(vrc, vrc);
1521
1522 RTVFSFILE hVfsFile;
1523 vrc = RTVfsFileFromBuffer(RTFILE_O_READWRITE, *ppvData, cbFile, &hVfsFile);
1524 AssertRCReturn(vrc, vrc);
1525
1526 try
1527 {
1528 pThis->pNvramStore->m->bd->mapNvram[Utf8Str(szId)] = hVfsFile;
1529 }
1530 catch (...)
1531 {
1532 AssertLogRelFailed();
1533 RTVfsFileRelease(hVfsFile);
1534 return VERR_UNEXPECTED_EXCEPTION;
1535 }
1536 }
1537
1538 return VINF_SUCCESS;
1539}
1540
1541
1542/**
1543 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1544 */
1545DECLCALLBACK(void *) NvramStore::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1546{
1547 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1548 PDRVMAINNVRAMSTORE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1549
1550 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1551 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVFSCONNECTOR, &pDrv->IVfs);
1552 return NULL;
1553}
1554
1555
1556/**
1557 * Destruct a NVRAM store driver instance.
1558 *
1559 * @returns VBox status code.
1560 * @param pDrvIns The driver instance data.
1561 */
1562DECLCALLBACK(void) NvramStore::i_drvDestruct(PPDMDRVINS pDrvIns)
1563{
1564 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1565 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1566 LogFlow(("NvramStore::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
1567
1568 if (pThis->pNvramStore)
1569 {
1570 uint32_t cRefs = ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1571 if ( !cRefs
1572 && !pThis->pNvramStore->m->fSsmSaved)
1573 {
1574 try
1575 {
1576 int vrc = pThis->pNvramStore->i_saveStore();
1577 AssertLogRelRC(vrc); /** @todo Disk full error? */
1578 }
1579 catch (...)
1580 {
1581 AssertLogRelFailed();
1582 }
1583 }
1584 }
1585}
1586
1587
1588/**
1589 * Construct a NVRAM store driver instance.
1590 *
1591 * @copydoc FNPDMDRVCONSTRUCT
1592 */
1593DECLCALLBACK(int) NvramStore::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1594{
1595 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1596 RT_NOREF(fFlags, pCfg);
1597 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1598 LogFlow(("NvramStore::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
1599
1600 /*
1601 * Validate configuration.
1602 */
1603 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", "");
1604 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1605 ("Configuration error: Not possible to attach anything to this driver!\n"),
1606 VERR_PDM_DRVINS_NO_ATTACH);
1607
1608 /*
1609 * IBase.
1610 */
1611 pDrvIns->IBase.pfnQueryInterface = NvramStore::i_drvQueryInterface;
1612
1613 pThis->IVfs.pfnQuerySize = NvramStore::i_nvramStoreQuerySize;
1614 pThis->IVfs.pfnReadAll = NvramStore::i_nvramStoreReadAll;
1615 pThis->IVfs.pfnWriteAll = NvramStore::i_nvramStoreWriteAll;
1616 pThis->IVfs.pfnDelete = NvramStore::i_nvramStoreDelete;
1617
1618 /*
1619 * Get the NVRAM store object pointer.
1620 */
1621 com::Guid uuid(COM_IIDOF(INvramStore));
1622 pThis->pNvramStore = (NvramStore *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
1623 if (!pThis->pNvramStore)
1624 {
1625 AssertMsgFailed(("Configuration error: No/bad NVRAM store object!\n"));
1626 return VERR_NOT_FOUND;
1627 }
1628
1629 /*
1630 * Only the first instance will register the SSM handlers and will do the work on behalf
1631 * of all other NVRAM store driver instances when it comes to SSM.
1632 */
1633 if (pDrvIns->iInstance == 0)
1634 {
1635 int vrc = PDMDrvHlpSSMRegister(pDrvIns, NVRAM_STORE_SAVED_STATE_VERSION, 0 /*cbGuess*/,
1636 NvramStore::i_SsmSaveExec, NvramStore::i_SsmLoadExec);
1637 if (RT_FAILURE(vrc))
1638 return PDMDrvHlpVMSetError(pDrvIns, vrc, RT_SRC_POS,
1639 N_("Failed to register the saved state unit for the NVRAM store"));
1640 }
1641
1642 uint32_t cRefs = ASMAtomicIncU32(&pThis->pNvramStore->m->cRefs);
1643 if (cRefs == 1)
1644 {
1645 int vrc;
1646 try
1647 {
1648 vrc = pThis->pNvramStore->i_loadStore(pThis->pNvramStore->m->bd->strNvramPath.c_str());
1649 }
1650 catch (...)
1651 {
1652 vrc = VERR_UNEXPECTED_EXCEPTION;
1653 }
1654 if (RT_FAILURE(vrc))
1655 {
1656 ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1657 return PDMDrvHlpVMSetError(pDrvIns, vrc, RT_SRC_POS,
1658 N_("Failed to load the NVRAM store from the file"));
1659 }
1660 }
1661
1662 return VINF_SUCCESS;
1663}
1664
1665
1666/**
1667 * NVRAM store driver registration record.
1668 */
1669const PDMDRVREG NvramStore::DrvReg =
1670{
1671 /* u32Version */
1672 PDM_DRVREG_VERSION,
1673 /* szName */
1674 "NvramStore",
1675 /* szRCMod */
1676 "",
1677 /* szR0Mod */
1678 "",
1679 /* pszDescription */
1680 "Main NVRAM store driver (Main as in the API).",
1681 /* fFlags */
1682 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1683 /* fClass. */
1684 PDM_DRVREG_CLASS_STATUS,
1685 /* cMaxInstances */
1686 ~0U,
1687 /* cbInstance */
1688 sizeof(DRVMAINNVRAMSTORE),
1689 /* pfnConstruct */
1690 NvramStore::i_drvConstruct,
1691 /* pfnDestruct */
1692 NvramStore::i_drvDestruct,
1693 /* pfnRelocate */
1694 NULL,
1695 /* pfnIOCtl */
1696 NULL,
1697 /* pfnPowerOn */
1698 NULL,
1699 /* pfnReset */
1700 NULL,
1701 /* pfnSuspend */
1702 NULL,
1703 /* pfnResume */
1704 NULL,
1705 /* pfnAttach */
1706 NULL,
1707 /* pfnDetach */
1708 NULL,
1709 /* pfnPowerOff */
1710 NULL,
1711 /* pfnSoftReset */
1712 NULL,
1713 /* u32EndVersion */
1714 PDM_DRVREG_VERSION
1715};
1716
1717#endif /* VBOX_COM_INPROC */
1718
1719/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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