VirtualBox

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

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

Main/{NvramStore,UefiVariableStore}: Always retain/releas the variable store reference in every externally accessible API to make sure the variable store is flushed to the NVRAM when saving avoiding data loss, bugref:9580

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 KB
Line 
1/* $Id: NvramStoreImpl.cpp 91535 2021-10-04 09:15:19Z vboxsync $ */
2/** @file
3 * VirtualBox COM NVRAM store class implementation
4 */
5
6/*
7 * Copyright (C) 2021 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 "AutoStateDep.h"
27#endif
28#include "UefiVariableStoreImpl.h"
29
30#include "AutoCaller.h"
31
32#include <VBox/com/array.h>
33#include <VBox/vmm/pdmdrv.h>
34#include <VBox/err.h>
35
36#include <iprt/cpp/utils.h>
37#include <iprt/efi.h>
38#include <iprt/file.h>
39#include <iprt/vfs.h>
40#include <iprt/zip.h>
41
42
43// defines
44////////////////////////////////////////////////////////////////////////////////
45
46// globals
47////////////////////////////////////////////////////////////////////////////////
48
49/**
50 * NVRAM store driver instance data.
51 */
52typedef struct DRVMAINNVRAMSTORE
53{
54 /** Pointer to the keyboard object. */
55 NvramStore *pNvramStore;
56 /** Pointer to the driver instance structure. */
57 PPDMDRVINS pDrvIns;
58 /** Our VFS connector interface. */
59 PDMIVFSCONNECTOR IVfs;
60} DRVMAINNVRAMSTORE, *PDRVMAINNVRAMSTORE;
61
62/** The NVRAM store map keyed by namespace/entity. */
63typedef std::map<Utf8Str, RTVFSFILE> NvramStoreMap;
64/** The NVRAM store map iterator. */
65typedef std::map<Utf8Str, RTVFSFILE>::iterator NvramStoreIter;
66
67struct BackupableNvramStoreData
68{
69 BackupableNvramStoreData()
70 { }
71
72 /** The NVRAM file path. */
73 com::Utf8Str strNvramPath;
74 /** The NVRAM store. */
75 NvramStoreMap mapNvram;
76};
77
78/////////////////////////////////////////////////////////////////////////////
79// NvramStore::Data structure
80/////////////////////////////////////////////////////////////////////////////
81
82struct NvramStore::Data
83{
84 Data()
85 : pParent(NULL)
86#ifdef VBOX_COM_INPROC
87 , cRefs(0)
88#endif
89 { }
90
91#ifdef VBOX_COM_INPROC
92 /** The Console owning this NVRAM store. */
93 Console * const pParent;
94 /** Number of references held to this NVRAM store from the various devices/drivers. */
95 volatile uint32_t cRefs;
96#else
97 /** The Machine object owning this NVRAM store. */
98 Machine * const pParent;
99 /** The peer NVRAM store object. */
100 ComObjPtr<NvramStore> pPeer;
101 /** The UEFI variable store. */
102 const ComObjPtr<UefiVariableStore> pUefiVarStore;
103#endif
104
105 Backupable<BackupableNvramStoreData> bd;
106};
107
108// constructor / destructor
109////////////////////////////////////////////////////////////////////////////////
110
111DEFINE_EMPTY_CTOR_DTOR(NvramStore)
112
113HRESULT NvramStore::FinalConstruct()
114{
115 return BaseFinalConstruct();
116}
117
118void NvramStore::FinalRelease()
119{
120 uninit();
121 BaseFinalRelease();
122}
123
124// public initializer/uninitializer for internal purposes only
125/////////////////////////////////////////////////////////////////////////////
126
127#if !defined(VBOX_COM_INPROC)
128/**
129 * Initializes the NVRAM store object.
130 *
131 * @returns COM result indicator
132 */
133HRESULT NvramStore::init(Machine *aParent)
134{
135 LogFlowThisFuncEnter();
136 LogFlowThisFunc(("aParent: %p\n", aParent));
137
138 ComAssertRet(aParent, E_INVALIDARG);
139
140 /* Enclose the state transition NotReady->InInit->Ready */
141 AutoInitSpan autoInitSpan(this);
142 AssertReturn(autoInitSpan.isOk(), E_FAIL);
143
144 m = new Data();
145
146 /* share the parent weakly */
147 unconst(m->pParent) = aParent;
148
149 m->bd.allocate();
150
151 autoInitSpan.setSucceeded();
152
153 LogFlowThisFuncLeave();
154 return S_OK;
155}
156
157/**
158 * Initializes the NVRAM store object given another NVRAM store object
159 * (a kind of copy constructor). This object shares data with
160 * the object passed as an argument.
161 *
162 * @note This object must be destroyed before the original object
163 * it shares data with is destroyed.
164 */
165HRESULT NvramStore::init(Machine *aParent, NvramStore *that)
166{
167 LogFlowThisFuncEnter();
168 LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
169
170 ComAssertRet(aParent && that, E_INVALIDARG);
171
172 /* Enclose the state transition NotReady->InInit->Ready */
173 AutoInitSpan autoInitSpan(this);
174 AssertReturn(autoInitSpan.isOk(), E_FAIL);
175
176 m = new Data();
177
178 unconst(m->pParent) = aParent;
179 m->pPeer = that;
180
181 AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
182 m->bd.share(that->m->bd);
183
184 autoInitSpan.setSucceeded();
185
186 LogFlowThisFuncLeave();
187 return S_OK;
188}
189
190/**
191 * Initializes the guest object given another guest object
192 * (a kind of copy constructor). This object makes a private copy of data
193 * of the original object passed as an argument.
194 */
195HRESULT NvramStore::initCopy(Machine *aParent, NvramStore *that)
196{
197 LogFlowThisFuncEnter();
198 LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
199
200 ComAssertRet(aParent && that, E_INVALIDARG);
201
202 /* Enclose the state transition NotReady->InInit->Ready */
203 AutoInitSpan autoInitSpan(this);
204 AssertReturn(autoInitSpan.isOk(), E_FAIL);
205
206 m = new Data();
207
208 unconst(m->pParent) = aParent;
209 // mPeer is left null
210
211 AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
212 m->bd.attachCopy(that->m->bd);
213
214 autoInitSpan.setSucceeded();
215
216 LogFlowThisFuncLeave();
217 return S_OK;
218}
219
220#else
221
222/**
223 * Initializes the NVRAM store object.
224 *
225 * @returns COM result indicator
226 * @param aParent Handle of our parent object
227 * @param strNonVolatileStorageFile The NVRAM file path.
228 */
229HRESULT NvramStore::init(Console *aParent, const com::Utf8Str &strNonVolatileStorageFile)
230{
231 LogFlowThisFunc(("aParent=%p\n", aParent));
232
233 ComAssertRet(aParent, E_INVALIDARG);
234
235 /* Enclose the state transition NotReady->InInit->Ready */
236 AutoInitSpan autoInitSpan(this);
237 AssertReturn(autoInitSpan.isOk(), E_FAIL);
238
239 m = new Data();
240 unconst(m->pParent) = aParent;
241
242 m->bd.allocate();
243 m->bd->strNvramPath = strNonVolatileStorageFile;
244
245 /* Confirm a successful initialization */
246 autoInitSpan.setSucceeded();
247
248 return S_OK;
249}
250#endif /* VBOX_COM_INPROC */
251
252
253/**
254 * Uninitializes the instance and sets the ready flag to FALSE.
255 * Called either from FinalRelease() or by the parent when it gets destroyed.
256 */
257void NvramStore::uninit()
258{
259 LogFlowThisFuncEnter();
260
261 /* Enclose the state transition Ready->InUninit->NotReady */
262 AutoUninitSpan autoUninitSpan(this);
263 if (autoUninitSpan.uninitDone())
264 return;
265
266 unconst(m->pParent) = NULL;
267#ifndef VBOX_COM_INPROC
268 unconst(m->pUefiVarStore) = NULL;
269#endif
270
271 /* Delete the NVRAM content. */
272 NvramStoreIter it = m->bd->mapNvram.begin();
273 while (it != m->bd->mapNvram.end())
274 {
275 RTVfsFileRelease(it->second);
276 it++;
277 }
278
279 m->bd->mapNvram.clear();
280
281 delete m;
282 m = NULL;
283
284 LogFlowThisFuncLeave();
285}
286
287
288HRESULT NvramStore::getNonVolatileStorageFile(com::Utf8Str &aNonVolatileStorageFile)
289{
290#ifndef VBOX_COM_INPROC
291 Utf8Str strTmp;
292 {
293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
294 strTmp = m->bd->strNvramPath;
295 }
296
297 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
298 if (strTmp.isEmpty())
299 strTmp = m->pParent->i_getDefaultNVRAMFilename();
300 if (strTmp.isNotEmpty())
301 m->pParent->i_calculateFullPath(strTmp, aNonVolatileStorageFile);
302#else
303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
304 aNonVolatileStorageFile = m->bd->strNvramPath;
305#endif
306
307 return S_OK;
308}
309
310
311HRESULT NvramStore::getUefiVariableStore(ComPtr<IUefiVariableStore> &aUefiVarStore)
312{
313#ifndef VBOX_COM_INPROC
314 /* the machine needs to be mutable */
315 AutoMutableStateDependency adep(m->pParent);
316 if (FAILED(adep.rc())) return adep.rc();
317
318 Utf8Str strPath;
319 NvramStore::getNonVolatileStorageFile(strPath);
320
321 /* We need a write lock because of the lazy initialization. */
322 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
323
324 /* Check if we have to create the UEFI variable store object */
325 HRESULT hrc = S_OK;
326 if (!m->pUefiVarStore)
327 {
328 /* Load the NVRAM file first if it isn't already. */
329 if (!m->bd->mapNvram.size())
330 {
331 int vrc = i_loadStore(strPath.c_str());
332 if (RT_FAILURE(vrc))
333 hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
334 }
335
336 if (SUCCEEDED(hrc))
337 {
338 NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
339 if (it != m->bd->mapNvram.end())
340 {
341 unconst(m->pUefiVarStore).createObject();
342 m->pUefiVarStore->init(this, m->pParent);
343 }
344 else
345 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
346 }
347 }
348
349 if (SUCCEEDED(hrc))
350 {
351 m->pUefiVarStore.queryInterfaceTo(aUefiVarStore.asOutParam());
352
353 /* Mark the NVRAM store as potentially modified. */
354 m->pParent->i_setModified(Machine::IsModified_NvramStore);
355 }
356
357 return hrc;
358#else
359 NOREF(aUefiVarStore);
360 return E_NOTIMPL;
361#endif
362}
363
364
365HRESULT NvramStore::initUefiVariableStore(ULONG aSize)
366{
367#ifndef VBOX_COM_INPROC
368 if (aSize != 0)
369 return setError(E_NOTIMPL, tr("Supporting another NVRAM size apart from the default one is not supported right now"));
370
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 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
380 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
381
382 if (m->pParent->i_getFirmwareType() == FirmwareType_BIOS)
383 return setError(VBOX_E_NOT_SUPPORTED, tr("The selected firmware type doesn't support a UEFI variable store"));
384
385 /* Load the NVRAM file first if it isn't already. */
386 HRESULT hrc = S_OK;
387 if (!m->bd->mapNvram.size())
388 {
389 int vrc = i_loadStore(strPath.c_str());
390 if (RT_FAILURE(vrc))
391 hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
392 }
393
394 if (SUCCEEDED(hrc))
395 {
396 int vrc = VINF_SUCCESS;
397 RTVFSFILE hVfsUefiVarStore = NIL_RTVFSFILE;
398 NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
399 if (it != m->bd->mapNvram.end())
400 hVfsUefiVarStore = it->second;
401 else
402 {
403 /* Create a new file. */
404 vrc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsUefiVarStore);
405 if (RT_SUCCESS(vrc))
406 {
407 /** @todo The size is hardcoded to match what the firmware image uses right now which is a gross hack... */
408 vrc = RTVfsFileSetSize(hVfsUefiVarStore, 540672, RTVFSFILE_SIZE_F_NORMAL);
409 if (RT_SUCCESS(vrc))
410 m->bd->mapNvram["efi/nvram"] = hVfsUefiVarStore;
411 else
412 RTVfsFileRelease(hVfsUefiVarStore);
413 }
414 }
415
416 if (RT_SUCCESS(vrc))
417 {
418 vrc = RTEfiVarStoreCreate(hVfsUefiVarStore, 0 /*offStore*/, 0 /*cbStore*/, RTEFIVARSTORE_CREATE_F_DEFAULT, 0 /*cbBlock*/,
419 NULL /*pErrInfo*/);
420 if (RT_FAILURE(vrc))
421 return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
422 }
423 else
424 return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
425
426 m->pParent->i_setModified(Machine::IsModified_NvramStore);
427 }
428
429 return hrc;
430#else
431 NOREF(aSize);
432 return E_NOTIMPL;
433#endif
434}
435
436
437Utf8Str NvramStore::i_getNonVolatileStorageFile()
438{
439 AutoCaller autoCaller(this);
440 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
441
442 Utf8Str strTmp;
443 NvramStore::getNonVolatileStorageFile(strTmp);
444 return strTmp;
445}
446
447
448/**
449 * Loads the NVRAM store from the given TAR filesystem stream.
450 *
451 * @returns IPRT status code.
452 * @param hVfsFssTar Handle to the tar filesystem stream.
453 */
454int NvramStore::i_loadStoreFromTar(RTVFSFSSTREAM hVfsFssTar)
455{
456 int rc = VINF_SUCCESS;
457
458 /*
459 * Process the stream.
460 */
461 for (;;)
462 {
463 /*
464 * Retrieve the next object.
465 */
466 char *pszName;
467 RTVFSOBJ hVfsObj;
468 rc = RTVfsFsStrmNext(hVfsFssTar, &pszName, NULL, &hVfsObj);
469 if (RT_FAILURE(rc))
470 {
471 if (rc == VERR_EOF)
472 rc = VINF_SUCCESS;
473 break;
474 }
475
476 RTFSOBJINFO UnixInfo;
477 rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
478 if (RT_SUCCESS(rc))
479 {
480 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
481 {
482 case RTFS_TYPE_FILE:
483 {
484 LogRel(("NvramStore: Loading '%s' from archive\n", pszName));
485 RTVFSIOSTREAM hVfsIosEntry = RTVfsObjToIoStream(hVfsObj);
486 Assert(hVfsIosEntry != NIL_RTVFSIOSTREAM);
487
488 RTVFSFILE hVfsFileEntry;
489 rc = RTVfsMemorizeIoStreamAsFile(hVfsIosEntry, RTFILE_O_READ | RTFILE_O_WRITE, &hVfsFileEntry);
490 if (RT_FAILURE(rc))
491 break;
492 RTVfsIoStrmRelease(hVfsIosEntry);
493
494 m->bd->mapNvram[Utf8Str(pszName)] = hVfsFileEntry;
495 break;
496 }
497 case RTFS_TYPE_DIRECTORY:
498 break;
499 default:
500 rc = VERR_NOT_SUPPORTED;
501 break;
502 }
503 }
504
505 /*
506 * Release the current object and string.
507 */
508 RTVfsObjRelease(hVfsObj);
509 RTStrFree(pszName);
510
511 if (RT_FAILURE(rc))
512 break;
513 }
514
515 return rc;
516}
517
518
519/**
520 * Loads the NVRAM store.
521 *
522 * @returns IPRT status code.
523 */
524int NvramStore::i_loadStore(const char *pszPath)
525{
526 uint64_t cbStore = 0;
527 int rc = RTFileQuerySizeByPath(pszPath, &cbStore);
528 if (RT_SUCCESS(rc))
529 {
530 if (cbStore <= _1M) /* Arbitrary limit to fend off bogus files because the file will be read into memory completely. */
531 {
532 /*
533 * Old NVRAM files just consist of the EFI variable store whereas starting
534 * with VirtualBox 7.0 and the introduction of the TPM the need to handle multiple
535 * independent NVRAM files came up. For those scenarios all NVRAM states are collected
536 * in a tar archive.
537 *
538 * Here we detect whether the file is the new tar archive format or whether it is just
539 * the plain EFI variable store file.
540 */
541 RTVFSIOSTREAM hVfsIosNvram;
542 rc = RTVfsIoStrmOpenNormal(pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE,
543 &hVfsIosNvram);
544 if (RT_SUCCESS(rc))
545 {
546 /* Read the content. */
547 RTVFSFILE hVfsFileNvram;
548 rc = RTVfsMemorizeIoStreamAsFile(hVfsIosNvram, RTFILE_O_READ, &hVfsFileNvram);
549 if (RT_SUCCESS(rc))
550 {
551 /* Try to parse it as an EFI variable store. */
552 RTVFS hVfsEfiVarStore;
553 rc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, RTVFSMNT_F_READ_ONLY, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
554 NULL /*pErrInfo*/);
555 if (RT_SUCCESS(rc))
556 {
557 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
558 AssertRC(rc);
559
560 RTVfsFileRetain(hVfsFileNvram); /* Retain a new reference for the map. */
561 m->bd->mapNvram[Utf8Str("efi/nvram")] = hVfsFileNvram;
562
563 RTVfsRelease(hVfsEfiVarStore);
564 }
565 else if (rc == VERR_VFS_UNKNOWN_FORMAT)
566 {
567 /* Check for the new style tar archive. */
568 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
569 AssertRC(rc);
570
571 RTVFSIOSTREAM hVfsIosTar = RTVfsFileToIoStream(hVfsFileNvram);
572 Assert(hVfsIosTar != NIL_RTVFSIOSTREAM);
573
574 RTVFSFSSTREAM hVfsFssTar;
575 rc = RTZipTarFsStreamFromIoStream(hVfsIosTar, 0 /*fFlags*/, &hVfsFssTar);
576 RTVfsIoStrmRelease(hVfsIosTar);
577 if (RT_SUCCESS(rc))
578 {
579 rc = i_loadStoreFromTar(hVfsFssTar);
580 RTVfsFsStrmRelease(hVfsFssTar);
581 }
582 else
583 LogRel(("The given NVRAM file is neither a raw UEFI variable store nor a tar archive (opening failed with %Rrc)\n", rc));
584 }
585 else
586 LogRel(("Opening the UEFI variable store '%s' failed with %Rrc\n", pszPath, rc));
587
588 RTVfsFileRelease(hVfsFileNvram);
589 }
590 else
591 LogRel(("Failed to memorize NVRAM store '%s' with %Rrc\n", pszPath, rc));
592
593 RTVfsIoStrmRelease(hVfsIosNvram);
594 }
595 else
596 LogRelMax(10, ("NVRAM store '%s' couldn't be opened with %Rrc\n", pszPath, rc));
597 }
598 else
599 LogRelMax(10, ("NVRAM store '%s' exceeds limit of %u bytes, actual size is %u\n",
600 pszPath, _1M, cbStore));
601 }
602 else if (rc == VERR_FILE_NOT_FOUND) /* Valid for the first run where no NVRAM file is there. */
603 rc = VINF_SUCCESS;
604
605 return rc;
606}
607
608
609/**
610 * Saves the NVRAM store as a tar archive.
611 */
612int NvramStore::i_saveStoreAsTar(void)
613{
614 uint32_t offError = 0;
615 RTERRINFOSTATIC ErrInfo;
616 RTVFSIOSTREAM hVfsIos;
617
618 int rc = RTVfsChainOpenIoStream(m->bd->strNvramPath.c_str(), RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
619 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
620 if (RT_SUCCESS(rc))
621 {
622 RTVFSFSSTREAM hVfsFss;
623 rc = RTZipTarFsStreamToIoStream(hVfsIos, RTZIPTARFORMAT_GNU, 0 /*fFlags*/, &hVfsFss);
624 if (RT_SUCCESS(rc))
625 {
626 NvramStoreIter it = m->bd->mapNvram.begin();
627
628 while (it != m->bd->mapNvram.end())
629 {
630 RTVFSFILE hVfsFile = it->second;
631
632 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
633 AssertRC(rc);
634
635 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile);
636 rc = RTVfsFsStrmAdd(hVfsFss, it->first.c_str(), hVfsObj, 0 /*fFlags*/);
637 RTVfsObjRelease(hVfsObj);
638 if (RT_FAILURE(rc))
639 break;
640
641 it++;
642 }
643
644 RTVfsFsStrmRelease(hVfsFss);
645 }
646
647 RTVfsIoStrmRelease(hVfsIos);
648 }
649
650 return rc;
651}
652
653
654/**
655 * Saves the NVRAM store.
656 *
657 * @returns IPRT status code.
658 */
659int NvramStore::i_saveStore(void)
660{
661 /*
662 * Skip creating the tar archive if only the UEFI NVRAM content is available in order
663 * to maintain backwards compatibility. As soon as there is more than one entry or
664 * it doesn't belong to the UEFI the tar archive will be created.
665 */
666 int rc = VINF_SUCCESS;
667
668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
669 if ( m->bd->mapNvram.size() == 1
670 && m->bd->mapNvram.find(Utf8Str("efi/nvram")) != m->bd->mapNvram.end())
671 {
672 RTVFSFILE hVfsFileNvram = m->bd->mapNvram[Utf8Str("efi/nvram")];
673
674 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
675 AssertRC(rc); RT_NOREF(rc);
676
677 Utf8Str strTmp;
678 NvramStore::getNonVolatileStorageFile(strTmp);
679
680 RTVFSIOSTREAM hVfsIosDst;
681 rc = RTVfsIoStrmOpenNormal(strTmp.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
682 &hVfsIosDst);
683 if (RT_SUCCESS(rc))
684 {
685 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsFileNvram);
686 Assert(hVfsIosSrc != NIL_RTVFSIOSTREAM);
687
688 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0 /*cbBufHint*/);
689
690 RTVfsIoStrmRelease(hVfsIosSrc);
691 RTVfsIoStrmRelease(hVfsIosDst);
692 }
693 }
694 else if (m->bd->mapNvram.size())
695 rc = i_saveStoreAsTar();
696 /* else: No NVRAM content to store so we are done here. */
697
698 return rc;
699}
700
701
702#ifndef VBOX_COM_INPROC
703HRESULT NvramStore::i_retainUefiVarStore(PRTVFS phVfs, bool fReadonly)
704{
705 /* the machine needs to be mutable */
706 AutoMutableStateDependency adep(m->pParent);
707 if (FAILED(adep.rc())) return adep.rc();
708
709 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
710
711 HRESULT hrc = S_OK;
712 NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
713 if (it != m->bd->mapNvram.end())
714 {
715 RTVFSFILE hVfsFileNvram = it->second;
716 RTVFS hVfsEfiVarStore;
717 uint32_t fMntFlags = fReadonly ? RTVFSMNT_F_READ_ONLY : 0;
718
719 int vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, fMntFlags, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
720 NULL /*pErrInfo*/);
721 if (RT_SUCCESS(vrc))
722 {
723 *phVfs = hVfsEfiVarStore;
724 if (!fReadonly)
725 m->pParent->i_setModified(Machine::IsModified_NvramStore);
726 }
727 else
728 hrc = setError(E_FAIL, tr("Opening the UEFI variable store failed (%Rrc)."), vrc);
729 }
730 else
731 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
732
733 return hrc;
734}
735
736
737HRESULT NvramStore::i_releaseUefiVarStore(RTVFS hVfs)
738{
739 RTVfsRelease(hVfs);
740 return S_OK;
741}
742
743
744/**
745 * Loads settings from the given machine node.
746 * May be called once right after this object creation.
747 *
748 * @param data Configuration settings.
749 *
750 * @note Locks this object for writing.
751 */
752HRESULT NvramStore::i_loadSettings(const settings::NvramSettings &data)
753{
754 AutoCaller autoCaller(this);
755 AssertComRCReturnRC(autoCaller.rc());
756
757 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
759
760 m->bd->strNvramPath = data.strNvramPath;
761
762 Utf8Str strTmp(m->bd->strNvramPath);
763 if (strTmp.isNotEmpty())
764 m->pParent->i_copyPathRelativeToMachine(strTmp, m->bd->strNvramPath);
765 if ( m->pParent->i_getFirmwareType() == FirmwareType_BIOS
766 || m->bd->strNvramPath == m->pParent->i_getDefaultNVRAMFilename())
767 m->bd->strNvramPath.setNull();
768
769 return S_OK;
770}
771
772/**
773 * Saves settings to the given machine node.
774 *
775 * @param data Configuration settings.
776 *
777 * @note Locks this object for writing.
778 */
779HRESULT NvramStore::i_saveSettings(settings::NvramSettings &data)
780{
781 AutoCaller autoCaller(this);
782 AssertComRCReturnRC(autoCaller.rc());
783
784 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
785
786 data.strNvramPath = m->bd->strNvramPath;
787
788 int vrc = i_saveStore();
789 if (RT_FAILURE(vrc))
790 return setError(E_FAIL, tr("Failed to save the NVRAM content to disk (%Rrc)"), vrc);
791
792 return S_OK;
793}
794
795void NvramStore::i_rollback()
796{
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798 m->bd.rollback();
799}
800
801void NvramStore::i_commit()
802{
803 /* sanity */
804 AutoCaller autoCaller(this);
805 AssertComRCReturnVoid(autoCaller.rc());
806
807 /* sanity too */
808 AutoCaller peerCaller(m->pPeer);
809 AssertComRCReturnVoid(peerCaller.rc());
810
811 /* lock both for writing since we modify both (mPeer is "master" so locked
812 * first) */
813 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
814
815 if (m->bd.isBackedUp())
816 {
817 m->bd.commit();
818 if (m->pPeer)
819 {
820 /* attach new data to the peer and reshare it */
821 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
822 m->pPeer->m->bd.attach(m->bd);
823 }
824 }
825}
826
827void NvramStore::i_copyFrom(NvramStore *aThat)
828{
829 AssertReturnVoid(aThat != NULL);
830
831 /* sanity */
832 AutoCaller autoCaller(this);
833 AssertComRCReturnVoid(autoCaller.rc());
834
835 /* sanity too */
836 AutoCaller thatCaller(aThat);
837 AssertComRCReturnVoid(thatCaller.rc());
838
839 /* peer is not modified, lock it for reading (aThat is "master" so locked
840 * first) */
841 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
842 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
843
844 /* this will back up current data */
845 m->bd.assignCopy(aThat->m->bd);
846
847 // Intentionally "forget" the NVRAM file since it must be unique and set
848 // to the correct value before the copy of the settings makes sense.
849 m->bd->strNvramPath.setNull();
850}
851
852void NvramStore::i_updateNonVolatileStorageFile(const Utf8Str &aNonVolatileStorageFile)
853{
854 /* sanity */
855 AutoCaller autoCaller(this);
856 AssertComRCReturnVoid(autoCaller.rc());
857
858 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
860
861 Utf8Str strTmp(aNonVolatileStorageFile);
862 if (strTmp == m->pParent->i_getDefaultNVRAMFilename())
863 strTmp.setNull();
864
865 if (strTmp == m->bd->strNvramPath)
866 return;
867
868 m->bd.backup();
869 m->bd->strNvramPath = strTmp;
870}
871
872#else
873//
874// private methods
875//
876/*static*/
877DECLCALLBACK(int) NvramStore::i_nvramStoreQuerySize(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
878 uint64_t *pcb)
879{
880 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
881
882 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
883 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
884 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
885 {
886 RTVFSFILE hVfsFile = it->second;
887 return RTVfsFileQuerySize(hVfsFile, pcb);
888 }
889
890 return VERR_NOT_FOUND;
891}
892
893
894/*static*/
895DECLCALLBACK(int) NvramStore::i_nvramStoreReadAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
896 void *pvBuf, size_t cbRead)
897{
898 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
899
900 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
901 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
902 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
903 {
904 RTVFSFILE hVfsFile = it->second;
905
906 int rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
907 AssertRC(rc); RT_NOREF(rc);
908
909 return RTVfsFileRead(hVfsFile, pvBuf, cbRead, NULL /*pcbRead*/);
910 }
911
912 return VERR_NOT_FOUND;
913}
914
915
916/*static*/
917DECLCALLBACK(int) NvramStore::i_nvramStoreWriteAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
918 const void *pvBuf, size_t cbWrite)
919{
920 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
921
922 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
923
924 int rc = VINF_SUCCESS;
925 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
926 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
927 {
928 RTVFSFILE hVfsFile = it->second;
929
930 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
931 AssertRC(rc);
932 rc = RTVfsFileSetSize(hVfsFile, cbWrite, RTVFSFILE_SIZE_F_NORMAL);
933 if (RT_SUCCESS(rc))
934 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbWrite, NULL /*pcbWritten*/);
935 }
936 else
937 {
938 /* Create a new entry. */
939 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
940 rc = RTVfsFileFromBuffer(RTFILE_O_READ | RTFILE_O_WRITE, pvBuf, cbWrite, &hVfsFile);
941 if (RT_SUCCESS(rc))
942 pThis->pNvramStore->m->bd->mapNvram[Utf8StrFmt("%s/%s", pszNamespace, pszPath)] = hVfsFile;
943 }
944
945 return rc;
946}
947
948
949/*static*/
950DECLCALLBACK(int) NvramStore::i_nvramStoreDelete(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath)
951{
952 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
953
954 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
955 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
956 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
957 {
958 RTVFSFILE hVfsFile = it->second;
959 pThis->pNvramStore->m->bd->mapNvram.erase(it);
960 RTVfsFileRelease(hVfsFile);
961 return VINF_SUCCESS;
962 }
963
964 return VERR_NOT_FOUND;
965}
966
967
968/**
969 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
970 */
971DECLCALLBACK(void *) NvramStore::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
972{
973 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
974 PDRVMAINNVRAMSTORE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
975
976 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
977 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVFSCONNECTOR, &pDrv->IVfs);
978 return NULL;
979}
980
981
982/**
983 * Destruct a NVRAM store driver instance.
984 *
985 * @returns VBox status code.
986 * @param pDrvIns The driver instance data.
987 */
988DECLCALLBACK(void) NvramStore::i_drvDestruct(PPDMDRVINS pDrvIns)
989{
990 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
991 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
992 LogFlow(("NvramStore::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
993
994 if (pThis->pNvramStore)
995 {
996 uint32_t cRefs = ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
997 if (!cRefs)
998 {
999 int rc = pThis->pNvramStore->i_saveStore();
1000 AssertRC(rc); /** @todo Disk full error? */
1001 }
1002 }
1003}
1004
1005
1006/**
1007 * Construct a NVRAM store driver instance.
1008 *
1009 * @copydoc FNPDMDRVCONSTRUCT
1010 */
1011DECLCALLBACK(int) NvramStore::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1012{
1013 RT_NOREF(fFlags);
1014 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1015 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1016 LogFlow(("NvramStore::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
1017
1018 /*
1019 * Validate configuration.
1020 */
1021 if (!CFGMR3AreValuesValid(pCfg, ""))
1022 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1023 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1024 ("Configuration error: Not possible to attach anything to this driver!\n"),
1025 VERR_PDM_DRVINS_NO_ATTACH);
1026
1027 /*
1028 * IBase.
1029 */
1030 pDrvIns->IBase.pfnQueryInterface = NvramStore::i_drvQueryInterface;
1031
1032 pThis->IVfs.pfnQuerySize = NvramStore::i_nvramStoreQuerySize;
1033 pThis->IVfs.pfnReadAll = NvramStore::i_nvramStoreReadAll;
1034 pThis->IVfs.pfnWriteAll = NvramStore::i_nvramStoreWriteAll;
1035 pThis->IVfs.pfnDelete = NvramStore::i_nvramStoreDelete;
1036
1037 /*
1038 * Get the NVRAM store object pointer.
1039 */
1040 com::Guid uuid(COM_IIDOF(INvramStore));
1041 pThis->pNvramStore = (NvramStore *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
1042 if (!pThis->pNvramStore)
1043 {
1044 AssertMsgFailed(("Configuration error: No/bad NVRAM store object!\n"));
1045 return VERR_NOT_FOUND;
1046 }
1047
1048 uint32_t cRefs = ASMAtomicIncU32(&pThis->pNvramStore->m->cRefs);
1049 if (cRefs == 1)
1050 {
1051 int rc = pThis->pNvramStore->i_loadStore(pThis->pNvramStore->m->bd->strNvramPath.c_str());
1052 if (RT_FAILURE(rc))
1053 {
1054 ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1055 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1056 N_("Failed to load the NVRAM store from the file"));
1057 }
1058 }
1059
1060 return VINF_SUCCESS;
1061}
1062
1063
1064/**
1065 * NVRAM store driver registration record.
1066 */
1067const PDMDRVREG NvramStore::DrvReg =
1068{
1069 /* u32Version */
1070 PDM_DRVREG_VERSION,
1071 /* szName */
1072 "NvramStore",
1073 /* szRCMod */
1074 "",
1075 /* szR0Mod */
1076 "",
1077 /* pszDescription */
1078 "Main NVRAM store driver (Main as in the API).",
1079 /* fFlags */
1080 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1081 /* fClass. */
1082 PDM_DRVREG_CLASS_STATUS,
1083 /* cMaxInstances */
1084 ~0U,
1085 /* cbInstance */
1086 sizeof(DRVMAINNVRAMSTORE),
1087 /* pfnConstruct */
1088 NvramStore::i_drvConstruct,
1089 /* pfnDestruct */
1090 NvramStore::i_drvDestruct,
1091 /* pfnRelocate */
1092 NULL,
1093 /* pfnIOCtl */
1094 NULL,
1095 /* pfnPowerOn */
1096 NULL,
1097 /* pfnReset */
1098 NULL,
1099 /* pfnSuspend */
1100 NULL,
1101 /* pfnResume */
1102 NULL,
1103 /* pfnAttach */
1104 NULL,
1105 /* pfnDetach */
1106 NULL,
1107 /* pfnPowerOff */
1108 NULL,
1109 /* pfnSoftReset */
1110 NULL,
1111 /* u32EndVersion */
1112 PDM_DRVREG_VERSION
1113};
1114#endif /* !VBOX_COM_INPROC */
1115
1116/* 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