VirtualBox

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

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

Main: Initialize the TPM type properly based on the guest OS type and also initialize the UEFI variable store based on the guest OS type secure boot setting, bugref:9580 and bugref:10075

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.5 KB
Line 
1/* $Id: NvramStoreImpl.cpp 91614 2021-10-07 10:12:16Z 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 "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 /*
663 * Skip creating the tar archive if only the UEFI NVRAM content is available in order
664 * to maintain backwards compatibility. As soon as there is more than one entry or
665 * it doesn't belong to the UEFI the tar archive will be created.
666 */
667 int rc = VINF_SUCCESS;
668
669 Utf8Str strTmp;
670 NvramStore::getNonVolatileStorageFile(strTmp);
671
672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
673 if ( m->bd->mapNvram.size() == 1
674 && m->bd->mapNvram.find(Utf8Str("efi/nvram")) != m->bd->mapNvram.end())
675 {
676 RTVFSFILE hVfsFileNvram = m->bd->mapNvram[Utf8Str("efi/nvram")];
677
678 rc = RTVfsFileSeek(hVfsFileNvram, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
679 AssertRC(rc); RT_NOREF(rc);
680
681 RTVFSIOSTREAM hVfsIosDst;
682 rc = RTVfsIoStrmOpenNormal(strTmp.c_str(), RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
683 &hVfsIosDst);
684 if (RT_SUCCESS(rc))
685 {
686 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsFileNvram);
687 Assert(hVfsIosSrc != NIL_RTVFSIOSTREAM);
688
689 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0 /*cbBufHint*/);
690
691 RTVfsIoStrmRelease(hVfsIosSrc);
692 RTVfsIoStrmRelease(hVfsIosDst);
693 }
694 }
695 else if (m->bd->mapNvram.size())
696 rc = i_saveStoreAsTar(strTmp.c_str());
697 /* else: No NVRAM content to store so we are done here. */
698
699 return rc;
700}
701
702
703#ifndef VBOX_COM_INPROC
704HRESULT NvramStore::i_retainUefiVarStore(PRTVFS phVfs, bool fReadonly)
705{
706 /* the machine needs to be mutable */
707 AutoMutableStateDependency adep(m->pParent);
708 if (FAILED(adep.rc())) return adep.rc();
709
710 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
711
712 HRESULT hrc = S_OK;
713 NvramStoreIter it = m->bd->mapNvram.find("efi/nvram");
714 if (it != m->bd->mapNvram.end())
715 {
716 RTVFSFILE hVfsFileNvram = it->second;
717 RTVFS hVfsEfiVarStore;
718 uint32_t fMntFlags = fReadonly ? RTVFSMNT_F_READ_ONLY : 0;
719
720 int vrc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, fMntFlags, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore,
721 NULL /*pErrInfo*/);
722 if (RT_SUCCESS(vrc))
723 {
724 *phVfs = hVfsEfiVarStore;
725 if (!fReadonly)
726 m->pParent->i_setModified(Machine::IsModified_NvramStore);
727 }
728 else
729 hrc = setError(E_FAIL, tr("Opening the UEFI variable store failed (%Rrc)."), vrc);
730 }
731 else
732 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("The UEFI NVRAM file is not existing for this machine."));
733
734 return hrc;
735}
736
737
738HRESULT NvramStore::i_releaseUefiVarStore(RTVFS hVfs)
739{
740 RTVfsRelease(hVfs);
741 return S_OK;
742}
743
744
745/**
746 * Loads settings from the given machine node.
747 * May be called once right after this object creation.
748 *
749 * @param data Configuration settings.
750 *
751 * @note Locks this object for writing.
752 */
753HRESULT NvramStore::i_loadSettings(const settings::NvramSettings &data)
754{
755 AutoCaller autoCaller(this);
756 AssertComRCReturnRC(autoCaller.rc());
757
758 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
760
761 m->bd->strNvramPath = data.strNvramPath;
762
763 Utf8Str strTmp(m->bd->strNvramPath);
764 if (strTmp.isNotEmpty())
765 m->pParent->i_copyPathRelativeToMachine(strTmp, m->bd->strNvramPath);
766 if ( m->pParent->i_getFirmwareType() == FirmwareType_BIOS
767 || m->bd->strNvramPath == m->pParent->i_getDefaultNVRAMFilename())
768 m->bd->strNvramPath.setNull();
769
770 return S_OK;
771}
772
773/**
774 * Saves settings to the given machine node.
775 *
776 * @param data Configuration settings.
777 *
778 * @note Locks this object for writing.
779 */
780HRESULT NvramStore::i_saveSettings(settings::NvramSettings &data)
781{
782 AutoCaller autoCaller(this);
783 AssertComRCReturnRC(autoCaller.rc());
784
785 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
786
787 data.strNvramPath = m->bd->strNvramPath;
788
789 int vrc = i_saveStore();
790 if (RT_FAILURE(vrc))
791 return setError(E_FAIL, tr("Failed to save the NVRAM content to disk (%Rrc)"), vrc);
792
793 return S_OK;
794}
795
796void NvramStore::i_rollback()
797{
798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
799 m->bd.rollback();
800}
801
802void NvramStore::i_commit()
803{
804 /* sanity */
805 AutoCaller autoCaller(this);
806 AssertComRCReturnVoid(autoCaller.rc());
807
808 /* sanity too */
809 AutoCaller peerCaller(m->pPeer);
810 AssertComRCReturnVoid(peerCaller.rc());
811
812 /* lock both for writing since we modify both (mPeer is "master" so locked
813 * first) */
814 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
815
816 if (m->bd.isBackedUp())
817 {
818 m->bd.commit();
819 if (m->pPeer)
820 {
821 /* attach new data to the peer and reshare it */
822 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
823 m->pPeer->m->bd.attach(m->bd);
824 }
825 }
826}
827
828void NvramStore::i_copyFrom(NvramStore *aThat)
829{
830 AssertReturnVoid(aThat != NULL);
831
832 /* sanity */
833 AutoCaller autoCaller(this);
834 AssertComRCReturnVoid(autoCaller.rc());
835
836 /* sanity too */
837 AutoCaller thatCaller(aThat);
838 AssertComRCReturnVoid(thatCaller.rc());
839
840 /* peer is not modified, lock it for reading (aThat is "master" so locked
841 * first) */
842 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
843 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
844
845 /* this will back up current data */
846 m->bd.assignCopy(aThat->m->bd);
847
848 // Intentionally "forget" the NVRAM file since it must be unique and set
849 // to the correct value before the copy of the settings makes sense.
850 m->bd->strNvramPath.setNull();
851}
852
853HRESULT NvramStore::i_applyDefaults(GuestOSType *aOSType)
854{
855 HRESULT hrc = S_OK;
856
857 if (aOSType->i_recommendedEFISecureBoot())
858 {
859 /* Initialize the UEFI variable store and enroll default keys. */
860 hrc = initUefiVariableStore(0 /*aSize*/);
861 if (SUCCEEDED(hrc))
862 {
863 ComPtr<IUefiVariableStore> pVarStore;
864
865 hrc = getUefiVariableStore(pVarStore);
866 if (SUCCEEDED(hrc))
867 {
868 hrc = pVarStore->EnrollOraclePlatformKey();
869 if (SUCCEEDED(hrc))
870 hrc = pVarStore->EnrollDefaultMsSignatures();
871 }
872 }
873 }
874
875 return hrc;
876}
877
878void NvramStore::i_updateNonVolatileStorageFile(const Utf8Str &aNonVolatileStorageFile)
879{
880 /* sanity */
881 AutoCaller autoCaller(this);
882 AssertComRCReturnVoid(autoCaller.rc());
883
884 AutoReadLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
886
887 Utf8Str strTmp(aNonVolatileStorageFile);
888 if (strTmp == m->pParent->i_getDefaultNVRAMFilename())
889 strTmp.setNull();
890
891 if (strTmp == m->bd->strNvramPath)
892 return;
893
894 m->bd.backup();
895 m->bd->strNvramPath = strTmp;
896}
897
898#else
899//
900// private methods
901//
902/*static*/
903DECLCALLBACK(int) NvramStore::i_nvramStoreQuerySize(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
904 uint64_t *pcb)
905{
906 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
907
908 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
909 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
910 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
911 {
912 RTVFSFILE hVfsFile = it->second;
913 return RTVfsFileQuerySize(hVfsFile, pcb);
914 }
915
916 return VERR_NOT_FOUND;
917}
918
919
920/*static*/
921DECLCALLBACK(int) NvramStore::i_nvramStoreReadAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
922 void *pvBuf, size_t cbRead)
923{
924 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
925
926 AutoReadLock rlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
927 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
928 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
929 {
930 RTVFSFILE hVfsFile = it->second;
931
932 int rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
933 AssertRC(rc); RT_NOREF(rc);
934
935 return RTVfsFileRead(hVfsFile, pvBuf, cbRead, NULL /*pcbRead*/);
936 }
937
938 return VERR_NOT_FOUND;
939}
940
941
942/*static*/
943DECLCALLBACK(int) NvramStore::i_nvramStoreWriteAll(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath,
944 const void *pvBuf, size_t cbWrite)
945{
946 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
947
948 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
949
950 int rc = VINF_SUCCESS;
951 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
952 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
953 {
954 RTVFSFILE hVfsFile = it->second;
955
956 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
957 AssertRC(rc);
958 rc = RTVfsFileSetSize(hVfsFile, cbWrite, RTVFSFILE_SIZE_F_NORMAL);
959 if (RT_SUCCESS(rc))
960 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbWrite, NULL /*pcbWritten*/);
961 }
962 else
963 {
964 /* Create a new entry. */
965 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
966 rc = RTVfsFileFromBuffer(RTFILE_O_READ | RTFILE_O_WRITE, pvBuf, cbWrite, &hVfsFile);
967 if (RT_SUCCESS(rc))
968 pThis->pNvramStore->m->bd->mapNvram[Utf8StrFmt("%s/%s", pszNamespace, pszPath)] = hVfsFile;
969 }
970
971 return rc;
972}
973
974
975/*static*/
976DECLCALLBACK(int) NvramStore::i_nvramStoreDelete(PPDMIVFSCONNECTOR pInterface, const char *pszNamespace, const char *pszPath)
977{
978 PDRVMAINNVRAMSTORE pThis = RT_FROM_MEMBER(pInterface, DRVMAINNVRAMSTORE, IVfs);
979
980 AutoWriteLock wlock(pThis->pNvramStore COMMA_LOCKVAL_SRC_POS);
981 NvramStoreIter it = pThis->pNvramStore->m->bd->mapNvram.find(Utf8StrFmt("%s/%s", pszNamespace, pszPath));
982 if (it != pThis->pNvramStore->m->bd->mapNvram.end())
983 {
984 RTVFSFILE hVfsFile = it->second;
985 pThis->pNvramStore->m->bd->mapNvram.erase(it);
986 RTVfsFileRelease(hVfsFile);
987 return VINF_SUCCESS;
988 }
989
990 return VERR_NOT_FOUND;
991}
992
993
994/**
995 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
996 */
997DECLCALLBACK(void *) NvramStore::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
998{
999 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1000 PDRVMAINNVRAMSTORE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1001
1002 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1003 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVFSCONNECTOR, &pDrv->IVfs);
1004 return NULL;
1005}
1006
1007
1008/**
1009 * Destruct a NVRAM store driver instance.
1010 *
1011 * @returns VBox status code.
1012 * @param pDrvIns The driver instance data.
1013 */
1014DECLCALLBACK(void) NvramStore::i_drvDestruct(PPDMDRVINS pDrvIns)
1015{
1016 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1017 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1018 LogFlow(("NvramStore::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
1019
1020 if (pThis->pNvramStore)
1021 {
1022 uint32_t cRefs = ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1023 if (!cRefs)
1024 {
1025 int rc = pThis->pNvramStore->i_saveStore();
1026 AssertRC(rc); /** @todo Disk full error? */
1027 }
1028 }
1029}
1030
1031
1032/**
1033 * Construct a NVRAM store driver instance.
1034 *
1035 * @copydoc FNPDMDRVCONSTRUCT
1036 */
1037DECLCALLBACK(int) NvramStore::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1038{
1039 RT_NOREF(fFlags);
1040 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1041 PDRVMAINNVRAMSTORE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINNVRAMSTORE);
1042 LogFlow(("NvramStore::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
1043
1044 /*
1045 * Validate configuration.
1046 */
1047 if (!CFGMR3AreValuesValid(pCfg, ""))
1048 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1049 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1050 ("Configuration error: Not possible to attach anything to this driver!\n"),
1051 VERR_PDM_DRVINS_NO_ATTACH);
1052
1053 /*
1054 * IBase.
1055 */
1056 pDrvIns->IBase.pfnQueryInterface = NvramStore::i_drvQueryInterface;
1057
1058 pThis->IVfs.pfnQuerySize = NvramStore::i_nvramStoreQuerySize;
1059 pThis->IVfs.pfnReadAll = NvramStore::i_nvramStoreReadAll;
1060 pThis->IVfs.pfnWriteAll = NvramStore::i_nvramStoreWriteAll;
1061 pThis->IVfs.pfnDelete = NvramStore::i_nvramStoreDelete;
1062
1063 /*
1064 * Get the NVRAM store object pointer.
1065 */
1066 com::Guid uuid(COM_IIDOF(INvramStore));
1067 pThis->pNvramStore = (NvramStore *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
1068 if (!pThis->pNvramStore)
1069 {
1070 AssertMsgFailed(("Configuration error: No/bad NVRAM store object!\n"));
1071 return VERR_NOT_FOUND;
1072 }
1073
1074 uint32_t cRefs = ASMAtomicIncU32(&pThis->pNvramStore->m->cRefs);
1075 if (cRefs == 1)
1076 {
1077 int rc = pThis->pNvramStore->i_loadStore(pThis->pNvramStore->m->bd->strNvramPath.c_str());
1078 if (RT_FAILURE(rc))
1079 {
1080 ASMAtomicDecU32(&pThis->pNvramStore->m->cRefs);
1081 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1082 N_("Failed to load the NVRAM store from the file"));
1083 }
1084 }
1085
1086 return VINF_SUCCESS;
1087}
1088
1089
1090/**
1091 * NVRAM store driver registration record.
1092 */
1093const PDMDRVREG NvramStore::DrvReg =
1094{
1095 /* u32Version */
1096 PDM_DRVREG_VERSION,
1097 /* szName */
1098 "NvramStore",
1099 /* szRCMod */
1100 "",
1101 /* szR0Mod */
1102 "",
1103 /* pszDescription */
1104 "Main NVRAM store driver (Main as in the API).",
1105 /* fFlags */
1106 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1107 /* fClass. */
1108 PDM_DRVREG_CLASS_STATUS,
1109 /* cMaxInstances */
1110 ~0U,
1111 /* cbInstance */
1112 sizeof(DRVMAINNVRAMSTORE),
1113 /* pfnConstruct */
1114 NvramStore::i_drvConstruct,
1115 /* pfnDestruct */
1116 NvramStore::i_drvDestruct,
1117 /* pfnRelocate */
1118 NULL,
1119 /* pfnIOCtl */
1120 NULL,
1121 /* pfnPowerOn */
1122 NULL,
1123 /* pfnReset */
1124 NULL,
1125 /* pfnSuspend */
1126 NULL,
1127 /* pfnResume */
1128 NULL,
1129 /* pfnAttach */
1130 NULL,
1131 /* pfnDetach */
1132 NULL,
1133 /* pfnPowerOff */
1134 NULL,
1135 /* pfnSoftReset */
1136 NULL,
1137 /* u32EndVersion */
1138 PDM_DRVREG_VERSION
1139};
1140#endif /* !VBOX_COM_INPROC */
1141
1142/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use