3ec523
From e2354416fdccb9649f080cdf912c5dc16d0eb578 Mon Sep 17 00:00:00 2001
3ec523
From: Michal Privoznik <mprivozn@redhat.com>
3ec523
Date: Thu, 7 Aug 2014 16:59:21 +0200
3ec523
Subject: [PATCH] qemu: Automatically create NVRAM store
3ec523
3ec523
When using split UEFI image, it may come handy if libvirt manages per
3ec523
domain _VARS file automatically. While the _CODE file is RO and can be
3ec523
shared among multiple domains, you certainly don't want to do that on
3ec523
the _VARS file. This latter one needs to be per domain. So at the
3ec523
domain startup process, if it's determined that domain needs _VARS
3ec523
file it's copied from this master _VARS file. The location of the
3ec523
master file is configurable in qemu.conf.
3ec523
3ec523
Temporary, on per domain basis the location of master NVRAM file can
3ec523
be overridden by this @template attribute I'm inventing to the
3ec523
<nvram/> element. All it does is holding path to the master NVRAM file
3ec523
from which local copy is created. If that's the case, the map in
3ec523
qemu.conf is not consulted.
3ec523
3ec523
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
3ec523
Acked-by: Laszlo Ersek <lersek@redhat.com>
3ec523
(cherry picked from commit 742b08e30fd503bc992e864828cbabd7e6a099ec)
3ec523
---
3ec523
 docs/formatdomain.html.in                          |  11 +-
3ec523
 docs/schemas/domaincommon.rng                      |   9 +-
3ec523
 libvirt.spec.in                                    |   2 +
3ec523
 src/Makefile.am                                    |   1 +
3ec523
 src/conf/domain_conf.c                             |  11 +-
3ec523
 src/conf/domain_conf.h                             |   1 +
3ec523
 src/qemu/libvirtd_qemu.aug                         |   3 +
3ec523
 src/qemu/qemu.conf                                 |  14 +++
3ec523
 src/qemu/qemu_conf.c                               |  94 ++++++++++++++
3ec523
 src/qemu/qemu_conf.h                               |   5 +
3ec523
 src/qemu/qemu_process.c                            | 137 +++++++++++++++++++++
3ec523
 src/qemu/test_libvirtd_qemu.aug.in                 |   3 +
3ec523
 tests/domainschemadata/domain-bios-nvram-empty.xml |  40 ++++++
3ec523
 13 files changed, 325 insertions(+), 6 deletions(-)
3ec523
 create mode 100644 tests/domainschemadata/domain-bios-nvram-empty.xml
3ec523
3ec523
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
3ec523
index 757035a..a2ea758 100644
3ec523
--- a/docs/formatdomain.html.in
3ec523
+++ b/docs/formatdomain.html.in
3ec523
@@ -103,7 +103,7 @@
3ec523
   <os>
3ec523
     <type>hvm</type>
3ec523
     <loader readonly='on' type='rom'>/usr/lib/xen/boot/hvmloader</loader>
3ec523
-    <nvram>/var/lib/libvirt/nvram/guest_VARS.fd</nvram>
3ec523
+    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/var/lib/libvirt/nvram/guest_VARS.fd</nvram>
3ec523
     <boot dev='hd'/>
3ec523
     <boot dev='cdrom'/>
3ec523
     <bootmenu enable='yes' timeout='3000'/>
3ec523
@@ -142,9 +142,12 @@
3ec523
         pflash.
3ec523
       
nvram
3ec523
       
Some UEFI firmwares may want to use a non-volatile memory to store
3ec523
-        some variables. In the host, this is represented as a file and the
3ec523
-        path to the file is stored in this element. Since
3ec523
-        1.2.8
3ec523
+        some variables. In the host, this is represented as a file and the path
3ec523
+        to the file is stored in this element. Moreover, when the domain is
3ec523
+        started up libvirt copies so called master NVRAM store file defined
3ec523
+        in qemu.conf. If needed, the template
3ec523
+        attribute can be used to per domain override map of master NVRAM stores
3ec523
+        from the config file. Since 1.2.8
3ec523
       
