1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-03-07 17:28:15 +00:00

conf: support stateless UEFI firmware

Normally when an UEFI firmware is marked as read-only, an associated
NVRAM file will be created. Some builds of UEFI firmware, however, wish
to remain stateless and so will be read-only, but never have any NVRAM
file. To represent this concept a 'stateless' tristate bool attribute
is introduced on the <loader/> element.

There are rather a large number of permutations to consider.

With default firmware selection

  *  <os/>

     => Historic default, no change

  *  <os>
       <loader stateless='yes'/>
     </os>

     => Explicit version of historic default, no change

  *  <os>
       <loader stateless='no'/>
     </os>

      => Invalid, bios is always stateless

With manual legacy BIOS selection

  *  <os>
       <loader>/path/to/seabios</loader>
       ...
     </os>

     => Historic default, no change

  *  <os>
       <loader stateless='yes'>/path/to/seabios</loader>
       ...
     </os>

     => Explicit version of historic default, no change

  *  <os>
       <loader stateless='no'>/path/to/seabios</loader>
       ...
     </os>

      => Invalid, bios is always stateless

With manual UEFI selection

  *  <os>
       <loader type='pflash'>/path/to/edk2</loader>
       ...
     </os>

     => Historic default, no change

  *  <os>
       <loader type='pflash' stateless='yes'>/path/to/edk2</loader>
       ...
     </os>

     => Skip auto-filling NVRAM / template

  *  <os>
       <loader type='pflash' stateless='no'>/path/to/edk2</loader>
       ...
     </os>

     => Explicit version of historic default, no change

With automatic firmware selection

  *  <os firmware='bios'/>

     => Historic default, no change

  *  <os firmware='bios'>
       <loader stateless='yes'/>
     </os>

     => Explicit version of historic default, no change

  *  <os firmware='bios'>
       <loader stateless='no'/>
     </os>

      => Invalid, bios is always stateless

  *  <os firmware='uefi'/>

     => Historic default, no change

  *  <os firmware='uefi'>
       <loader stateless='yes'/>
     </os>

     => Skip auto-filling NVRAM / template

  *  <os firmware='uefi'>
       <loader stateless='no'/>
     </os>

     => Explicit version of historic default, no change

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2022-07-22 15:27:55 +01:00
parent 429c15259c
commit 578ac25c6a
22 changed files with 326 additions and 1 deletions

View File

@ -173,6 +173,15 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
</os> </os>
... ...
<!-- QEMU with automatic UEFI stateless firmware for AMD SEV -->
...
<os firmware='efi'>
<type>hvm</type>
<loader stateless='yes'/>
<boot dev='hd'/>
</os>
...
``firmware`` ``firmware``
The ``firmware`` attribute allows management applications to automatically The ``firmware`` attribute allows management applications to automatically
fill ``<loader/>`` and ``<nvram/>`` elements and possibly enable some fill ``<loader/>`` and ``<nvram/>`` elements and possibly enable some
@ -242,7 +251,12 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
firmwares may implement the Secure boot feature. Attribute ``secure`` can be firmwares may implement the Secure boot feature. Attribute ``secure`` can be
used to tell the hypervisor that the firmware is capable of Secure Boot feature. used to tell the hypervisor that the firmware is capable of Secure Boot feature.
It cannot be used to enable or disable the feature itself in the firmware. It cannot be used to enable or disable the feature itself in the firmware.
:since:`Since 2.1.0` :since:`Since 2.1.0`. If the loader is marked as read-only, then with UEFI it
is assumed that there will be a writable NVRAM available. In some cases,
however, it may be desirable for the loader to run without any NVRAM, discarding
any config changes on shutdown. The ``stateless`` flag (:since:`Since 8.6.0`)
can be used to control this behaviour, when set to ``no`` NVRAM will never
be created.
``nvram`` ``nvram``
Some UEFI firmwares may want to use a non-volatile memory to store some Some UEFI firmwares may want to use a non-volatile memory to store some
variables. In the host, this is represented as a file and the absolute path variables. In the host, this is represented as a file and the absolute path
@ -262,6 +276,9 @@ harddisk, cdrom, network) determining where to obtain/find the boot image.
**Note:** ``network`` backed NVRAM the variables are not instantiated from **Note:** ``network`` backed NVRAM the variables are not instantiated from
the ``template`` and it's user's responsibility to provide a valid NVRAM image. the ``template`` and it's user's responsibility to provide a valid NVRAM image.
It is not valid to provide this element if the loader is marked as
stateless.
``boot`` ``boot``
The ``dev`` attribute takes one of the values "fd", "hd", "cdrom" or 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 "network" and is used to specify the next boot device to consider. The

