]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
conf: introduce support for firmware auto-selection feature filtering
authorPavel Hrdina <phrdina@redhat.com>
Tue, 16 Mar 2021 12:07:26 +0000 (13:07 +0100)
committerPavel Hrdina <phrdina@redhat.com>
Thu, 18 Mar 2021 17:42:26 +0000 (18:42 +0100)
When the firmware auto-selection was introduced it always picked first
usable firmware based on the JSON descriptions on the host. It is
possible to add/remove/change the JSON files but it will always be for
the whole host.

This patch introduces support for configuring the auto-selection per VM
by adding users an option to limit what features they would like to have
available in the firmware.

Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
12 files changed:
docs/formatdomain.rst
docs/schemas/domaincommon.rng
src/conf/domain_conf.c
src/conf/domain_conf.h
tests/qemuxml2argvdata/os-firmware-invalid-type.x86_64-latest.err [new file with mode: 0644]
tests/qemuxml2argvdata/os-firmware-invalid-type.xml [new file with mode: 0644]
tests/qemuxml2argvtest.c
tests/qemuxml2xmloutdata/aarch64-os-firmware-efi.aarch64-latest.xml
tests/qemuxml2xmloutdata/os-firmware-bios.x86_64-latest.xml
tests/qemuxml2xmloutdata/os-firmware-efi-secboot.x86_64-latest.xml
tests/qemuxml2xmloutdata/os-firmware-efi.x86_64-latest.xml
tests/vmx2xmldata/vmx2xml-firmware-efi.xml

index 70709ef9467c361c99d98af446536abf37f142ba..9392c80113d62314298ae88388cd9807d637d760 100644 (file)
@@ -155,6 +155,37 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
    the host native arch will be chosen. For the ``test``, ``ESX`` and ``VMWare``
    hypervisor drivers, however, the ``i686`` arch will always be chosen even on
    an ``x86_64`` host. :since:`Since 0.0.1`
+``firmware``
+   :since:`Since 7.2.0 QEMU/KVM only`
+
+   When used together with ``firmware`` attribute of ``os`` element the ``type``
+   attribute must have the same value.
+
+   List of mandatory attributes:
+
+   - ``type`` (accepted values are ``bios`` and ``efi``) same as the ``firmware``
+     attribute of ``os`` element.
+
+   When using firmware auto-selection there are different features enabled in
+   the firmwares. The list of features can be used to limit what firmware should
+   be automatically selected for the VM. The list of features can be specified
+   using zero or more ``feature`` elements. Libvirt will take into consideration
+   only the listed features and ignore the rest when selecting the firmware.
+
+   ``feature``
+      The list of mandatory attributes:
+
+      - ``enabled`` (accepted values are ``yes`` and ``no``) is used to tell libvirt
+        if the feature must be enabled or not in the automatically selected firmware
+
+      - ``name`` the name of the feature, the list of the features:
+
+        - ``enrolled-keys`` whether the selected nvram template has default
+          certificate enrolled. Firmware with Secure Boot feature but without
+          enrolled keys will successfully boot non-signed binaries as well.
+          Valid only for firmwares with Secure Boot feature.
+
+        - ``secure-boot`` whether the firmware implements UEFI Secure boot feature.
 ``loader``
    The optional ``loader`` tag refers to a firmware blob, which is specified by
    absolute path, used to assist the domain creation process. It is used by Xen
index e6db2f5b740d71fc0a1e059c867050a25e7f04e4..1dbfc68f182b45ff258858b89a71a54b3eb1b711 100644 (file)
           </attribute>
         </optional>
         <ref name="ostypehvm"/>
+        <optional>
+          <element name="firmware">
+            <attribute name="type">
+              <choice>
+                <value>bios</value>
+                <value>efi</value>
+              </choice>
+            </attribute>
+            <zeroOrMore>
+              <element name="feature">
+                <attribute name="enabled">
+                  <ref name="virYesNo"/>
+                </attribute>
+                <attribute name="name">
+                  <choice>
+                    <value>enrolled-keys</value>
+                    <value>secure-boot</value>
+                  </choice>
+                </attribute>
+              </element>
+            </zeroOrMore>
+          </element>
+        </optional>
         <optional>
           <element name="loader">
             <optional>
index c1fcab8507c2c0489e8949f61b2a29a407bd5c07..7671050134c499f0c084d32b6d97cb20b12bb162 100644 (file)
@@ -1318,6 +1318,12 @@ VIR_ENUM_IMPL(virDomainOsDefFirmware,
               "efi",
 );
 