boot
3ec523
       
The dev attribute takes one of the values "fd", "hd",
3ec523
         "cdrom" or "network" and is used to specify the next boot device
3ec523
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
3ec523
index 5d9c21c..6ae940a 100644
3ec523
--- a/docs/schemas/domaincommon.rng
3ec523
+++ b/docs/schemas/domaincommon.rng
3ec523
@@ -263,7 +263,14 @@
3ec523
         </optional>
3ec523
         <optional>
3ec523
           <element name="nvram">
3ec523
-            <ref name="absFilePath"/>
3ec523
+            <optional>
3ec523
+              <attribute name="template">
3ec523
+                <ref name="absFilePath"/>
3ec523
+              </attribute>
3ec523
+            </optional>
3ec523
+            <optional>
3ec523
+              <ref name="absFilePath"/>
3ec523
+            </optional>
3ec523
           </element>
3ec523
         </optional>
3ec523
         <optional>
3ec523
diff --git a/libvirt.spec.in b/libvirt.spec.in
3ec523
index 6129f00..935b8c8 100644
3ec523
--- a/libvirt.spec.in
3ec523
+++ b/libvirt.spec.in
3ec523
@@ -1970,6 +1970,7 @@ exit 0
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/channel/
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/channel/target/
3ec523
+%dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/nvram/
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/cache/libvirt/qemu/
3ec523
 %{_datadir}/augeas/lenses/libvirtd_qemu.aug
3ec523
 %{_datadir}/augeas/lenses/tests/test_libvirtd_qemu.aug
3ec523
@@ -2072,6 +2073,7 @@ exit 0
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/channel/
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/channel/target/
3ec523
+%dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/nvram/
3ec523
 %dir %attr(0750, %{qemu_user}, %{qemu_group}) %{_localstatedir}/cache/libvirt/qemu/
3ec523
 %{_datadir}/augeas/lenses/libvirtd_qemu.aug
3ec523
 %{_datadir}/augeas/lenses/tests/test_libvirtd_qemu.aug
3ec523
diff --git a/src/Makefile.am b/src/Makefile.am
3ec523
index 46e411e..fa741a8 100644
3ec523
--- a/src/Makefile.am
3ec523
+++ b/src/Makefile.am
3ec523
@@ -2679,6 +2679,7 @@ endif WITH_SANLOCK
3ec523
 if WITH_QEMU
3ec523
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/qemu"
3ec523
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/qemu/channel/target"
3ec523
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/qemu/nvram"
3ec523
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/qemu"
3ec523
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt/qemu"
3ec523
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/log/libvirt/qemu"
3ec523
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
3ec523
index 6ee5c17..84f5f1d 100644
3ec523
--- a/src/conf/domain_conf.c
3ec523
+++ b/src/conf/domain_conf.c
3ec523
@@ -2023,6 +2023,7 @@ virDomainLoaderDefFree(virDomainLoaderDefPtr loader)
3ec523
 
3ec523
     VIR_FREE(loader->path);
3ec523
     VIR_FREE(loader->nvram);
3ec523
+    VIR_FREE(loader->templt);
3ec523
     VIR_FREE(loader);
3ec523
 }
3ec523
 
3ec523
@@ -12768,6 +12769,7 @@ virDomainDefParseXML(xmlDocPtr xml,
3ec523
                 goto error;
3ec523
 
3ec523
             def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt);
3ec523
+            def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt);
3ec523
         }
3ec523
     }
3ec523
 
