VirtualBox

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

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

Main/NvramStore: Don't try to save the NVRAM store when the filepath is empty, bugref:10191

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