+VIR_ENUM_IMPL(virDomainOsDefFirmwareFeature,
+              VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_LAST,
+              "enrolled-keys",
+              "secure-boot",
+);
+
 VIR_ENUM_IMPL(virDomainCFPC,
               VIR_DOMAIN_CFPC_LAST,
               "none",
@@ -19582,22 +19588,67 @@ virDomainDefParseBootFirmwareOptions(virDomainDefPtr def,
                                      xmlXPathContextPtr ctxt)
 {
     g_autofree char *firmware = virXPathString("string(./os/@firmware)", ctxt);
+    g_autofree char *type = virXPathString("string(./os/firmware/@type)", ctxt);
+    g_autofree xmlNodePtr *nodes = NULL;
+    g_autofree int *features = NULL;
     int fw = 0;
+    int n = 0;
+    size_t i;
 
-    if (!firmware)
+    if (!firmware && !type)
         return 0;
 
-    fw = virDomainOsDefFirmwareTypeFromString(firmware);
+    if (firmware && type && STRNEQ(firmware, type)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("firmware attribute and firmware type has to be the same"));
+        return -1;
+    }
+
+    if (!type)
+        type = g_steal_pointer(&firmware);
+
+    fw = virDomainOsDefFirmwareTypeFromString(type);
 
     if (fw <= 0) {
         virReportError(VIR_ERR_XML_ERROR,
                        _("unknown firmware value %s"),
-                       firmware);
+                       type);
         return -1;
     }
 
     def->os.firmware = fw;
 
+    if ((n = virXPathNodeSet("./os/firmware/feature", ctxt, &nodes)) < 0)
+        return -1;
+
+    if (n > 0)
+        features = g_new0(int, VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_LAST);
+
+    for (i = 0; i < n; i++) {
+        g_autofree char *name = virXMLPropString(nodes[i], "name");
+        g_autofree char *enabled = virXMLPropString(nodes[i], "enabled");
+        int feature = virDomainOsDefFirmwareFeatureTypeFromString(name);
+        int val = virTristateBoolTypeFromString(enabled);
+
+        if (feature < 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("invalid firmware feature name '%s'"),
+                           name);
+            return -1;
+        }
+
+        if (val < 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("invalid firmware feature enabled value '%s'"),
+                           enabled);
+            return -1;
+        }
+
+        features[feature] = val;
+    }
+
+    def->os.firmwareFeatures = g_steal_pointer(&features);
+
     return 0;
 }
 