3ec523
@@ -17866,7 +17868,14 @@ virDomainLoaderDefFormat(virBufferPtr buf,
3ec523
     virBufferAsprintf(buf, " type='%s'>", type);
3ec523
 
3ec523
     virBufferEscapeString(buf, "%s</loader>\n", loader->path);
3ec523
-    virBufferEscapeString(buf, "<nvram>%s</nvram>\n", loader->nvram);
3ec523
+    if (loader->nvram || loader->templt) {
3ec523
+        virBufferAddLit(buf, "
3ec523
+        virBufferEscapeString(buf, " template='%s'", loader->templt);
3ec523
+        if (loader->nvram)
3ec523
+            virBufferEscapeString(buf, ">%s</nvram>\n", loader->nvram);
3ec523
+        else
3ec523
+            virBufferAddLit(buf, "/>\n");
3ec523
+    }
3ec523
 }
3ec523
 
3ec523
 static bool
3ec523
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
3ec523
index c97a10c..3316fb6 100644
3ec523
--- a/src/conf/domain_conf.h
3ec523
+++ b/src/conf/domain_conf.h
3ec523
@@ -1644,6 +1644,7 @@ struct _virDomainLoaderDef {
3ec523
     int readonly;   /* enum virTristateBool */
3ec523
     virDomainLoader type;
3ec523
     char *nvram;    /* path to non-volatile RAM */
3ec523
+    char *templt;   /* user override of path to master nvram */
3ec523
 };
3ec523
 
3ec523
 void virDomainLoaderDefFree(virDomainLoaderDefPtr loader);
3ec523
diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug
3ec523
index e7db7fe..62951da 100644
3ec523
--- a/src/qemu/libvirtd_qemu.aug
3ec523
+++ b/src/qemu/libvirtd_qemu.aug
3ec523
@@ -88,6 +88,8 @@ module Libvirtd_qemu =
3ec523
 
3ec523
    let log_entry = bool_entry "log_timestamp"
3ec523
 
3ec523
+   let nvram_entry = str_array_entry "nvram"
3ec523
+
3ec523
    (* Each entry in the config is one of the following ... *)
3ec523
    let entry = vnc_entry
3ec523
              | spice_entry
3ec523
@@ -100,6 +102,7 @@ module Libvirtd_qemu =
3ec523
              | rpc_entry
3ec523
              | network_entry
3ec523
              | log_entry
3ec523
+             | nvram_entry
3ec523
 
3ec523
    let comment = [ label "#comment" . del /#[ \t]*/ "# " .  store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
3ec523
    let empty = [ label "#empty" . eol ]
3ec523
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
3ec523
index 7bbbe09..79bba36 100644
3ec523
--- a/src/qemu/qemu.conf
3ec523
+++ b/src/qemu/qemu.conf
3ec523
@@ -487,3 +487,17 @@
3ec523
 # Defaults to 1.
3ec523
 #
3ec523
 #log_timestamp = 0
3ec523
+
3ec523
+
3ec523
+# Location of master nvram file
3ec523
+#
3ec523
+# When a domain is configured to use UEFI instead of standard
3ec523
+# BIOS it may use a separate storage for UEFI variables. If
3ec523
+# that's the case libvirt creates the variable store per domain
3ec523
+# using this master file as image. Each UEFI firmware can,
3ec523
+# however, have different variables store. Therefore the nvram is
3ec523
+# a list of strings when a single item is in form of:
3ec523
+#   ${PATH_TO_UEFI_FW}:${PATH_TO_UEFI_VARS}.
3ec523
+# Later, when libvirt creates per domain variable store, this
3ec523
+# list is searched for the master image.
3ec523
+#nvram = [ "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd" ]
3ec523
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
3ec523
index e2ec54f..ac10b64 100644
3ec523
--- a/src/qemu/qemu_conf.c
3ec523
+++ b/src/qemu/qemu_conf.c
3ec523
@@ -107,6 +107,9 @@ void qemuDomainCmdlineDefFree(qemuDomainCmdlineDefPtr def)
3ec523
     VIR_FREE(def);
3ec523
 }
3ec523
 
3ec523
+#define VIR_QEMU_LOADER_FILE_PATH "/usr/share/OVMF/OVMF_CODE.fd"
3ec523
+#define VIR_QEMU_NVRAM_FILE_PATH "/usr/share/OVMF/OVMF_VARS.fd"
3ec523
+
3ec523
 virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
3ec523
 {
3ec523
     virQEMUDriverConfigPtr cfg;
3ec523
@@ -255,6 +258,15 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
3ec523
 
3ec523
     cfg->logTimestamp = true;
3ec523
 
3ec523
+    if (VIR_ALLOC_N(cfg->loader, 1) < 0 ||
3ec523
+        VIR_ALLOC_N(cfg->nvram, 1) < 0)
3ec523
+        goto error;
3ec523
+    cfg->nloader = 1;
3ec523
+
3ec523
+    if (VIR_STRDUP(cfg->loader[0], VIR_QEMU_LOADER_FILE_PATH) < 0 ||
3ec523
+        VIR_STRDUP(cfg->nvram[0], VIR_QEMU_NVRAM_FILE_PATH) < 0)
3ec523
+        goto error;
3ec523
+
3ec523
     return cfg;
3ec523
 
3ec523
  error:
3ec523
@@ -305,6 +317,14 @@ static void virQEMUDriverConfigDispose(void *obj)
3ec523
     virStringFreeList(cfg->securityDriverNames);
3ec523
 
3ec523
     VIR_FREE(cfg->lockManagerName);
3ec523
+
3ec523
+    while (cfg->nloader) {
3ec523
+        VIR_FREE(cfg->loader[cfg->nloader - 1]);
3ec523
+        VIR_FREE(cfg->nvram[cfg->nloader - 1]);
3ec523
+        cfg->nloader--;
3ec523
+    }
3ec523
+    VIR_FREE(cfg->loader);
3ec523
+    VIR_FREE(cfg->nvram);
3ec523
 }
3ec523
 
3ec523
 
3ec523
@@ -328,6 +348,43 @@ virQEMUDriverConfigHugeTLBFSInit(virHugeTLBFSPtr hugetlbfs,
3ec523
 }
3ec523
 
3ec523
 
3ec523
+static int
3ec523
+virQEMUDriverConfigNVRAMParse(const char *str,
3ec523
+                              char **loader,
3ec523
+                              char **nvram)
3ec523
+{
3ec523
+    int ret = -1;
3ec523
+    char **token;
3ec523
+
3ec523
+    if (!(token = virStringSplit(str, ":", 0)))
3ec523
+        goto cleanup;
3ec523
+
3ec523
+    if (token[0]) {
3ec523
+        virSkipSpaces((const char **) &token[0]);
3ec523
+        if (token[1])
3ec523
+            virSkipSpaces((const char **) &token[1]);
3ec523
+    }
3ec523
+
3ec523
+    /* Exactly two tokens are expected */
3ec523
+    if (!token[0] || !token[1] || token[2] ||
3ec523
+        STREQ(token[0], "") || STREQ(token[1], "")) {
3ec523
+        virReportError(VIR_ERR_CONF_SYNTAX,
3ec523
+                       _("Invalid nvram format: '%s'"),
3ec523
+                       str);
3ec523
+        goto cleanup;
3ec523
+    }
3ec523
+
3ec523
+    if (VIR_STRDUP(*loader, token[0]) < 0 ||
3ec523
+        VIR_STRDUP(*nvram, token[1]) < 0)
3ec523
+        goto cleanup;
3ec523
+
3ec523
+    ret = 0;
3ec523
+ cleanup:
3ec523
+    virStringFreeList(token);
3ec523
+    return ret;
3ec523
+}
3ec523
+
3ec523
+
3ec523
 int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
3ec523
                                 const char *filename)
