VirtualBox

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

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

Main/NvramStore,Runtime/efivarstorevfs: Some fixes to the nvram initialization, bugref:9580

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.1 KB
Line 
1/* $Id: NvramStoreImpl.cpp 91497 2021-09-30 11:34:59Z 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 RTVFSFILE hVfsFileNvram = it->second;
342 RTVFS hVfsEfiVarStore;
343 int vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, 0 /*fMntFlags*/, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
344 NULL /*pErrInfo*/);
345 if (RT_SUCCESS(vrc))
346 {
347 unconst(m->pUefiVarStore).createObject();
348 m->pUefiVarStore->init(this, m->pParent, hVfsEfiVarStore);
349 }
350 else
351 hrc = setError(E_FAIL, tr("Opening the UEFI variable store failed (%Rrc)."), vrc);
352 }
353 else
354 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
355 }
356 }
357
358 if (SUCCEEDED(hrc))
359 {
360 m->pUefiVarStore.queryInterfaceTo(aUefiVarStore.asOutParam());
361
362 /* Mark the NVRAM store as potentially modified. */
363 m->pParent->i_setModified(Machine::IsModified_NvramStore);
364 }
365
366 return hrc;
367#else
368 NOREF(aUefiVarStore);
369 return E_NOTIMPL;
370#endif
371}
372
373
374HRESULT NvramStore::initUefiVariableStore(ULONG aSize)
375{
376#ifndef VBOX_COM_INPROC
377 if (aSize != 0)
378 return setError(E_NOTIMPL, tr("Supporting another NVRAM size apart from the default one is not supported right now"));
379
380 /* the machine needs to be mutable */
381 AutoMutableStateDependency adep(m->pParent);
382 if (FAILED(adep.rc())) return adep.rc();
383
384 Utf8Str strPath;
385 NvramStore::getNonVolatileStorageFile(strPath);
386
387 /* We need a write lock because of the lazy initialization. */
388 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
389 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
390
391 if (m->pParent->i_getFirmwareType() == FirmwareType_BIOS)
392 return setError(VBOX_E_NOT_SUPPORTED, tr("The selected firmware type doesn't support a UEFI variable store"));
393
394 /* Load the NVRAM file first if it isn't already. */
395 HRESULT hrc = S_OK;
396 if (!m->bd->mapNvram.size())
397 {
398 int vrc = i_loadStore(strPath.c_str());
399 if (RT_FAILURE(vrc))
400 hrc = setError(E_FAIL, tr("Loading the NVRAM store failed (%Rrc)\n"), vrc);
401 }
402
403 if (SUCCEEDED(hrc))
404 {
405 int vrc = VINF_SUCCESS;
406 RTVFSFILE hVfsUefiVarStore = NIL_RTVFSFILE;
407 NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
408 if (it != m->bd->mapNvram.end())
409 hVfsUefiVarStore = it->second;
410 else
411 {
412 /* Create a new file. */
413 vrc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsUefiVarStore);
414 if (RT_SUCCESS(vrc))
415 {
416 /** @todo The size is hardcoded to match what the firmware image uses right now which is a gross hack... */
417 vrc = RTVfsFileSetSize(hVfsUefiVarStore, 540672, RTVFSFILE_SIZE_F_NORMAL);
418 if (RT_SUCCESS(vrc))
419 m->bd->mapNvram["efi/nvram"] = hVfsUefiVarStore;
420 else
421 RTVfsFileRelease(hVfsUefiVarStore);
422 }
423 }
424
425 if (RT_SUCCESS(vrc))
426 {
427 vrc = RTEfiVarStoreCreate(hVfsUefiVarStore, 0 /*offStore*/, 0 /*cbStore*/, RTEFIVARSTORE_CREATE_F_DEFAULT, 0 /*cbBlock*/,
428 NULL /*pErrInfo*/);
429 if (RT_FAILURE(vrc))
430 return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
431 }
432 else
433 return setError(E_FAIL, tr("Failed to initialize the UEFI variable store (%Rrc)"), vrc);
434
435 m->pParent->i_setModified(Machine::IsModified_NvramStore);
436 }
437
438 return hrc;
439#else
440 NOREF(aSize);
441 return E_NOTIMPL;
442#endif
443}
444
445
446Utf8Str NvramStore::i_getNonVolatileStorageFile()
447{
448 AutoCaller autoCaller(this);
449 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
450
451 Utf8Str strTmp;
452 NvramStore::getNonVolatileStorageFile(strTmp);
453 return strTmp;
454}
455
456
457/**
458 * Loads the NVRAM store from the given TAR filesystem stream.
459 *
460 * @returns IPRT status code.
461 * @param hVfsFssTar Handle to the tar filesystem stream.
462 */
463int NvramStore::i_loadStoreFromTar(RTVFSFSSTREAM hVfsFssTar)
464{
465 int rc = VINF_SUCCESS;
466
467 /*
468 * Process the stream.
469 */
470 for (;;)
471 {
472 /*
473 * Retrieve the next object.
474 */
475 char *pszName;
476 RTVFSOBJ hVfsObj;
477 rc = RTVfsFsStrmNext(hVfsFssTar, &pszName, NULL, &hVfsObj);
478 if (RT_FAILURE(rc))
479 {
480 if (rc == VERR_EOF)
481 rc = VINF_SUCCESS;
482 break;
483 }
484
485 RTFSOBJINFO UnixInfo;
486 rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
487 if (RT_SUCCESS(rc))
488 {
489 switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
490 {
491 case RTFS_TYPE_FILE:
492 {
493 LogRel(("NvramStore: Loading '%s' from archive\n", pszName));
494 RTVFSIOSTREAM hVfsIosEntry = RTVfsObjToIoStream(hVfsObj);
495 Assert(hVfsIosEntry != NIL_RTVFSIOSTREAM);
496
497 RTVFSFILE hVfsFileEntry;
498 rc = RTVfsMemorizeIoStreamAsFile(hVfsIosEntry, RTFILE_O_READ | RTFILE_O_WRITE, &hVfsFileEntry);
499 if (RT_FAILURE(rc))
500 break;
501 RTVfsIoStrmRelease(hVfsIosEntry);
502
503 m->bd->mapNvram[Utf8Str(pszName)] = hVfsFileEntry;
504 break;
505 }
506 case RTFS_TYPE_DIRECTORY:
507 break;
508 default:
509 rc = VERR_NOT_SUPPORTED;
510 break;
511 }
512 }
513
514 /*
515 * Release the current object and string.
516 */
517 RTVfsObjRelease(hVfsObj);
518 RTStrFree(pszName);
519
520 if (RT_FAILURE(rc))
521 break;
522 }
523
524 return rc;
525}
526
527
528/**
529 * Loads the NVRAM store.
530 *
531 * @returns IPRT status code.
532 */
533int NvramStore::i_loadStore(const char *pszPath)
534{
535 uint64_t cbStore = 0;
536 int rc = RTFileQuerySizeByPath(pszPath, &cbStore);
537 if (RT_SUCCESS(rc))
538 {
539 if (cbStore <= _1M) /* Arbitrary limit to fend off bogus files because the file will be read into memory completely. */
540 {
541 /*
542 * Old NVRAM files just consist of the EFI variable store whereas starting
543 * with VirtualBox 7.0 and the introduction of the TPM the need to handle multiple
544 * independent NVRAM files came up. For those scenarios all NVRAM states are collected
545 * in a tar archive.
546 *
547 * Here we detect whether the file is the new tar archive format or whether it is just
548 * the plain EFI variable store file.
549 */
550 RTVFSIOSTREAM hVfsIosNvram;
551 rc = RTVfsIoStrmOpenNormal(pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE,
552 &hVfsIosNvram);
553 if (RT_SUCCESS(rc))
554 {
555 /* Read the content. */
556 RTVFSFILE hVfsFileNvram;
557 rc = RTVfsMemorizeIoStreamAsFile(hVfsIosNvram, RTFILE_O_READ, &hVfsFileNvram);
558 if (RT_SUCCESS(rc))
559 {
560 /* Try to parse it as an EFI variable store. */
561 RTVFS hVfsEfiVarStore;
562 rc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, RTVFSMNT_F_READ_ONLY, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
563 NULL /*pErrInfo*/);
564 if (RT_SUCCESS(rc))
565 {
566 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
567 AssertRC(rc);
568
569 RTVfsFileRetain(hVfsFileNvram); /* Retain a new reference for the map. */
570 m->bd->mapNvram[Utf8Str("efi/nvram")] = hVfsFileNvram;
571
572 RTVfsRelease(hVfsEfiVarStore);
573 }
574 else if (rc == VERR_VFS_UNKNOWN_FORMAT)
575 {
576 /* Check for the new style tar archive. */
577 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
578 AssertRC(rc);
579
580 RTVFSIOSTREAM hVfsIosTar = RTVfsFileToIoStream(hVfsFileNvram);
581 Assert(hVfsIosTar != NIL_RTVFSIOSTREAM);
582
583 RTVFSFSSTREAM hVfsFssTar;
584 rc = RTZipTarFsStreamFromIoStream(hVfsIosTar, 0 /*fFlags*/, &hVfsFssTar);
585 RTVfsIoStrmRelease(hVfsIosTar);
586 if (RT_SUCCESS(rc))
587 {
588 rc = i_loadStoreFromTar(hVfsFssTar);
589 RTVfsFsStrmRelease(hVfsFssTar);
590 }
591 else
592 LogRel(("The given NVRAM file is neither a raw UEFI variable store nor a tar archive (opening failed with %Rrc)\n", rc));
593 }
594 else
595 LogRel(("Opening the UEFI variable store '%s' failed with %Rrc\n", pszPath, rc));
596
597 RTVfsFileRelease(hVfsFileNvram);
598 }
599 else
600 LogRel(("Failed to memorize NVRAM store '%s' with %Rrc\n", pszPath, rc));
601
602 RTVfsIoStrmRelease(hVfsIosNvram);
603 }
604 else
605 LogRelMax(10, ("NVRAM store '%s' couldn't be opened with %Rrc\n", pszPath, rc));
606 }
607 else
608 LogRelMax(10, ("NVRAM store '%s' exceeds limit of %u bytes, actual size is %u\n",
609 pszPath, _1M, cbStore));
610 }
611 else if (rc == VERR_FILE_NOT_FOUND) /* Valid for the first run where no NVRAM file is there. */
612 rc = VINF_SUCCESS;
613
614 return rc;
615}
616
617
618/**
619 * Saves the NVRAM store as a tar archive.
620 */
621int NvramStore::i_saveStoreAsTar(void)
622{
623 uint32_t offError = 0;
624 RTERRINFOSTATIC ErrInfo;
625 RTVFSIOSTREAM hVfsIos;
626
627 int rc = RTVfsChainOpenIoStream(m->bd->strNvramPath.c_str(), RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
628 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
629 if (RT_SUCCESS(rc))
630 {
631 RTVFSFSSTREAM hVfsFss;
632 rc = RTZipTarFsStreamToIoStream(hVfsIos, RTZIPTARFORMAT_GNU, 0 /*fFlags*/, &hVfsFss);
633 if (RT_SUCCESS(rc))
634 {
635 NvramStoreIter it = m->bd->mapNvram.begin();
636
637 while (it != m->bd->mapNvram.end())
638 {
639 RTVFSFILE hVfsFile = it->second;
640
641 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
642 AssertRC(rc);
643
644 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile);
645 rc = RTVfsFsStrmAdd(hVfsFss, it->first.c_str(), hVfsObj, 0 /*fFlags*/);
646 RTVfsObjRelease(hVfsObj);
647 if (RT_FAILURE(rc))
648 break;
649
650 it++;
651 }
652
653 RTVfsFsStrmRelease(hVfsFss);
654 }
655
656 RTVfsIoStrmRelease(hVfsIos);
657 }
658
659 return rc;
660}
661
662
663/**
664 * Saves the NVRAM store.
665 *
666 * @returns IPRT status code.
667 */
668int NvramStore::i_saveStore(void)
669{
670 /*
671 * Skip creating the tar archive if only the UEFI NVRAM content is available in order
672 * to maintain backwards compatibility. As soon as there is more than one entry or
673 * it doesn't belong to the UEFI the tar archive will be created.
674 */
675 int rc = VINF_SUCCESS;
676
677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
678 if ( m->bd->mapNvram.size() == 1
679 && m->bd->mapNvram.find(Utf8Str("efi/nvram")) != m->bd->mapNvram.end())
680 {
681 RTVFSFILE hVfsFileNvram = m->bd->mapNvram[Utf8Str("efi/nvram")];
682
683 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
684 AssertRC(rc); RT_NOREF(rc);
685
686 Utf8Str strTmp;
687 NvramStore::getNonVolatileStorageFile(strTmp);
688
689 RTVFSIOSTREAM hVfsIosDst;
690 rc = RTVfsIoStrmOpenNormal(strTmp.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
691 &hVfsIosDst);
692 if (RT_SUCCESS(rc))
693 {
694 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsFileNvram);
695 Assert(hVfsIosSrc != NIL_RTVFSIOSTREAM);
696
697 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0 /*cbBufHint*/);
698
699 RTVfsIoStrmRelease(hVfsIosSrc);
700 RTVfsIoStrmRelease(hVfsIosDst);
701 }
702 }
703 else if (m->bd->mapNvram.size())
704 rc = i_saveStoreAsTar();
705 /* else: No NVRAM content to store so we are done here. */
706
707 return rc;
708}
709
710
711#ifndef VBOX_COM_INPROC
712/**
713 * Loads settings from the given machine node.
714 * May be called once right after this object creation.
715 *
716 * @param data Configuration settings.
717 *
718 * @note Locks this object for writing.
719 */
720HRESULT NvramStore::i_loadSettings(const settings::NvramSettings &data)
721{
722 AutoCaller autoCaller(this);
723 AssertComRCReturnRC(autoCaller.rc());
724
725 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
727
728 m->bd->strNvramPath = data.strNvramPath;
729
730 Utf8Str strTmp(m->bd->strNvramPath);
731 if (strTmp.isNotEmpty())
732 m->pParent->i_copyPathRelativeToMachine(strTmp, m->bd->strNvramPath);
733 if ( m->pParent->i_getFirmwareType() == FirmwareType_BIOS
734 || m->bd->strNvramPath == m->pParent->i_getDefaultNVRAMFilename())
735 m->bd->strNvramPath.setNull();
736
737 return S_OK;
738}
739
740/**
741 * Saves settings to the given machine node.
742 *
743 * @param data Configuration settings.
744 *
745 * @note Locks this object for writing.
746 */
747HRESULT NvramStore::i_saveSettings(settings::NvramSettings &data)
748{
749 AutoCaller autoCaller(this);
750 AssertComRCReturnRC(autoCaller.rc());
751
752 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
753
754 data.strNvramPath = m->bd->strNvramPath;
755
756 unconst(m->pUefiVarStore) = NULL;
757
758 int vrc = i_saveStore();
759 if (RT_FAILURE(vrc))
760 return setError(E_FAIL, tr("Failed to save the NVRAM content to disk (%Rrc)"), vrc);
761
762 return S_OK;
763}
764
765void NvramStore::i_rollback()
766{
767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
768 m->bd.rollback();
769}
770
771void NvramStore::i_commit()
772{
773 /* sanity */
774 AutoCaller autoCaller(this);
775 AssertComRCReturnVoid(autoCaller.rc());
776
777 /* sanity too */
778 AutoCaller peerCaller(m->pPeer);
779 AssertComRCReturnVoid(peerCaller.rc());
780
781 /* lock both for writing since we modify both (mPeer is "master" so locked
782 * first) */
783 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
784
785 if (m->bd.isBackedUp())
786 {
787 m->bd.commit();
788 if (m->pPeer)
789 {
790 /* attach new data to the peer and reshare it */
791 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
792 m->pPeer->m->bd.attach(m->bd);
793 }
794 }
795}
796
797void NvramStore::i_copyFrom(NvramStore *aThat)
798{
799 AssertReturnVoid(aThat != NULL);
800
801 /* sanity */
802 AutoCaller autoCaller(this);
803 AssertComRCReturnVoid(autoCaller.rc());
804
805 /* sanity too */
806 AutoCaller thatCaller(aThat);
807 AssertComRCReturnVoid(thatCaller.rc());
808
809 /* peer is not modified, lock it for reading (aThat is "master" so locked
810 * first) */
811 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
812 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
813
814 /* this will back up current data */
815 m->bd.assignCopy(aThat->m->bd);
816
817 // Intentionally "forget" the NVRAM file since it must be unique and set
818 // to the correct value before the copy of the settings makes sense.
819 m->bd->strNvramPath.setNull();
820}
821
822void NvramStore::i_updateNonVolatileStorageFile(const Utf8Str &aNonVolatileStorageFile)
823{
824 /* sanity */
825 AutoCaller autoCaller(this);
826 AssertComRCReturnVoid(autoCaller.rc());
827
828 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
830
831 Utf8Str strTmp(aNonVolatileStorageFile);
832 if (strTmp == m->pParent->i_getDefaultNVRAMFilename())
833 strTmp.setNull();
834
835 if (strTmp == m->bd->strNvramPath)
836 return;
837
838 m->bd.backup();
839 m->bd->strNvramPath = strTmp;
840}
841
842#else
843//
844// private methods
845//
846/*static*/
847DECLCALLBACK(int) NvramStore::i_nvramStoreQuerySize(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
848 uint64_t *pcb)
849{
850 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
851
852 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
853 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
854 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
855 {
856 RTVFSFILE hVfsFile = it->second;
857 return RTVfsFileQuerySize(hVfsFile, pcb);
858 }
859
860 return VERR_NOT_FOUND;
861}
862
863
864/*static*/
865DECLCALLBACK(int) NvramStore::i_nvramStoreReadAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
866 void *pvBuf, size_t cbRead)
867{
868 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
869
870 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
871 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
872 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
873 {
874 RTVFSFILE hVfsFile = it->second;
875
876 int rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
877 AssertRC(rc); RT_NOREF(rc);
878
879 return RTVfsFileRead(hVfsFile, pvBuf, cbRead, NULL /*pcbRead*/);
880 }
881
882 return VERR_NOT_FOUND;
883}
884
885
886/*static*/
887DECLCALLBACK(int) NvramStore::i_nvramStoreWriteAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
888 const void *pvBuf, size_t cbWrite)
889{
890 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
891
892 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
893
894 int rc = VINF_SUCCESS;
895 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
896 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
897 {
898 RTVFSFILE hVfsFile = it->second;
899
900 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
901 AssertRC(rc);
902 rc = RTVfsFileSetSize(hVfsFile, cbWrite, RTVFSFILE_SIZE_F_NORMAL);
903 if (RT_SUCCESS(rc))
904 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbWrite, NULL /*pcbWritten*/);
905 }
906 else
907 {
908 /* Create a new entry. */
909 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
910 rc = RTVfsFileFromBuffer(RTFILE_O_READ | RTFILE_O_WRITE, pvBuf, cbWrite, &hVfsFile);
911 if (RT_SUCCESS(rc))
912 pThis->pNvramStore->m->bd->mapNvram[Utf8StrFmt("%s/%s", pszNamespace, pszPath)] = hVfsFile;
913 }
914
915 return rc;
916}
917
918
919/*static*/
920DECLCALLBACK(int) NvramStore::i_nvramStoreDelete(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath)
921{
922 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
923
924 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
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 pThis->pNvramStore->m->bd->mapNvram.erase(it);
930 RTVfsFileRelease(hVfsFile);
931 return VINF_SUCCESS;
932 }
933
934 return VERR_NOT_FOUND;
935}
936
937
938/**
939 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
940 */
941DECLCALLBACK(void *) NvramStore::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
942{
943 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
944 PDRVMAINNVRAMSTORE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
945
946 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
947 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVFSCONNECTOR, &pDrv->IVfs);
948 return NULL;
949}
950
951
952/**
953 * Destruct a NVRAM store driver instance.
954 *
955 * @returns VBox status code.
956 * @param pDrvIns The driver instance data.
957 */
958DECLCALLBACK(void) NvramStore::i_drvDestruct(PPDMDRVINS pDrvIns)
959{
960 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
961 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
962 LogFlow(("NvramStore::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
963
964 if (pThis->pNvramStore)
965 {
966 uint32_t cRefs = ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
967 if (!cRefs)
968 {
969 int rc = pThis->pNvramStore->i_saveStore();
970 AssertRC(rc); /** @todo Disk full error? */
971 }
972 }
973}
974
975
976/**
977 * Construct a NVRAM store driver instance.
978 *
979 * @copydoc FNPDMDRVCONSTRUCT
980 */
981DECLCALLBACK(int) NvramStore::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
982{
983 RT_NOREF(fFlags);
984 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
985 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
986 LogFlow(("NvramStore::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
987
988 /*
989 * Validate configuration.
990 */
991 if (!CFGMR3AreValuesValid(pCfg, ""))
992 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
993 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
994 ("Configuration error: Not possible to attach anything to this driver!\n"),
995 VERR_PDM_DRVINS_NO_ATTACH);
996
997 /*
998 * IBase.
999 */
1000 pDrvIns->IBase.pfnQueryInterface = NvramStore::i_drvQueryInterface;
1001
1002 pThis->IVfs.pfnQuerySize = NvramStore::i_nvramStoreQuerySize;
1003 pThis->IVfs.pfnReadAll = NvramStore::i_nvramStoreReadAll;
1004 pThis->IVfs.pfnWriteAll = NvramStore::i_nvramStoreWriteAll;
1005 pThis->IVfs.pfnDelete = NvramStore::i_nvramStoreDelete;
1006
1007 /*
1008 * Get the NVRAM store object pointer.
1009 */
1010 com::Guid uuid(COM_IIDOF(INvramStore));
1011 pThis->pNvramStore = (NvramStore *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
1012 if (!pThis->pNvramStore)
1013 {
1014 AssertMsgFailed(("Configuration error: No/bad NVRAM store object!\n"));
1015 return VERR_NOT_FOUND;
1016 }
1017
1018 uint32_t cRefs = ASMAtomicIncU32(&pThis->pNvramStore->m->cRefs);
1019 if (cRefs == 1)
1020 {
1021 int rc = pThis->pNvramStore->i_loadStore(pThis->pNvramStore->m->bd->strNvramPath.c_str());
1022 if (RT_FAILURE(rc))
1023 {
1024 ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1025 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1026 N_("Failed to load the NVRAM store from the file"));
1027 }
1028 }
1029
1030 return VINF_SUCCESS;
1031}
1032
1033
1034/**
1035 * NVRAM store driver registration record.
1036 */
1037const PDMDRVREG NvramStore::DrvReg =
1038{
1039 /* u32Version */
1040 PDM_DRVREG_VERSION,
1041 /* szName */
1042 "NvramStore",
1043 /* szRCMod */
1044 "",
1045 /* szR0Mod */
1046 "",
1047 /* pszDescription */
1048 "Main NVRAM store driver (Main as in the API).",
1049 /* fFlags */
1050 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1051 /* fClass. */
1052 PDM_DRVREG_CLASS_STATUS,
1053 /* cMaxInstances */
1054 ~0U,
1055 /* cbInstance */
1056 sizeof(DRVMAINNVRAMSTORE),
1057 /* pfnConstruct */
1058 NvramStore::i_drvConstruct,
1059 /* pfnDestruct */
1060 NvramStore::i_drvDestruct,
1061 /* pfnRelocate */
1062 NULL,
1063 /* pfnIOCtl */
1064 NULL,
1065 /* pfnPowerOn */
1066 NULL,
1067 /* pfnReset */
1068 NULL,
1069 /* pfnSuspend */
1070 NULL,
1071 /* pfnResume */
1072 NULL,
1073 /* pfnAttach */
1074 NULL,
1075 /* pfnDetach */
1076 NULL,
1077 /* pfnPowerOff */
1078 NULL,
1079 /* pfnSoftReset */
1080 NULL,
1081 /* u32EndVersion */
1082 PDM_DRVREG_VERSION
1083};
1084#endif /* !VBOX_COM_INPROC */
1085
1086/* 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