View File

@ -16682,6 +16682,10 @@ virDomainLoaderDefParseXML(virDomainLoaderDef *loader,
&loader->secure) < 0) &loader->secure) < 0)
return -1; return -1;
if (virXMLPropTristateBool(loaderNode, "stateless", VIR_XML_PROP_NONE,
&loader->stateless) < 0)
return -1;
return 0; return 0;
} }
@ -25888,6 +25892,11 @@ virDomainLoaderDefFormat(virBuffer *buf,
virBufferAsprintf(&loaderAttrBuf, " type='%s'", virBufferAsprintf(&loaderAttrBuf, " type='%s'",
virDomainLoaderTypeToString(loader->type)); virDomainLoaderTypeToString(loader->type));
if (loader->stateless != VIR_TRISTATE_BOOL_ABSENT) {
virBufferAsprintf(&loaderAttrBuf, " stateless='%s'",
virTristateBoolTypeToString(loader->stateless));
}
virBufferEscapeString(&loaderChildBuf, "%s", loader->path); virBufferEscapeString(&loaderChildBuf, "%s", loader->path);
virXMLFormatElementInternal(buf, "loader", &loaderAttrBuf, &loaderChildBuf, false, false); virXMLFormatElementInternal(buf, "loader", &loaderAttrBuf, &loaderChildBuf, false, false);

View File

@ -2266,6 +2266,7 @@ struct _virDomainLoaderDef {
virTristateBool readonly; virTristateBool readonly;
virDomainLoader type; virDomainLoader type;
virTristateBool secure; virTristateBool secure;
virTristateBool stateless;
virStorageSource *nvram; virStorageSource *nvram;
bool newStyleNVRAM; bool newStyleNVRAM;
char *nvramTemplate; /* user override of path to master nvram */ char *nvramTemplate; /* user override of path to master nvram */

View File

@ -1672,6 +1672,32 @@ virDomainDefOSValidate(const virDomainDef *def,
} }
} }
if (loader->stateless == VIR_TRISTATE_BOOL_YES) {
if (loader->nvramTemplate) {
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("NVRAM template is not permitted when loader is stateless"));
return -1;
}
if (loader->nvram) {
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("NVRAM is not permitted when loader is stateless"));
return -1;
}
} else if (loader->stateless == VIR_TRISTATE_BOOL_NO) {
if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) {
if (def->os.loader->type != VIR_DOMAIN_LOADER_TYPE_PFLASH) {
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("Only pflash loader type permits NVRAM"));
return -1;
}
} else if (def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_EFI) {
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("Only EFI firmware permits NVRAM"));
return -1;
}
}
return 0; return 0;
} }

View File

@ -320,6 +320,11 @@
</choice> </choice>
</attribute> </attribute>
</optional> </optional>
<optional>
<attribute name="stateless">
<ref name="virYesNo"/>
</attribute>
</optional>
<optional> <optional>
<ref name="absFilePath"/> <ref name="absFilePath"/>
</optional> </optional>

View File

@ -0,0 +1 @@
Only EFI firmware permits NVRAM

View File

@ -0,0 +1,18 @@
<domain type='kvm'>
<name>fedora</name>
<uuid>63840878-0deb-4095-97e6-fc444d9bc9fa</uuid>
<memory unit='KiB'>8192</memory>
<vcpu placement='static'>1</vcpu>
<os firmware='bios'>
<type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
<loader stateless='no'/>
</os>
<features>
<acpi/>
</features>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<controller type='usb' model='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1,32 @@
LC_ALL=C \
PATH=/bin \
HOME=/tmp/lib/domain--1-fedora \
USER=test \
LOGNAME=test \
XDG_DATA_HOME=/tmp/lib/domain--1-fedora/.local/share \
XDG_CACHE_HOME=/tmp/lib/domain--1-fedora/.cache \
XDG_CONFIG_HOME=/tmp/lib/domain--1-fedora/.config \
/usr/bin/qemu-system-x86_64 \
-name guest=fedora,debug-threads=on \
-S \
-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-fedora/master-key.aes"}' \
-machine pc-q35-4.0,usb=off,dump-guest-core=off,memory-backend=pc.ram \
-accel kvm \
-cpu qemu64 \
-bios /usr/share/seabios/bios-256k.bin \
-m 8 \
-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":8388608}' \
-overcommit mem-lock=off \
-smp 1,sockets=1,cores=1,threads=1 \
-uuid 63840878-0deb-4095-97e6-fc444d9bc9fa \
-display none \
-no-user-config \
-nodefaults \
-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc \
-no-shutdown \
-boot strict=on \
-audiodev '{"id":"audio1","driver":"none"}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on