3ec523
 {
3ec523
@@ -654,6 +711,43 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
3ec523
 
3ec523
     GET_VALUE_BOOL("log_timestamp", cfg->logTimestamp);
3ec523
 
3ec523
+    if ((p = virConfGetValue(conf, "nvram"))) {
3ec523
+        size_t len;
3ec523
+        virConfValuePtr pp;
3ec523
+
3ec523
+        CHECK_TYPE("nvram", VIR_CONF_LIST);
3ec523
+
3ec523
+        while (cfg->nloader) {
3ec523
+            VIR_FREE(cfg->loader[cfg->nloader - 1]);
3ec523
+            VIR_FREE(cfg->nvram[cfg->nloader - 1]);
3ec523
+            cfg->nloader--;
3ec523
+        }
3ec523
+        VIR_FREE(cfg->loader);
3ec523
+        VIR_FREE(cfg->nvram);
3ec523
+
3ec523
+        /* Calc length and check items */
3ec523
+        for (len = 0, pp = p->list; pp; len++, pp = pp->next) {
3ec523
+            if (pp->type != VIR_CONF_STRING) {
3ec523
+                virReportError(VIR_ERR_CONF_SYNTAX, "%s",
3ec523
+                               _("nvram must be a list of strings"));
3ec523
+                goto cleanup;
3ec523
+            }
3ec523
+        }
3ec523
+
3ec523
+        if (len &&
3ec523
+            (VIR_ALLOC_N(cfg->loader, len) < 0 ||
3ec523
+             VIR_ALLOC_N(cfg->nvram, len) < 0))
3ec523
+            goto cleanup;
3ec523
+        cfg->nloader = len;
3ec523
+
3ec523
+        for (i = 0, pp = p->list; pp; i++, pp = pp->next) {
3ec523
+            if (virQEMUDriverConfigNVRAMParse(pp->str,
3ec523
+                                              &cfg->loader[i],
3ec523
+                                              &cfg->nvram[i]) < 0)
3ec523
+                goto cleanup;
3ec523
+        }
3ec523
+    }
3ec523
+
3ec523
     ret = 0;
3ec523
 
3ec523
  cleanup:
3ec523
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
3ec523
index ae7ac56..1f521e5 100644
3ec523
--- a/src/qemu/qemu_conf.h
3ec523
+++ b/src/qemu/qemu_conf.h
3ec523
@@ -172,6 +172,11 @@ struct _virQEMUDriverConfig {
3ec523
     int migrationPortMax;
3ec523
 
3ec523
     bool logTimestamp;
3ec523
+
3ec523
+    /* Pairs of loader:nvram paths. The list is @nloader items long */
3ec523
+    char **loader;
3ec523
+    char **nvram;
3ec523
+    size_t nloader;
3ec523
 };
3ec523
 
3ec523
 /* Main driver state */
3ec523
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
3ec523
index f68dfbe..5b120d4 100644
3ec523
--- a/src/qemu/qemu_process.c
3ec523
+++ b/src/qemu/qemu_process.c
3ec523
@@ -67,6 +67,7 @@
3ec523
 #include "virstring.h"
3ec523
 #include "virhostdev.h"
3ec523
 #include "storage/storage_driver.h"
3ec523
+#include "configmake.h"
3ec523
 
3ec523
 #define VIR_FROM_THIS VIR_FROM_QEMU
3ec523
 
3ec523
@@ -3734,6 +3735,135 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver,
3ec523
 }