@@ -29437,6 +29488,32 @@ virDomainDefFormatInternalSetRootName(virDomainDefPtr def,
         virBufferAsprintf(buf, ">%s</type>\n",
                           virDomainOSTypeToString(def->os.type));
 
+    if (def->os.firmware) {
+        virBufferAsprintf(buf, "<firmware type='%s'",
+                          virDomainOsDefFirmwareTypeToString(def->os.firmware));
+
+        if (def->os.firmwareFeatures) {
+            virBufferAddLit(buf, ">\n");
+
+            virBufferAdjustIndent(buf, 2);
+
+            for (i = 0; i < VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_LAST; i++) {
+                if (def->os.firmwareFeatures[i] == VIR_TRISTATE_BOOL_ABSENT)
+                    continue;
+
+                virBufferAsprintf(buf, "<feature enabled='%s' name='%s'/>\n",
+                                  virTristateBoolTypeToString(def->os.firmwareFeatures[i]),
+                                  virDomainOsDefFirmwareFeatureTypeToString(i));
+            }
+
+            virBufferAdjustIndent(buf, -2);
+
+            virBufferAddLit(buf, "</firmware>\n");
+        } else {
+            virBufferAddLit(buf, "/>\n");
+        }
+    }
+
     virBufferEscapeString(buf, "<init>%s</init>\n",
                           def->os.init);
     for (i = 0; def->os.initargv && def->os.initargv[i]; i++)
index 54a631853ba2450311ee1bd84eed6b22a907223f..87bc7e8625259da5298b847534daeabfd2d1f701 100644 (file)
@@ -2235,9 +2235,19 @@ G_STATIC_ASSERT((int)VIR_DOMAIN_OS_DEF_FIRMWARE_LAST == (int)VIR_DOMAIN_LOADER_T
 
 VIR_ENUM_DECL(virDomainOsDefFirmware);
 
+typedef enum {
+    VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_ENROLLED_KEYS,
+    VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT,
+
+    VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_LAST
+} virDomainOsDefFirmwareFeature;
+
+VIR_ENUM_DECL(virDomainOsDefFirmwareFeature);
+
 struct _virDomainOSDef {
     int type;
     virDomainOsDefFirmware firmware;
+    int *firmwareFeatures;
     virArch arch;
     char *machine;
     size_t nBootDevs;
diff --git a/tests/qemuxml2argvdata/os-firmware-invalid-type.x86_64-latest.err b/tests/qemuxml2argvdata/os-firmware-invalid-type.x86_64-latest.err
new file mode 100644 (file)
index 0000000..c8174b1
--- /dev/null
@@ -0,0 +1 @@
+unsupported configuration: firmware attribute and firmware type has to be the same
diff --git a/tests/qemuxml2argvdata/os-firmware-invalid-type.xml b/tests/qemuxml2argvdata/os-firmware-invalid-type.xml
new file mode 100644 (file)
index 0000000..41360df
--- /dev/null
@@ -0,0 +1,28 @@
+<domain type='kvm'>
+  <name>fedora</name>
+  <uuid>63840878-0deb-4095-97e6-fc444d9bc9fa</uuid>
+  <memory unit='KiB'>8192</memory>
+  <currentMemory unit='KiB'>8192</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <os firmware='efi'>
+    <type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
+    <firmware type='bios'/>
+    <loader secure='no'/>
+    <nvram>/var/lib/libvirt/qemu/nvram/fedora_VARS.fd</nvram>
+    <boot dev='hd'/>
+    <bootmenu enable='yes'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <pae/>
+  </features>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>restart</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <memballoon model='none'/>
+  </devices>
+</domain>
index 29054ba16849b1ce734d1702154765d414ed43a7..2b32b7f303acacfbe7a589a10db509d124b8e1dd 100644 (file)
@@ -3549,6 +3549,7 @@ mymain(void)
     DO_TEST_CAPS_LATEST("os-firmware-bios");
     DO_TEST_CAPS_LATEST("os-firmware-efi");
     DO_TEST_CAPS_LATEST("os-firmware-efi-secboot");
+    DO_TEST_CAPS_LATEST_PARSE_ERROR("os-firmware-invalid-type");
     DO_TEST_CAPS_ARCH_LATEST("aarch64-os-firmware-efi", "aarch64");
 
     DO_TEST_CAPS_LATEST("vhost-user-vga");
index 627e285ae1e0128bd326023f07b006c447b6368e..cb4f3ccfceeff01b3cf9df726b13782129974113 100644 (file)
@@ -6,6 +6,7 @@
   <vcpu placement='static'>1</vcpu>
   <os firmware='efi'>
     <type arch='aarch64' machine='virt-4.0'>hvm</type>
+    <firmware type='efi'/>
     <kernel>/aarch64.kernel</kernel>
     <initrd>/aarch64.initrd</initrd>
     <cmdline>earlyprintk console=ttyAMA0,115200n8 rw root=/dev/vda rootwait</cmdline>
index df6f61421a1625dba7bf6346334b949b596f8d24..016c5b863f51b8c2d7a91a6dd915a567293e46f9 100644 (file)
@@ -6,6 +6,7 @@
   <vcpu placement='static'>1</vcpu>
   <os firmware='bios'>
     <type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
+    <firmware type='bios'/>
     <loader secure='no'/>
     <nvram>/var/lib/libvirt/qemu/nvram/fedora_VARS.fd</nvram>
     <boot dev='hd'/>
index c383546cc6c361e990efc288ff3664e2755cc93e..fa5eaa3148a8e1ff11c6b7a0bac1bf2c310f2fa8 100644 (file)
@@ -6,6 +6,7 @@
   <vcpu placement='static'>1</vcpu>
   <os firmware='efi'>
     <type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
+    <firmware type='efi'/>
     <loader secure='yes'/>
     <nvram>/var/lib/libvirt/qemu/nvram/fedora_VARS.fd</nvram>
     <boot dev='hd'/>
index 04d57860e7752d18633c1c0010956ee5a2d7c43c..382146c23bd18d509f5108278e6fbc41c9eaad04 100644 (file)
@@ -6,6 +6,7 @@
   <vcpu placement='static'>1</vcpu>
   <os firmware='efi'>
     <type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
+    <firmware type='efi'/>
     <loader secure='no'/>
     <nvram>/var/lib/libvirt/qemu/nvram/fedora_VARS.fd</nvram>
     <boot dev='hd'/>
index fee707fe71af6c0c4f5af3076b31e58a55c74aed..fa10daf3a69c154ff53d21c101d529b4a385be8d 100644 (file)
@@ -6,6 +6,7 @@
   <vcpu placement='static'>1</vcpu>
   <os firmware='efi'>
     <type arch='i686'>hvm</type>
+    <firmware type='efi'/>
   </os>
   <clock offset='utc'/>
   <on_poweroff>destroy</on_poweroff>