View File

@ -0,0 +1,18 @@
<domain type='kvm'>
<name>fedora</name>
<uuid>63840878-0deb-4095-97e6-fc444d9bc9fa</uuid>
<memory unit='KiB'>8192</memory>
<vcpu placement='static'>1</vcpu>
<os firmware='bios'>
<type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
<loader stateless='yes'/>
</os>
<features>
<acpi/>
</features>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<controller type='usb' model='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1 @@
Only pflash loader type permits NVRAM

View File

@ -0,0 +1,15 @@
<domain type='qemu'>
<name>test-bios</name>
<uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
<memory unit='KiB'>1048576</memory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<loader stateless='no'>/usr/share/seabios/bios.bin</loader>
</os>
<devices>
<emulator>/usr/bin/qemu-system-i386</emulator>
<controller type='usb' model='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1,30 @@
LC_ALL=C \
PATH=/bin \
HOME=/tmp/lib/domain--1-test-bios \
USER=test \
LOGNAME=test \
XDG_DATA_HOME=/tmp/lib/domain--1-test-bios/.local/share \
XDG_CACHE_HOME=/tmp/lib/domain--1-test-bios/.cache \
XDG_CONFIG_HOME=/tmp/lib/domain--1-test-bios/.config \
QEMU_AUDIO_DRV=none \
/usr/bin/qemu-system-i386 \
-name guest=test-bios,debug-threads=on \
-S \
-object secret,id=masterKey0,format=raw,file=/tmp/lib/domain--1-test-bios/master-key.aes \
-machine pc,usb=off,dump-guest-core=off \
-accel tcg \
-bios /usr/share/seabios/bios.bin \
-m 1024 \
-overcommit mem-lock=off \
-smp 1,sockets=1,cores=1,threads=1 \
-uuid 362d1fc1-df7d-193e-5c18-49a71bd1da66 \
-display none \
-no-user-config \
-nodefaults \
-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc \
-no-shutdown \
-no-acpi \
-boot strict=on \
-msg timestamp=on

View File

@ -0,0 +1,15 @@
<domain type='qemu'>
<name>test-bios</name>
<uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
<memory unit='KiB'>1048576</memory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<loader stateless='yes'>/usr/share/seabios/bios.bin</loader>
</os>
<devices>
<emulator>/usr/bin/qemu-system-i386</emulator>
<controller type='usb' model='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1 @@
NVRAM is not permitted when loader is stateless

View File

@ -0,0 +1,21 @@
<domain type='qemu'>
<name>test-bios</name>
<uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
<memory unit='KiB'>1048576</memory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<loader stateless='yes' readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram type='file'>
<source file='/var/lib/libvirt/nvram/guest_VARS.fd'/>
</nvram>
</os>
<features>
<acpi/>
</features>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<controller type='usb' model='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1 @@
NVRAM template is not permitted when loader is stateless

View File