3ec523
 
3ec523
 
3ec523
+static int
3ec523
+qemuPrepareNVRAM(virQEMUDriverConfigPtr cfg,
3ec523
+                 virDomainDefPtr def,
3ec523
+                 bool migrated)
3ec523
+{
3ec523
+    int ret = -1;
3ec523
+    int srcFD = -1;
3ec523
+    int dstFD = -1;
3ec523
+    virDomainLoaderDefPtr loader = def->os.loader;
3ec523
+    bool generated = false;
3ec523
+    bool created = false;
3ec523
+
3ec523
+    /* Unless domain has RO loader of pflash type, we have
3ec523
+     * nothing to do here.  If the loader is RW then it's not
3ec523
+     * using split code and vars feature, so no nvram file needs
3ec523
+     * to be created. */
3ec523
+    if (!loader || loader->type != VIR_DOMAIN_LOADER_TYPE_PFLASH ||
3ec523
+        loader->readonly != VIR_TRISTATE_SWITCH_ON)
3ec523
+        return 0;
3ec523
+
3ec523
+    /* If the nvram path is configured already, there's nothing
3ec523
+     * we need to do. Unless we are starting the destination side
3ec523
+     * of migration in which case nvram is configured in the
3ec523
+     * domain XML but the file doesn't exist yet. Moreover, after
3ec523
+     * the migration is completed, qemu will invoke a
3ec523
+     * synchronization write into the nvram file so we don't have
3ec523
+     * to take care about transmitting the real data on the other
3ec523
+     * side. */
3ec523
+    if (loader->nvram && !migrated)
3ec523
+        return 0;
3ec523
+
3ec523
+    /* Autogenerate nvram path if needed.*/
3ec523
+    if (!loader->nvram) {
3ec523
+        if (virAsprintf(&loader->nvram,
3ec523
+                        "%s/lib/libvirt/qemu/nvram/%s_VARS.fd",
3ec523
+                        LOCALSTATEDIR, def->name) < 0)
3ec523
+            goto cleanup;
3ec523
+
3ec523
+        generated = true;
3ec523
+    }
3ec523
+
3ec523
+    if (!virFileExists(loader->nvram)) {
3ec523
+        const char *master_nvram_path = loader->templt;
3ec523
+        ssize_t r;
3ec523
+
3ec523
+        if (!loader->templt) {
3ec523
+            size_t i;
3ec523
+            for (i = 0; i < cfg->nloader; i++) {
3ec523
+                if (STREQ(cfg->loader[i], loader->path)) {
3ec523
+                    master_nvram_path = cfg->nvram[i];
3ec523
+                    break;
3ec523
+                }
3ec523
+            }
3ec523
+        }
3ec523
+
3ec523
+        if (!master_nvram_path) {
3ec523
+            virReportError(VIR_ERR_OPERATION_FAILED,
3ec523
+                           _("unable to find any master var store for "
3ec523
+                             "loader: %s"), loader->path);
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+
3ec523
+        if ((srcFD = virFileOpenAs(master_nvram_path, O_RDONLY,
3ec523
+                                   0, -1, -1, 0)) < 0) {
3ec523
+            virReportSystemError(-srcFD,
3ec523
+                                 _("Failed to open file '%s'"),
3ec523
+                                 master_nvram_path);
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+        if ((dstFD = virFileOpenAs(loader->nvram,
3ec523
+                                   O_WRONLY | O_CREAT | O_EXCL,
3ec523
+                                   S_IRUSR | S_IWUSR,
3ec523
+                                   cfg->user, cfg->group, 0)) < 0) {
3ec523
+            virReportSystemError(-dstFD,
3ec523
+                                 _("Failed to create file '%s'"),
3ec523
+                                 loader->nvram);
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+        created = true;
3ec523
+
3ec523
+        do {
3ec523
+            char buf[1024];
3ec523
+
3ec523
+            if ((r = saferead(srcFD, buf, sizeof(buf))) < 0) {
3ec523
+                virReportSystemError(errno,
3ec523
+                                     _("Unable to read from file '%s'"),
3ec523
+                                     master_nvram_path);
3ec523
+                goto cleanup;
3ec523
+            }
3ec523
+
3ec523
+            if (safewrite(dstFD, buf, r) < 0) {
3ec523
+                virReportSystemError(errno,
3ec523
+                                     _("Unable to write to file '%s'"),
3ec523
+                                     loader->nvram);
3ec523
+                goto cleanup;
3ec523
+            }
3ec523
+        } while (r);
3ec523
+
3ec523
+        if (VIR_CLOSE(srcFD) < 0) {
3ec523
+            virReportSystemError(errno,
3ec523
+                                 _("Unable to close file '%s'"),
3ec523
+                                 master_nvram_path);
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+        if (VIR_CLOSE(dstFD) < 0) {
3ec523
+            virReportSystemError(errno,
3ec523
+                                 _("Unable to close file '%s'"),
3ec523
+                                 loader->nvram);
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+    }
3ec523
+
3ec523
+    ret = 0;
3ec523
+ cleanup:
3ec523
+    /* We successfully generated the nvram path, but failed to
3ec523
+     * copy the file content. Roll back. */
3ec523
+    if (ret < 0) {
3ec523
+        if (created)
3ec523
+            unlink(loader->nvram);
3ec523
+        if (generated)
3ec523
+            VIR_FREE(loader->nvram);
3ec523
+    }
3ec523
+
3ec523
+    VIR_FORCE_CLOSE(srcFD);
3ec523
+    VIR_FORCE_CLOSE(dstFD);
3ec523
+    return ret;
3ec523
+}
3ec523
+
3ec523
+
3ec523
 int qemuProcessStart(virConnectPtr conn,
3ec523
                      virQEMUDriverPtr driver,
3ec523
                      virDomainObjPtr vm,
3ec523
@@ -3802,6 +3932,13 @@ int qemuProcessStart(virConnectPtr conn,
3ec523
     if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
3ec523
         goto cleanup;
3ec523
 
3ec523
+    /* Some things, paths, ... are generated here and we want them to persist.
3ec523
+     * Fill them in prior to setting the domain def as transient. */
3ec523
+    VIR_DEBUG("Generating paths");
3ec523
+
3ec523
+    if (qemuPrepareNVRAM(cfg, vm->def, migrateFrom) < 0)
3ec523
+        goto cleanup;
3ec523
+
3ec523
     /* Do this upfront, so any part of the startup process can add
3ec523
      * runtime state to vm->def that won't be persisted. This let's us
3ec523
      * report implicit runtime defaults in the XML, like vnc listen/socket
3ec523
diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in
3ec523
index 7796acc..d2bc2c0 100644
3ec523
--- a/src/qemu/test_libvirtd_qemu.aug.in
3ec523
+++ b/src/qemu/test_libvirtd_qemu.aug.in
3ec523
@@ -74,3 +74,6 @@ module Test_libvirtd_qemu =
3ec523
 { "migration_port_min" = "49152" }
3ec523
 { "migration_port_max" = "49215" }
3ec523
 { "log_timestamp" = "0" }
3ec523
+{ "nvram"
3ec523
+    { "1" = "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd" }
3ec523
+}
3ec523
diff --git a/tests/domainschemadata/domain-bios-nvram-empty.xml b/tests/domainschemadata/domain-bios-nvram-empty.xml
3ec523
new file mode 100644
3ec523
index 0000000..e7643f3
3ec523
--- /dev/null
3ec523
+++ b/tests/domainschemadata/domain-bios-nvram-empty.xml
3ec523
@@ -0,0 +1,40 @@
3ec523
+<domain type='qemu'>
3ec523
+  <name>test-bios</name>
3ec523
+  <uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
3ec523
+  <memory unit='KiB'>1048576</memory>
3ec523
+  <currentMemory unit='KiB'>1048576</currentMemory>
3ec523
+  <vcpu placement='static'>1</vcpu>
3ec523
+  <os>
3ec523
+    <type arch='x86_64' machine='pc'>hvm</type>
3ec523
+    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
3ec523
+    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'/>
3ec523
+    <boot dev='hd'/>
3ec523
+    <bootmenu enable='yes'/>
3ec523
+  </os>
3ec523
+  <features>
3ec523
+    <acpi/>
3ec523
+  </features>
3ec523
+  <clock offset='utc'/>
3ec523
+  <on_poweroff>destroy</on_poweroff>
3ec523
+  <on_reboot>restart</on_reboot>
3ec523
+  <on_crash>restart</on_crash>
3ec523
+  <devices>
3ec523
+    <emulator>/usr/bin/qemu</emulator>
3ec523
+    <disk type='block' device='disk'>
3ec523
+      <source dev='/dev/HostVG/QEMUGuest1'/>
3ec523
+      <target dev='hda' bus='ide'/>
3ec523
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
3ec523
+    </disk>
3ec523
+    <controller type='usb' index='0'/>
3ec523
+    <controller type='ide' index='0'/>
3ec523
+    <controller type='pci' index='0' model='pci-root'/>
3ec523
+    <serial type='pty'>
3ec523
+      <target port='0'/>
3ec523
+    </serial>
3ec523
+    <console type='pty'>
3ec523
+      <target type='serial' port='0'/>
3ec523
+    </console>
3ec523
+    <input type='tablet' bus='usb'/>
3ec523
+    <memballoon model='virtio'/>
3ec523
+  </devices>
3ec523
+</domain>