VirtualBox

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

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

Main/src-all: Adjust to the new rules wrt. to rc -> hrc,vrc usage, ​bugref:10223

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