[PATCH] Allow slices for file/block backed NVRAM
For NVRAM storage specifically, the guest-visible size is critical to correct handling of the OVMF address space. Allow specifying storage slices for NVRAM storage when the underlying file/block may be larger due to alignment/allocation restrictions. Signed-off-by: John Levon <john.levon@nutanix.com> Reviewed-by: Mark Cave-Ayland <mark.caveayland@nutanix.com> --- src/qemu/qemu_validate.c | 13 +++--- ...ual-efi-nvram-dev-slice.x86_64-latest.args | 35 ++++++++++++++++ ...nual-efi-nvram-dev-slice.x86_64-latest.xml | 42 +++++++++++++++++++ .../firmware-manual-efi-nvram-dev-slice.xml | 25 +++++++++++ tests/qemuxmlconftest.c | 1 + 5 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.xml diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 61f3bd3278..113a711390 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -708,7 +708,14 @@ qemuValidateDomainDefNvram(const virDomainDef *def, switch (src->type) { case VIR_STORAGE_TYPE_FILE: case VIR_STORAGE_TYPE_BLOCK: + break; + case VIR_STORAGE_TYPE_NETWORK: + if (src->sliceStorage) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("slices are not supported with network-backed NVRAM")); + return -1; + } break; case VIR_STORAGE_TYPE_DIR: @@ -728,12 +735,6 @@ qemuValidateDomainDefNvram(const virDomainDef *def, return -1; } - if (src->sliceStorage) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("slices are not supported with NVRAM")); - return -1; - } - if (src->pr) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent reservations are not supported with NVRAM")); diff --git a/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.args b/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.args new file mode 100644 index 0000000000..bb911d4ae1 --- /dev/null +++ b/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.args @@ -0,0 +1,35 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-guest \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-guest/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-guest/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=guest,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-guest/master-key.aes"}' \ +-blockdev '{"driver":"file","filename":"/usr/share/edk2/ovmf/OVMF_CODE.fd","node-name":"libvirt-pflash0-storage","auto-read-only":true,"discard":"unmap"}' \ +-blockdev '{"node-name":"libvirt-pflash0-format","read-only":true,"driver":"raw","file":"libvirt-pflash0-storage"}' \ +-blockdev '{"driver":"host_device","filename":"/dev/lv/guest_VARS.fd","node-name":"libvirt-pflash1-storage","auto-read-only":true,"discard":"unmap"}' \ +-blockdev '{"driver":"raw","file":"libvirt-pflash1-storage","offset":0,"size":540672,"node-name":"libvirt-0-slice-sto","read-only":false}' \ +-machine pc-i440fx-10.0,usb=off,dump-guest-core=off,memory-backend=pc.ram,pflash0=libvirt-pflash0-format,pflash1=libvirt-0-slice-sto,acpi=on \ +-accel tcg \ +-cpu qemu64 \ +-m size=1048576k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":1073741824}' \ +-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=@mon-fd@,server=on,wait=off \ +-object '{"qom-type":"monitor-qmp","id":"monitor","chardev":"charmonitor"}' \ +-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 diff --git a/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.xml b/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.xml new file mode 100644 index 0000000000..9409c6a7a6 --- /dev/null +++ b/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.xml @@ -0,0 +1,42 @@ +<domain type='qemu'> + <name>guest</name> + <uuid>63840878-0deb-4095-97e6-fc444d9bc9fa</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>1048576</currentMemory> + <vcpu placement='static'>1</vcpu> + <os firmware='efi'> + <type arch='x86_64' machine='pc-i440fx-10.0'>hvm</type> + <firmware> + <feature enabled='no' name='enrolled-keys'/> + <feature enabled='no' name='secure-boot'/> + </firmware> + <loader readonly='yes' type='pflash' format='raw'>/usr/share/edk2/ovmf/OVMF_CODE.fd</loader> + <nvram template='/usr/share/edk2/ovmf/OVMF_VARS.fd' templateFormat='raw' type='block' format='raw'> + <source dev='/dev/lv/guest_VARS.fd'> + <slices> + <slice type='storage' offset='0' size='540672'/> + </slices> + </source> + </nvram> + <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='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> diff --git a/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.xml b/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.xml new file mode 100644 index 0000000000..31cf086c2a --- /dev/null +++ b/tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.xml @@ -0,0 +1,25 @@ +<domain type='qemu'> + <name>guest</name> + <uuid>63840878-0deb-4095-97e6-fc444d9bc9fa</uuid> + <memory unit='KiB'>1048576</memory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-i440fx-10.0'>hvm</type> + <loader readonly='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.fd</loader> + <nvram type='block'> + <source dev='/dev/lv/guest_VARS.fd'> + <slices> + <slice type="storage" offset="0" size="540672"/> + </slices> + </source> + </nvram> + </os> + <features> + <acpi/> + </features> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' model='none'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index e0e64c6c38..3b6f97c2b0 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -1442,6 +1442,7 @@ mymain(void) 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-file"); + DO_TEST_CAPS_LATEST("firmware-manual-efi-nvram-dev-slice"); DO_TEST_CAPS_LATEST_PARSE_ERROR("firmware-manual-efi-nvram-stateless"); DO_TEST_CAPS_ARCH_LATEST_FULL("firmware-manual-efi-sev-snp", "x86_64", -- 2.43.0
On Tue, Aug 11, 2026 at 13:58:07 +0100, John Levon wrote:
For NVRAM storage specifically, the guest-visible size is critical to correct handling of the OVMF address space. Allow specifying storage slices for NVRAM storage when the underlying file/block may be larger
This explanation doesn't make sense for the 'file' backed NVRAM because file size can be arbitrary.
due to alignment/allocation restrictions.
For block devices we support 'qcow2' formatted nvram block device for this reason so that the size can be embedded. Have you considered using that configuration? The advantage is that libvirt supports auto-population of the block device from the template. With 'raw'+slice you'll have to populate the nvram yourself.
Signed-off-by: John Levon <john.levon@nutanix.com> Reviewed-by: Mark Cave-Ayland <mark.caveayland@nutanix.com> --- src/qemu/qemu_validate.c | 13 +++--- ...ual-efi-nvram-dev-slice.x86_64-latest.args | 35 ++++++++++++++++ ...nual-efi-nvram-dev-slice.x86_64-latest.xml | 42 +++++++++++++++++++ .../firmware-manual-efi-nvram-dev-slice.xml | 25 +++++++++++ tests/qemuxmlconftest.c | 1 + 5 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/firmware-manual-efi-nvram-dev-slice.xml
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 61f3bd3278..113a711390 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -708,7 +708,14 @@ qemuValidateDomainDefNvram(const virDomainDef *def, switch (src->type) { case VIR_STORAGE_TYPE_FILE: case VIR_STORAGE_TYPE_BLOCK: + break;
As said above I don't see a reason to do this for _FILE as file can be of arbitrary size. It'd also complicate things because `qemuPrepareNVRAMFileCommon` would not comply with the sected size when the image is autopopulated. For _BLOCK this patch is incomplete. Theoretically use of a storage slice still allows the qcow2 image format to be present. The code in `qemuPrepareNVRAMBlock` tries to probe the qcow2 format in the block device to see if it needs to be populated. That will not work with a storage slice (if e.g. offset is populated). So either `qemuPrepareNVRAMBlock` needs to have an exception if a slice is present or the slice needs to be refused for qcow2 ... or rather allowed only for 'raw' nvram block device. I still suggest using qcow2 in the block device instead.
+ case VIR_STORAGE_TYPE_NETWORK: + if (src->sliceStorage) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("slices are not supported with network-backed NVRAM")); + return -1; + } break;
case VIR_STORAGE_TYPE_DIR:
On Thu, Aug 13, 2026 at 04:09:00PM +0200, Peter Krempa wrote:
On Tue, Aug 11, 2026 at 13:58:07 +0100, John Levon wrote:
For NVRAM storage specifically, the guest-visible size is critical to correct handling of the OVMF address space. Allow specifying storage slices for NVRAM storage when the underlying file/block may be larger
This explanation doesn't make sense for the 'file' backed NVRAM because file size can be arbitrary.
I can correct the explanation so that it mentions offset instead for files, as that still seems useful even for file backend?
due to alignment/allocation restrictions.
For block devices we support 'qcow2' formatted nvram block device for this reason so that the size can be embedded.
Yes, I'm aware of that, it's not a good fit for the way we manage this storage for a few reasons. FWIW it was Nutanix who introduced this restriction in the first place, so this is just correcting a previous patch of ours.
For _BLOCK this patch is incomplete. Theoretically use of a storage slice still allows the qcow2 image format to be present. The code in `qemuPrepareNVRAMBlock` tries to probe the qcow2 format in the block device to see if it needs to be populated. That will not work with a storage slice (if e.g. offset is populated). So either `qemuPrepareNVRAMBlock` needs to have an exception if a slice is present or the slice needs to be refused for qcow2 ... or rather allowed only for 'raw' nvram block device.
Thanks, good spot, before I prepare one, would you accept a patch that does this? Allows the slice for file+block but only if qcow2 is not configured? regards john
On Thu, Aug 13, 2026 at 15:22:15 +0100, John Levon wrote:
On Thu, Aug 13, 2026 at 04:09:00PM +0200, Peter Krempa wrote:
On Tue, Aug 11, 2026 at 13:58:07 +0100, John Levon wrote:
For NVRAM storage specifically, the guest-visible size is critical to correct handling of the OVMF address space. Allow specifying storage slices for NVRAM storage when the underlying file/block may be larger
This explanation doesn't make sense for the 'file' backed NVRAM because file size can be arbitrary.
I can correct the explanation so that it mentions offset instead for files, as that still seems useful even for file backend?
IMO it does not. And I don't really want to have another piece of unused code to maintain. If you want to do that anyways for any reason, separate it to another patch that will include also all the other required modifications and checks that will need to be done to integrate it with the code that populates the nvram so that we can separately judge if it makes sense to carry that code.
due to alignment/allocation restrictions.
For block devices we support 'qcow2' formatted nvram block device for this reason so that the size can be embedded.
Yes, I'm aware of that, it's not a good fit for the way we manage this storage for a few reasons.
Can you elaborate? The advantage of qcow2 is that it works with the _RESET_NVRAM VM startup flag, since we have code to auto-populate QCOW2 based storage, that will not work with 'raw' _BLOCK storage.
FWIW it was Nutanix who introduced this restriction in the first place, so this is just correcting a previous patch of ours.
That doesn't matter. I've added the code that handles qcow2 based block devices so now if you want to add nvram it needs to work with that too.
For _BLOCK this patch is incomplete. Theoretically use of a storage slice still allows the qcow2 image format to be present. The code in `qemuPrepareNVRAMBlock` tries to probe the qcow2 format in the block device to see if it needs to be populated. That will not work with a storage slice (if e.g. offset is populated). So either `qemuPrepareNVRAMBlock` needs to have an exception if a slice is present or the slice needs to be refused for qcow2 ... or rather allowed only for 'raw' nvram block device.
Thanks, good spot, before I prepare one, would you accept a patch that does this? Allows the slice for file+block but only if qcow2 is not configured?
As said I see absolutely no reason to support _FILE. You'll have to come up with a good justification to persuade me. If you want to support 'raw' _BLOCK+slice then that's fine if you don't want to use the IMO much more reasonable qcow2 formatted backend. I don't think we want to support _BLOCK+storageslice+qcow2. You'd have to justify that one separately too. I'd also refuse non-0 offset. I don't think that any sane config would ever need that.
On Thu, Aug 13, 2026 at 04:36:35PM +0200, Peter Krempa wrote:
due to alignment/allocation restrictions.
For block devices we support 'qcow2' formatted nvram block device for this reason so that the size can be embedded.
Yes, I'm aware of that, it's not a good fit for the way we manage this storage for a few reasons.
Can you elaborate?
Usually our nvram is from networked storage (and exactly sized). The templating stuff is not useful for us as a result. And, as a consequence, we don't want to have a completely separate path/workflow for the local storage case.
As said I see absolutely no reason to support _FILE. You'll have to come up with a good justification to persuade me.
That's fine, we don't need it either. I'll prepare a patch instead that allows slices only for raw block with offset=0. thanks john
participants (2)
-
John Levon -
Peter Krempa