render / rpms / libvirt

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