]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
conf: Parse and format varstore element
authorAndrea Bolognani <abologna@redhat.com>
Mon, 19 Jan 2026 13:20:06 +0000 (14:20 +0100)
committerAndrea Bolognani <abologna@redhat.com>
Tue, 24 Feb 2026 10:29:06 +0000 (11:29 +0100)
This will be used to configure the backing storage used by the
uefi-vars QEMU device.

Dealing with the element itself is trivial, however we have to
refactor the existing code which deals with the loader and nvram
elements slightly: in particular, we can no longer perform an
early exit if those elements are absent.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
docs/formatdomain.rst
docs/kbase/secureboot.rst
src/conf/domain_conf.c
src/conf/domain_conf.h
src/conf/schemas/domaincommon.rng
src/conf/virconftypes.h
src/libvirt_private.syms

index db664857af0fd90af724d1b4504d20df9fe49f39..b4e28e99ef1a2c0dc79f5155b48c14e6240dee67 100644 (file)
@@ -196,9 +196,9 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
 
 ``firmware``
    The ``firmware`` attribute allows management applications to automatically
-   fill ``<loader/>`` and ``<nvram/>`` elements and possibly enable some
-   features required by selected firmware. Accepted values are ``bios`` and
-   ``efi``.
+   fill ``<loader/>`` and ``<nvram/>`` or ``<varstore/>`` elements and possibly
+   enable some features required by selected firmware. Accepted values are
+   ``bios`` and ``efi``.
    The selection process scans for files describing installed firmware images in
    specified location and uses the most specific one which fulfills domain
    requirements. The locations in order of preference (from generic to most
@@ -311,6 +311,23 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
    It is not valid to provide this element if the loader is marked as
    stateless.
 
+``varstore``
+   This works much the same way as the ``<nvram/>`` element described above,
+   except that variable storage is handled by the ``uefi-vars`` QEMU device
+   instead of being backed by a pflash device. :since:`Since 12.1.0 (QEMU only)`
+
+   The ``path`` attribute contains the path of the domain-specific file where
+   variables are stored, while the ``template`` attribute points to a template
+   that the domain-specific file can be (re)generated from. Assuming that the
+   necessary JSON firmware descriptor files are present, both attributes will
+   be filled in automatically by libvirt.
+
+   Using ``<varstore/>`` instead of ``<nvram/>`` is particularly useful on
+   non-x86 architectures such as aarch64, where it represents the only way to
+   get Secure Boot working. It can be used on x86 too, and doing so will make
+   it possible to keep UEFI authenticated variables safe from tampering without
+   requiring the use of SMM emulation.
+
 ``boot``
    The ``dev`` attribute takes one of the values "fd", "hd", "cdrom" or
    "network" and is used to specify the next boot device to consider. The
index 6c22b08d22fc819f481619387b39409dff3232bd..b411b65f004da8ce9268ae78c61faf5b03011b1c 100644 (file)
@@ -74,8 +74,8 @@ Changing an existing VM
 
 When a VM is defined, libvirt will pick the firmware that best
 satisfies the provided criteria and record this information for use
-on subsequent boots. The resulting XML configuration will look like
-this:
+on subsequent boots. The resulting XML configuration will look either
+like this:
 
 ::
 
@@ -88,14 +88,28 @@ this:
     <nvram template='/usr/share/edk2/ovmf/OVMF_VARS.secboot.fd'>/var/lib/libvirt/qemu/nvram/vm_VARS.fd</nvram>
   </os>
 
+or like this:
+
+::
+
+  <os firmware='efi'>
+    <firmware>
+      <feature enabled='yes' name='enrolled-keys'/>
+      <feature enabled='yes' name='secure-boot'/>
+    </firmware>
+    <loader type='rom' format='raw'>/usr/share/edk2/aarch64/QEMU_EFI.qemuvars.fd</loader>
+    <varstore template='/usr/share/edk2/aarch64/vars.secboot.json' path='/var/lib/libvirt/qemu/varstore/vm.json'/>
+  </os>
+
 In order to force libvirt to repeat the firmware autoselection
-process, it's necessary to remove the ``<loader>`` and ``<nvram>``
-elements. Failure to do so will likely result in an error.
+process, it's necessary to remove the ``<loader>`` as well as the
+``<nvram>`` or ``<varstore>`` elements, depending on what's
+applicable. Failure to do so will likely result in an error.
 
 Note that updating the XML configuration as described above is
-**not** enough to change the Secure Boot status: the NVRAM file
-associated with the VM has to be regenerated from its template as
-well.
+**not** enough to change the Secure Boot status: the NVRAM/varstore
+file associated with the VM has to be regenerated from its template
+as well.
 
 In order to do that, update the XML and then start the VM with
 
@@ -107,9 +121,9 @@ This option is only available starting with libvirt 8.1.0, so if your
 version of libvirt is older than that you will have to delete the
 NVRAM file manually before starting the VM.
 
-Most guest operating systems will be able to cope with the NVRAM file
-being reinitialized, but in some cases the VM will be unable to boot
-after the change.
+Most guest operating systems will be able to cope with the
+NVRAM/varstore file being reinitialized, but in some cases the VM
+will be unable to boot after the change.
 
 
 Additional information
@@ -126,15 +140,15 @@ can be used to validate the operating system signature need to be
 provided as well.
 
 Asking for the ``enrolled-keys`` firmware feature to be enabled will
-cause libvirt to initialize the NVRAM file associated with the VM
-from a template that contains a suitable set of keys. These keys
-being present will cause the firmware to enforce the Secure Boot
+cause libvirt to initialize the NVRAM/varstore file associated with
+the VM from a template that contains a suitable set of keys. These
+keys being present will cause the firmware to enforce the Secure Boot
 signing requirements.
 
 The opposite configuration, where the feature is explicitly disabled,
-will result in no keys being present in the NVRAM file. Unable to
-verify signatures, the firmware will allow even unsigned operating
-systems to run.
+will result in no keys being present in the NVRAM/varstore file.
+Unable to verify signatures, the firmware will allow even unsigned
+operating systems to run.
 
 If running unsigned code is desired, it's also possible to ask for
 the ``secure-boot`` feature to be disabled, which will cause libvirt
index a77670ea8d41773cbd91f3e5c247938f41ee0386..998b333c74391312b1a37fa859f23c9128b66499 100644 (file)
@@ -3960,6 +3960,27 @@ virDomainLoaderDefFree(virDomainLoaderDef *loader)
     g_free(loader);
 }
 
