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(a)redhat.com>
---
docs/formatdomain.rst | 31 +++++++
docs/schemas/domaincommon.rng | 23 +++++
src/conf/domain_conf.c | 83 ++++++++++++++++++-
src/conf/domain_conf.h | 10 +++
.../os-firmware-efi-invalid-type.xml | 28 +++++++
...os-firmware-invalid-type.x86_64-latest.err | 1 +
.../os-firmware-invalid-type.xml | 28 +++++++
tests/qemuxml2argvtest.c | 1 +
...aarch64-os-firmware-efi.aarch64-latest.xml | 1 +
.../os-firmware-bios.x86_64-latest.xml | 1 +
.../os-firmware-efi-secboot.x86_64-latest.xml | 1 +
.../os-firmware-efi.x86_64-latest.xml | 1 +
tests/vmx2xmldata/vmx2xml-firmware-efi.xml | 1 +
13 files changed, 207 insertions(+), 3 deletions(-)
create mode 100644 tests/qemuxml2argvdata/os-firmware-efi-invalid-type.xml
create mode 100644 tests/qemuxml2argvdata/os-firmware-invalid-type.x86_64-latest.err
create mode 100644 tests/qemuxml2argvdata/os-firmware-invalid-type.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index c101d5a1f1..dd063b0794 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -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
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index e6db2f5b74..1dbfc68f18 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -276,6 +276,29 @@
</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>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 7729333897..dcfe5c0d03 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -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",
@@ -19600,22 +19606,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;
}
@@ -29455,6 +29506,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++)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 54a631853b..87bc7e8625 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -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-efi-invalid-type.xml
b/tests/qemuxml2argvdata/os-firmware-efi-invalid-type.xml
new file mode 100644
index 0000000000..41360df0f7
--- /dev/null
+++ b/tests/qemuxml2argvdata/os-firmware-efi-invalid-type.xml
@@ -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>
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
index 0000000000..c8174b1c8b
--- /dev/null
+++ b/tests/qemuxml2argvdata/os-firmware-invalid-type.x86_64-latest.err
@@ -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
index 0000000000..41360df0f7
--- /dev/null
+++ b/tests/qemuxml2argvdata/os-firmware-invalid-type.xml
@@ -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>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 29054ba168..2b32b7f303 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -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");
diff --git a/tests/qemuxml2xmloutdata/aarch64-os-firmware-efi.aarch64-latest.xml
b/tests/qemuxml2xmloutdata/aarch64-os-firmware-efi.aarch64-latest.xml
index 627e285ae1..cb4f3ccfce 100644
--- a/tests/qemuxml2xmloutdata/aarch64-os-firmware-efi.aarch64-latest.xml
+++ b/tests/qemuxml2xmloutdata/aarch64-os-firmware-efi.aarch64-latest.xml
@@ -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>
diff --git a/tests/qemuxml2xmloutdata/os-firmware-bios.x86_64-latest.xml
b/tests/qemuxml2xmloutdata/os-firmware-bios.x86_64-latest.xml
index df6f61421a..016c5b863f 100644
--- a/tests/qemuxml2xmloutdata/os-firmware-bios.x86_64-latest.xml
+++ b/tests/qemuxml2xmloutdata/os-firmware-bios.x86_64-latest.xml
@@ -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'/>
diff --git a/tests/qemuxml2xmloutdata/os-firmware-efi-secboot.x86_64-latest.xml
b/tests/qemuxml2xmloutdata/os-firmware-efi-secboot.x86_64-latest.xml
index c383546cc6..fa5eaa3148 100644
--- a/tests/qemuxml2xmloutdata/os-firmware-efi-secboot.x86_64-latest.xml
+++ b/tests/qemuxml2xmloutdata/os-firmware-efi-secboot.x86_64-latest.xml
@@ -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'/>
diff --git a/tests/qemuxml2xmloutdata/os-firmware-efi.x86_64-latest.xml
b/tests/qemuxml2xmloutdata/os-firmware-efi.x86_64-latest.xml
index 04d57860e7..382146c23b 100644
--- a/tests/qemuxml2xmloutdata/os-firmware-efi.x86_64-latest.xml
+++ b/tests/qemuxml2xmloutdata/os-firmware-efi.x86_64-latest.xml
@@ -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'/>
diff --git a/tests/vmx2xmldata/vmx2xml-firmware-efi.xml
b/tests/vmx2xmldata/vmx2xml-firmware-efi.xml
index fee707fe71..fa10daf3a6 100644
--- a/tests/vmx2xmldata/vmx2xml-firmware-efi.xml
+++ b/tests/vmx2xmldata/vmx2xml-firmware-efi.xml
@@ -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>
--
2.30.2