@ -0,0 +1,19 @@
<domain type='qemu'>
<name>test-bios</name>
<uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
<memory unit='KiB'>1048576</memory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<loader stateless='yes' readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram template="/usr/share/OVMF/OVMF_VARS.fd"/>
</os>
<features>
<acpi/>
</features>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<controller type='usb' model='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -1190,6 +1190,10 @@ mymain(void)
DO_TEST("firmware-manual-bios", DO_TEST("firmware-manual-bios",
QEMU_CAPS_DEVICE_ISA_SERIAL); QEMU_CAPS_DEVICE_ISA_SERIAL);
DO_TEST("firmware-manual-bios-stateless",
QEMU_CAPS_DEVICE_ISA_SERIAL);
DO_TEST_PARSE_ERROR("firmware-manual-bios-not-stateless",
QEMU_CAPS_DEVICE_ISA_SERIAL);
DO_TEST_NOCAPS("firmware-manual-efi"); DO_TEST_NOCAPS("firmware-manual-efi");
DO_TEST_PARSE_ERROR_NOCAPS("firmware-manual-efi-no-path"); DO_TEST_PARSE_ERROR_NOCAPS("firmware-manual-efi-no-path");
DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-manual-efi-features"); DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-manual-efi-features");
@ -1202,10 +1206,12 @@ mymain(void)
QEMU_CAPS_ICH9_AHCI, QEMU_CAPS_ICH9_AHCI,
QEMU_CAPS_VIRTIO_SCSI); QEMU_CAPS_VIRTIO_SCSI);
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-template"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-template");
DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-manual-efi-nvram-template-stateless");
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-iscsi"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-iscsi");
DO_TEST_CAPS_VER_PARSE_ERROR("firmware-manual-efi-nvram-network-iscsi", "4.1.0"); DO_TEST_CAPS_VER_PARSE_ERROR("firmware-manual-efi-nvram-network-iscsi", "4.1.0");
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-nbd"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-nbd");
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-file"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-file");
DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-manual-efi-nvram-stateless");
/* Make sure all combinations of ACPI and UEFI behave as expected */ /* Make sure all combinations of ACPI and UEFI behave as expected */
DO_TEST_NOCAPS("firmware-manual-efi-acpi-aarch64"); DO_TEST_NOCAPS("firmware-manual-efi-acpi-aarch64");
@ -1218,6 +1224,8 @@ mymain(void)
DO_TEST_NOCAPS("firmware-manual-noefi-noacpi-q35"); DO_TEST_NOCAPS("firmware-manual-noefi-noacpi-q35");
DO_TEST_CAPS_LATEST("firmware-auto-bios"); DO_TEST_CAPS_LATEST("firmware-auto-bios");
DO_TEST_CAPS_LATEST("firmware-auto-bios-stateless");
DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-auto-bios-not-stateless");
DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-auto-bios-nvram"); DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-auto-bios-nvram");
DO_TEST_CAPS_LATEST("firmware-auto-efi"); DO_TEST_CAPS_LATEST("firmware-auto-efi");
DO_TEST_CAPS_LATEST("firmware-auto-efi-nvram"); DO_TEST_CAPS_LATEST("firmware-auto-efi-nvram");

View File

@ -0,0 +1,34 @@
<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='bios'>
<type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
<loader stateless='yes'/>
<boot dev='hd'/>
</os>
<features>
<acpi/>
</features>
<cpu mode='custom' match='exact' check='none'>
<model fallback='forbid'>qemu64</model>
</cpu>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<controller type='usb' index='0' model='none'/>
<controller type='sata' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pcie-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<audio id='1' type='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1,25 @@
<domain type='qemu'>
<name>test-bios</name>
<uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>1048576</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<loader type='rom' stateless='yes'>/usr/share/seabios/bios.bin</loader>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-i386</emulator>
<controller type='usb' index='0' model='none'/>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<audio id='1' type='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -0,0 +1,25 @@
<domain type='qemu'>
<name>test-bios</name>
<uuid>362d1fc1-df7d-193e-5c18-49a71bd1da66</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>1048576</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<loader type='rom'>/usr/share/seabios/bios.bin</loader>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-i386</emulator>
<controller type='usb' index='0' model='none'/>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<audio id='1' type='none'/>
<memballoon model='none'/>
</devices>
</domain>

View File

@ -1067,12 +1067,15 @@ mymain(void)
DO_TEST("numatune-hmat", QEMU_CAPS_NUMA_HMAT, QEMU_CAPS_OBJECT_MEMORY_RAM); DO_TEST("numatune-hmat", QEMU_CAPS_NUMA_HMAT, QEMU_CAPS_OBJECT_MEMORY_RAM);
DO_TEST_CAPS_LATEST("numatune-memnode-restrictive-mode"); DO_TEST_CAPS_LATEST("numatune-memnode-restrictive-mode");
DO_TEST_NOCAPS("firmware-manual-bios");
DO_TEST_NOCAPS("firmware-manual-bios-stateless");
DO_TEST_NOCAPS("firmware-manual-efi"); DO_TEST_NOCAPS("firmware-manual-efi");
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-iscsi"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-iscsi");
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-nbd"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-network-nbd");
DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-file"); DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-file");
DO_TEST_CAPS_LATEST("firmware-auto-bios"); DO_TEST_CAPS_LATEST("firmware-auto-bios");
DO_TEST_CAPS_LATEST("firmware-auto-bios-stateless");
DO_TEST_CAPS_LATEST("firmware-auto-efi"); DO_TEST_CAPS_LATEST("firmware-auto-efi");
DO_TEST_CAPS_LATEST("firmware-auto-efi-nvram"); DO_TEST_CAPS_LATEST("firmware-auto-efi-nvram");
DO_TEST_CAPS_LATEST("firmware-auto-efi-loader-secure"); DO_TEST_CAPS_LATEST("firmware-auto-efi-loader-secure");