+virDomainVarstoreDef *
+virDomainVarstoreDefNew(void)
+{
+    virDomainVarstoreDef *def = NULL;
+
+    def = g_new0(virDomainVarstoreDef, 1);
+
+    return def;
+}
+
+void
+virDomainVarstoreDefFree(virDomainVarstoreDef *varstore)
+{
+    if (!varstore)
+        return;
+
+    g_free(varstore->path);
+    g_free(varstore->template);
+    g_free(varstore);
+}
+
 
 static void
 virDomainResctrlMonDefFree(virDomainResctrlMonDef *domresmon)
@@ -4062,6 +4083,7 @@ virDomainOSDefClear(virDomainOSDef *os)
         virDomainOSACPITableDefFree(os->acpiTables[i]);
     g_free(os->acpiTables);
     virDomainLoaderDefFree(os->loader);
+    virDomainVarstoreDefFree(os->varstore);
     g_free(os->bootloader);
     g_free(os->bootloaderArgs);
 }
@@ -18090,6 +18112,17 @@ virDomainLoaderDefParseXMLLoader(virDomainLoaderDef *loader,
 }
 
 
+static int
+virDomainVarstoreDefParseXML(virDomainVarstoreDef *varstore,
+                             xmlNodePtr varstoreNode)
+{
+    varstore->path = virXMLPropString(varstoreNode, "path");
+    varstore->template = virXMLPropString(varstoreNode, "template");
+
+    return 0;
+}
+
+
 static int
 virDomainLoaderDefParseXML(virDomainLoaderDef *loader,
                            xmlNodePtr loaderNode,
@@ -18537,16 +18570,29 @@ virDomainDefParseBootLoaderOptions(virDomainDef *def,
     xmlNodePtr loaderNode = virXPathNode("./os/loader[1]", ctxt);
     xmlNodePtr nvramNode = virXPathNode("./os/nvram[1]", ctxt);
     xmlNodePtr nvramSourceNode = virXPathNode("./os/nvram/source[1]", ctxt);
+    xmlNodePtr varstoreNode = virXPathNode("./os/varstore[1]", ctxt);
 
-    if (!loaderNode && !nvramNode)
-        return 0;
+    if (nvramNode && varstoreNode) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Cannot have both <nvram> and <varstore>"));
+        return -1;
+    }
 
-    def->os.loader = virDomainLoaderDefNew();
+    if (loaderNode || nvramNode) {
+        def->os.loader = virDomainLoaderDefNew();
 
-    if (virDomainLoaderDefParseXML(def->os.loader,
-                                   loaderNode, nvramNode, nvramSourceNode,
-                                   ctxt, xmlopt, flags) < 0)
-        return -1;
+        if (virDomainLoaderDefParseXML(def->os.loader,
+                                       loaderNode, nvramNode, nvramSourceNode,
+                                       ctxt, xmlopt, flags) < 0)
+            return -1;
+    }
+
+    if (varstoreNode) {
+        def->os.varstore = virDomainVarstoreDefNew();
+
+        if (virDomainVarstoreDefParseXML(def->os.varstore, varstoreNode) < 0)
+            return -1;
+    }
 
     return 0;
 }
@@ -28250,6 +28296,20 @@ virDomainLoaderDefFormat(virBuffer *buf,
     return 0;
 }
 
+static int
+virDomainVarstoreDefFormat(virBuffer *buf,
+                           virDomainVarstoreDef *varstore)
+{
+    g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+
+    virBufferEscapeString(&attrBuf, " template='%s'", varstore->template);
+    virBufferEscapeString(&attrBuf, " path='%s'", varstore->path);
+
+    virXMLFormatElementEmpty(buf, "varstore", &attrBuf, NULL);
+
+    return 0;
+}
+
 static void
 virDomainKeyWrapDefFormat(virBuffer *buf, virDomainKeyWrapDef *keywrap)
 {
@@ -29722,6 +29782,11 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def,
     if (def->os.loader &&
         virDomainLoaderDefFormat(buf, def->os.loader, xmlopt, flags) < 0)
         return -1;
+
+    if (def->os.varstore &&
+        virDomainVarstoreDefFormat(buf, def->os.varstore) < 0)
+        return -1;
+
     virBufferEscapeString(buf, "<kernel>%s</kernel>\n",
                           def->os.kernel);
     virBufferEscapeString(buf, "<initrd>%s</initrd>\n",
index a13f6d79e94a7a63c13178aa9844e4941e1a955f..e63230beec8b210279ef57593ecedab9a600eaca 100644 (file)
@@ -2423,6 +2423,14 @@ struct _virDomainLoaderDef {
 virDomainLoaderDef *virDomainLoaderDefNew(void);
 void virDomainLoaderDefFree(virDomainLoaderDef *loader);
 
+struct _virDomainVarstoreDef {
+    char *path;
+    char *template;
+};
+
+virDomainVarstoreDef *virDomainVarstoreDefNew(void);
+void virDomainVarstoreDefFree(virDomainVarstoreDef *varstore);
+
 typedef enum {
     VIR_DOMAIN_IOAPIC_NONE = 0,
     VIR_DOMAIN_IOAPIC_QEMU,
@@ -2576,6 +2584,7 @@ struct _virDomainOSDef {
     size_t nacpiTables;
     virDomainOSACPITableDef **acpiTables;
     virDomainLoaderDef *loader;
+    virDomainVarstoreDef *varstore;
     char *bootloader;
     char *bootloaderArgs;
     int smbios_mode;
index e09f6e80f3eb6f430c7ee5eff62b1e390fd08645..376218118dfe2d059660205925f1daeffdb2873c 100644 (file)
             </element>
           </optional>
           <optional>
-            <ref name="osnvram"/>
+            <choice>
+              <ref name="osnvram"/>
+              <ref name="osvarstore"/>
+            </choice>
           </optional>
           <optional>
             <ref name="osbootkernel"/>
     </element>
   </define>
 
+  <define name="osvarstore">
+    <element name="varstore">
+      <interleave>
+        <optional>
+          <attribute name="template">
+            <ref name="absFilePath"/>
+          </attribute>
+        </optional>
+        <optional>
+          <attribute name="path">
+            <ref name="absFilePath"/>
+          </attribute>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+
   <define name="osexe">
     <element name="os">
       <interleave>
index 6e2573035ad457a85e1fea56a5148f0bd5acefdd..0596791a4dae71efefe8944e2b92b8105d9a8b87 100644 (file)
@@ -164,6 +164,8 @@ typedef struct _virDomainLeaseDef virDomainLeaseDef;
 
 typedef struct _virDomainLoaderDef virDomainLoaderDef;
 
+typedef struct _virDomainVarstoreDef virDomainVarstoreDef;
+
 typedef struct _virDomainMemballoonDef virDomainMemballoonDef;
 
 typedef struct _virDomainMemoryDef virDomainMemoryDef;
index b200037189530a080c0b160045bb7b2ddc5d3db4..19edf7eb127236024e89c7936311e95e58222b91 100644 (file)
@@ -719,6 +719,8 @@ virDomainTPMProfileRemoveDisabledTypeToString;
 virDomainTPMVersionTypeFromString;
 virDomainTPMVersionTypeToString;
 virDomainUSBDeviceDefForeach;
+virDomainVarstoreDefFree;
+virDomainVarstoreDefNew;
 virDomainVideoDefaultRAM;
 virDomainVideoDefClear;
 virDomainVideoDefFree;