[PATCH RESEND 0/5] Introduce pstore device

This is a rebased version of: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/UAUKL... which was a rebased version of: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/VWR67... Let's hope no more resends will be needed. Michal Prívozník (5): qemu_capabilities: Introduce QEMU_CAPS_DEVICE_ACPI_ERST conf: Introduce pstore device qemu: Build cmd line for pstore device security: Set seclabels for pstore device NEWS: Document pstore device addition NEWS.rst | 7 + docs/formatdomain.rst | 32 ++++ src/ch/ch_domain.c | 1 + src/conf/domain_conf.c | 153 ++++++++++++++++++ src/conf/domain_conf.h | 19 +++ src/conf/domain_postparse.c | 1 + src/conf/domain_validate.c | 30 ++++ src/conf/schemas/domaincommon.rng | 25 +++ src/conf/virconftypes.h | 2 + src/hyperv/hyperv_driver.c | 1 + src/libvirt_private.syms | 2 + src/libxl/libxl_driver.c | 6 + src/lxc/lxc_driver.c | 6 + src/qemu/qemu_alias.c | 10 ++ src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 52 ++++++ src/qemu/qemu_domain.c | 3 + src/qemu/qemu_domain_address.c | 11 ++ src/qemu/qemu_driver.c | 3 + src/qemu/qemu_hotplug.c | 5 + src/qemu/qemu_validate.c | 26 +++ src/security/security_dac.c | 10 ++ src/security/security_selinux.c | 9 ++ src/security/virt-aa-helper.c | 4 + src/test/test_driver.c | 1 + .../caps_7.0.0_aarch64+hvf.xml | 1 + .../caps_7.0.0_aarch64.xml | 1 + .../qemucapabilitiesdata/caps_7.0.0_ppc64.xml | 1 + .../caps_7.0.0_x86_64.xml | 1 + .../qemucapabilitiesdata/caps_7.1.0_ppc64.xml | 1 + .../caps_7.1.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml | 1 + .../caps_7.2.0_x86_64+hvf.xml | 1 + .../caps_7.2.0_x86_64.xml | 1 + .../caps_8.0.0_x86_64.xml | 1 + .../caps_8.1.0_x86_64.xml | 1 + .../caps_8.2.0_aarch64.xml | 1 + .../caps_8.2.0_armv7l.xml | 1 + .../caps_8.2.0_loongarch64.xml | 1 + .../caps_8.2.0_x86_64.xml | 1 + .../caps_9.0.0_x86_64.xml | 1 + .../caps_9.1.0_x86_64.xml | 1 + .../pstore-acpi-erst.x86_64-latest.args | 38 +++++ .../pstore-acpi-erst.x86_64-latest.xml | 1 + tests/qemuxmlconfdata/pstore-acpi-erst.xml | 53 ++++++ tests/qemuxmlconftest.c | 1 + 47 files changed, 532 insertions(+) create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args create mode 120000 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.xml -- 2.44.2

This capability tracks whether QEMU has acpi-erst device. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml | 1 + tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml | 1 + tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml | 1 + tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml | 1 + tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml | 1 + tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml | 1 + 19 files changed, 20 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 17db563b09..29dfe8d35a 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -712,6 +712,7 @@ VIR_ENUM_IMPL(virQEMUCaps, /* 460 */ "sev-snp-guest", /* QEMU_CAPS_SEV_SNP_GUEST */ "netdev.user", /* QEMU_CAPS_NETDEV_USER */ + "acpi-erst", /* QEMU_CAPS_DEVICE_ACPI_ERST */ ); @@ -1398,6 +1399,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "virtio-sound-pci", QEMU_CAPS_DEVICE_VIRTIO_SOUND }, { "virtio-sound-device", QEMU_CAPS_DEVICE_VIRTIO_SOUND }, { "sev-snp-guest", QEMU_CAPS_SEV_SNP_GUEST }, + { "acpi-erst", QEMU_CAPS_DEVICE_ACPI_ERST }, }; diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 910181993c..51d951771d 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -691,6 +691,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 460 */ QEMU_CAPS_SEV_SNP_GUEST, /* -object sev-snp-guest */ QEMU_CAPS_NETDEV_USER, /* -netdev user */ + QEMU_CAPS_DEVICE_ACPI_ERST, /* -device acpi-erst */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml index 69d0701dc9..13b67cc7bf 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml @@ -159,6 +159,7 @@ <flag name='usb-mtp'/> <flag name='machine.virt.ras'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>6002092</version> <microcodeVersion>61700243</microcodeVersion> <package>v7.0.0-rc2</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml index eaed5601ed..a9fe9a2014 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml @@ -159,6 +159,7 @@ <flag name='usb-mtp'/> <flag name='machine.virt.ras'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>6002092</version> <microcodeVersion>61700243</microcodeVersion> <package>v7.0.0-rc2</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml b/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml index a49d45a68a..92636bc7d4 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml @@ -157,6 +157,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7000000</version> <microcodeVersion>42900243</microcodeVersion> <package>v7.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml index 5c57ae8eb8..fea02f7728 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml @@ -187,6 +187,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7000000</version> <microcodeVersion>43100243</microcodeVersion> <package>v7.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml b/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml index 5aeb56bac6..a851d89974 100644 --- a/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml @@ -158,6 +158,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7001000</version> <microcodeVersion>42900244</microcodeVersion> <package>v7.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml index 3c589a4d61..555120e958 100644 --- a/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml @@ -191,6 +191,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7001000</version> <microcodeVersion>43100244</microcodeVersion> <package>v7.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml b/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml index c5e1b23fb7..998c548556 100644 --- a/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml +++ b/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml @@ -153,6 +153,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7002000</version> <microcodeVersion>0</microcodeVersion> <package>qemu-7.2.0-6.fc37</package> diff --git a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml index f78e596fcc..ca25a5549e 100644 --- a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml +++ b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml @@ -195,6 +195,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7002000</version> <microcodeVersion>43100245</microcodeVersion> <package>v7.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml index 9b37253d4f..8b835e6187 100644 --- a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml @@ -195,6 +195,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>7002000</version> <microcodeVersion>43100245</microcodeVersion> <package>v7.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml index bd391ca0b6..3ac9e96066 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml @@ -199,6 +199,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>8000000</version> <microcodeVersion>43100244</microcodeVersion> <package>v8.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml index 4b7809f635..476195d147 100644 --- a/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml @@ -201,6 +201,7 @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>8001000</version> <microcodeVersion>43100245</microcodeVersion> <package>v8.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml index b856bc55b3..0043443880 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml @@ -168,6 +168,7 @@ <flag name='machine.virt.ras'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>8002000</version> <microcodeVersion>61700246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml b/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml index 1b95b2de85..16377a74e3 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml @@ -175,6 +175,7 @@ <flag name='machine.virt.ras'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>8002000</version> <microcodeVersion>0</microcodeVersion> <package>qemu-8.2.0-7.fc39</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml b/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml index aaf8f32485..eb80be7a06 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml @@ -158,6 +158,7 @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>8002000</version> <microcodeVersion>106300246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml index a1a5d6fd0f..25cb22aafc 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml @@ -204,6 +204,7 @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>8002000</version> <microcodeVersion>43100246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml index ab841db53b..b646377dec 100644 --- a/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml @@ -206,6 +206,7 @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>9000000</version> <microcodeVersion>43100245</microcodeVersion> <package>v9.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml index 05cba09035..dc548583a5 100644 --- a/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml @@ -204,6 +204,7 @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='acpi-erst'/> <version>9000050</version> <microcodeVersion>43100246</microcodeVersion> <package>v9.0.0-1388-g80e8f06021-dirty</package> -- 2.44.2

The aim of pstore device is to provide a bit of NVRAM storage for guest kernel to record oops/panic logs just before the it crashes. Typical usage includes usage in combination with a watchdog so that the logs can be inspected after the watchdog rebooted the machine. While Linux kernel (and possibly Windows too) support many backends, in QEMU there's just 'acpi-erst' device so stick with that for now. The device must be attached to a PCI bus and needs two additional values (well, corresponding memory-backend-file needs them): size and path. Despite using memory-backeng-file this does NOT add any additional RAM to the guest and thus I've decided to expose it as another device type instead of memory model. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.rst | 32 ++++ src/ch/ch_domain.c | 1 + src/conf/domain_conf.c | 153 ++++++++++++++++++ src/conf/domain_conf.h | 19 +++ src/conf/domain_postparse.c | 1 + src/conf/domain_validate.c | 30 ++++ src/conf/schemas/domaincommon.rng | 25 +++ src/conf/virconftypes.h | 2 + src/hyperv/hyperv_driver.c | 1 + src/libvirt_private.syms | 2 + src/libxl/libxl_driver.c | 6 + src/lxc/lxc_driver.c | 6 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain.c | 3 + src/qemu/qemu_domain_address.c | 11 ++ src/qemu/qemu_driver.c | 3 + src/qemu/qemu_hotplug.c | 5 + src/qemu/qemu_validate.c | 26 +++ src/test/test_driver.c | 1 + .../pstore-acpi-erst.x86_64-latest.args | 36 +++++ .../pstore-acpi-erst.x86_64-latest.xml | 1 + tests/qemuxmlconfdata/pstore-acpi-erst.xml | 53 ++++++ tests/qemuxmlconftest.c | 1 + 23 files changed, 419 insertions(+) create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args create mode 120000 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.xml diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 10584dfe83..8b35005c9e 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8659,6 +8659,38 @@ The optional attribute ``backend`` is required if the ``type`` is ``qemu``, the ... +Pstore +~~~~~~~~~ + +Pstore is an oops/panic logger that writes its logs to a block device and +non-block device before the system crashes. Currently only ACPI Error Record +Serialization Table, ERST, is supported. This feature is designed for storing +error records in persistent storage for future reference and/or debugging. +:since:`Since v10.6.0` + +:: + + ... + <pstore backend='acpi-erst'> + <path>/tmp/guest_acpi_esrt</path> + <size unit='KiB'>8</size> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </pstore> + ... + +The ``pstore`` element has one mandatory attribute ``backend`` which selects +desired backend (only ``acpi-erst`` is accepted for now). Then it has the +following child elements: + +``path`` + Represents a path in the host that backs the pstore device in the guest. It + is mandatory. + +``size`` + Configures the size of the persistent storage available to the guest. It is + mandatory. + + Security label -------------- diff --git a/src/ch/ch_domain.c b/src/ch/ch_domain.c index 8e3e205c8c..e1e14554a8 100644 --- a/src/ch/ch_domain.c +++ b/src/ch/ch_domain.c @@ -180,6 +180,7 @@ chValidateDomainDeviceDef(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Cloud-Hypervisor doesn't support '%1$s' device"), virDomainDeviceTypeToString(dev->type)); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6733857a3a..12ae8a9526 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -337,6 +337,7 @@ VIR_ENUM_IMPL(virDomainDevice, "vsock", "audio", "crypto", + "pstore", ); VIR_ENUM_IMPL(virDomainDiskDevice, @@ -1513,6 +1514,11 @@ VIR_ENUM_IMPL(virDomainLaunchSecurity, "s390-pv", ); +VIR_ENUM_IMPL(virDomainPstoreBackend, + VIR_DOMAIN_PSTORE_BACKEND_LAST, + "acpi-erst", +); + typedef enum { VIR_DOMAIN_NET_VHOSTUSER_MODE_NONE, VIR_DOMAIN_NET_VHOSTUSER_MODE_CLIENT, @@ -3548,6 +3554,16 @@ void virDomainMemoryDefFree(virDomainMemoryDef *def) g_free(def); } +void virDomainPstoreDefFree(virDomainPstoreDef *def) +{ + if (!def) + return; + + g_free(def->path); + virDomainDeviceInfoClear(&def->info); + g_free(def); +} + void virDomainDeviceDefFree(virDomainDeviceDef *def) { if (!def) @@ -3632,6 +3648,9 @@ void virDomainDeviceDefFree(virDomainDeviceDef *def) case VIR_DOMAIN_DEVICE_CRYPTO: virDomainCryptoDefFree(def->data.crypto); break; + case VIR_DOMAIN_DEVICE_PSTORE: + virDomainPstoreDefFree(def->data.pstore); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -3997,6 +4016,8 @@ void virDomainDefFree(virDomainDef *def) virDomainIOMMUDefFree(def->iommu); + virDomainPstoreDefFree(def->pstore); + g_free(def->idmap.uidmap); g_free(def->idmap.gidmap); @@ -4554,6 +4575,8 @@ virDomainDeviceGetInfo(const virDomainDeviceDef *device) return &device->data.vsock->info; case VIR_DOMAIN_DEVICE_CRYPTO: return &device->data.crypto->info; + case VIR_DOMAIN_DEVICE_PSTORE: + return &device->data.pstore->info; /* The following devices do not contain virDomainDeviceInfo */ case VIR_DOMAIN_DEVICE_LEASE: @@ -4659,6 +4682,9 @@ virDomainDeviceSetData(virDomainDeviceDef *device, case VIR_DOMAIN_DEVICE_CRYPTO: device->data.crypto = devicedata; break; + case VIR_DOMAIN_DEVICE_PSTORE: + device->data.pstore = devicedata; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -4877,6 +4903,13 @@ virDomainDeviceInfoIterateFlags(virDomainDef *def, return rc; } + device.type = VIR_DOMAIN_DEVICE_PSTORE; + if (def->pstore) { + device.data.pstore = def->pstore; + if ((rc = cb(def, &device, &def->pstore->info, opaque)) != 0) + return rc; + } + /* If the flag below is set, make sure @cb can handle @info being NULL */ if (iteratorFlags & DOMAIN_DEVICE_ITERATE_MISSING_INFO) { device.type = VIR_DOMAIN_DEVICE_GRAPHICS; @@ -4936,6 +4969,7 @@ virDomainDeviceInfoIterateFlags(virDomainDef *def, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; } #endif @@ -14006,6 +14040,40 @@ virDomainCryptoDefParseXML(virDomainXMLOption *xmlopt, } +static virDomainPstoreDef * +virDomainPstoreDefParseXML(virDomainXMLOption *xmlopt, + xmlNodePtr node, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + g_autoptr(virDomainPstoreDef) def = NULL; + VIR_XPATH_NODE_AUTORESTORE(ctxt) + + def = g_new0(virDomainPstoreDef, 1); + + ctxt->node = node; + + if (virXMLPropEnum(node, "backend", + virDomainPstoreBackendTypeFromString, + VIR_XML_PROP_REQUIRED, + &def->backend) < 0) { + return NULL; + } + + def->path = virXPathString("string(./path)", ctxt); + + if (virDomainParseMemory("./size", "./size/@unit", ctxt, + &def->size, true, false) < 0) { + return NULL; + } + + if (virDomainDeviceInfoParseXML(xmlopt, node, ctxt, &def->info, flags) < 0) + return NULL; + + return g_steal_pointer(&def); +} + + static int virDomainDeviceDefParseType(const char *typestr, virDomainDeviceType *type) @@ -14185,6 +14253,12 @@ virDomainDeviceDefParse(const char *xmlStr, flags))) return NULL; break; + case VIR_DOMAIN_DEVICE_PSTORE: + if (!(dev->data.pstore = virDomainPstoreDefParseXML(xmlopt, node, + ctxt, flags))) { + return NULL; + } + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -19532,6 +19606,22 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt, } VIR_FREE(nodes); + if ((n = virXPathNodeSet("./devices/pstore", ctxt, &nodes)) < 0) + return NULL; + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only a single pstore device is supported")); + return NULL; + } + + if (n > 0) { + if (!(def->pstore = virDomainPstoreDefParseXML(xmlopt, nodes[0], + ctxt, flags))) + return NULL; + } + VIR_FREE(nodes); + /* analysis of the user namespace mapping */ if ((n = virXPathNodeSet("./idmap/uid", ctxt, &nodes)) < 0) return NULL; @@ -21404,6 +21494,33 @@ virDomainVsockDefCheckABIStability(virDomainVsockDef *src, } +static bool +virDomainPstoreDefCheckABIStability(virDomainPstoreDef *src, + virDomainPstoreDef *dst) +{ + if (src->backend != dst->backend) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target pstore device backend '%1$s' does not match source '%2$s'"), + virDomainPstoreBackendTypeToString(dst->backend), + virDomainPstoreBackendTypeToString(src->backend)); + return false; + } + + if (src->size != dst->size) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target pstore size '%1$llu' does not match source '%2$llu'"), + dst->size, + src->size); + return false; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + return false; + + return true; +} + + static bool virDomainDefVcpuCheckAbiStability(virDomainDef *src, virDomainDef *dst) @@ -21863,6 +21980,17 @@ virDomainDefCheckABIStabilityFlags(virDomainDef *src, !virDomainVsockDefCheckABIStability(src->vsock, dst->vsock)) goto error; + if (!!src->pstore != !!dst->pstore) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target domain pstore device count does not match source")); + goto error; + } + + if (src->pstore && + !virDomainPstoreDefCheckABIStability(src->pstore, dst->pstore)) { + goto error; + } + if (xmlopt && xmlopt->abi.domain && !xmlopt->abi.domain(src, dst)) goto error; @@ -21903,6 +22031,7 @@ virDomainDefCheckABIStabilityFlags(virDomainDef *src, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; } #endif @@ -27849,6 +27978,26 @@ virDomainDefFormatFeatures(virBuffer *buf, return 0; } +static int +virDomainPstoreDefFormat(virBuffer *buf, + virDomainPstoreDef *pstore, + unsigned int flags) +{ + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); + + virBufferAsprintf(&attrBuf, " backend='%s'", + virDomainPstoreBackendTypeToString(pstore->backend)); + + virBufferAsprintf(&childBuf, "<path>%s</path>\n", pstore->path); + virBufferAsprintf(&childBuf, "<size unit='KiB'>%llu</size>\n", pstore->size); + virDomainDeviceInfoFormat(&childBuf, &pstore->info, flags); + + virXMLFormatElement(buf, "pstore", &attrBuf, &childBuf); + return 0; +} + + int virDomainDefFormatInternal(virDomainDef *def, virDomainXMLOption *xmlopt, @@ -28320,6 +28469,9 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def, if (def->vsock) virDomainVsockDefFormat(buf, def->vsock); + if (def->pstore) + virDomainPstoreDefFormat(buf, def->pstore, flags); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</devices>\n"); @@ -28479,6 +28631,7 @@ virDomainDeviceIsUSB(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8283493dfc..546c14e7b0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -87,6 +87,7 @@ typedef enum { VIR_DOMAIN_DEVICE_VSOCK, VIR_DOMAIN_DEVICE_AUDIO, VIR_DOMAIN_DEVICE_CRYPTO, + VIR_DOMAIN_DEVICE_PSTORE, VIR_DOMAIN_DEVICE_LAST } virDomainDeviceType; @@ -120,6 +121,7 @@ struct _virDomainDeviceDef { virDomainVsockDef *vsock; virDomainAudioDef *audio; virDomainCryptoDef *crypto; + virDomainPstoreDef *pstore; } data; }; @@ -2983,6 +2985,19 @@ struct _virDomainVirtioOptions { virTristateSwitch page_per_vq; }; +typedef enum { + VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST, + + VIR_DOMAIN_PSTORE_BACKEND_LAST +} virDomainPstoreBackend; + +struct _virDomainPstoreDef { + virDomainPstoreBackend backend; + unsigned long long size; + char *path; + virDomainDeviceInfo info; +}; + #define SCSI_SUPER_WIDE_BUS_MAX_CONT_UNIT 64 #define SCSI_WIDE_BUS_MAX_CONT_UNIT 16 @@ -3159,6 +3174,7 @@ struct _virDomainDef { virDomainRedirFilterDef *redirfilter; virDomainIOMMUDef *iommu; virDomainVsockDef *vsock; + virDomainPstoreDef *pstore; void *namespaceData; virXMLNamespace ns; @@ -3598,6 +3614,8 @@ void virDomainVsockDefFree(virDomainVsockDef *vsock); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainVsockDef, virDomainVsockDefFree); void virDomainCryptoDefFree(virDomainCryptoDef *def); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainCryptoDef, virDomainCryptoDefFree); +void virDomainPstoreDefFree(virDomainPstoreDef *def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainPstoreDef, virDomainPstoreDefFree); void virDomainNetTeamingInfoFree(virDomainNetTeamingInfo *teaming); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainNetTeamingInfo, virDomainNetTeamingInfoFree); void virDomainNetPortForwardFree(virDomainNetPortForward *pf); @@ -4268,6 +4286,7 @@ VIR_ENUM_DECL(virDomainCryptoBackend); VIR_ENUM_DECL(virDomainShmemModel); VIR_ENUM_DECL(virDomainShmemRole); VIR_ENUM_DECL(virDomainLaunchSecurity); +VIR_ENUM_DECL(virDomainPstoreBackend); /* from libvirt.h */ VIR_ENUM_DECL(virDomainState); VIR_ENUM_DECL(virDomainNostateReason); diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c index 112795ea65..bf33f29638 100644 --- a/src/conf/domain_postparse.c +++ b/src/conf/domain_postparse.c @@ -757,6 +757,7 @@ virDomainDeviceDefPostParseCommon(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: ret = 0; break; diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 8209e8fdae..39b8d67928 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -3022,6 +3022,33 @@ virDomainTPMDevValidate(const virDomainTPMDef *tpm) } +static int +virDomainPstoreDefValidate(const virDomainPstoreDef *pstore) +{ + if (pstore->backend != VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported backend for pstore device: %1$s"), + virDomainPstoreBackendTypeToString(pstore->backend)); + return -1; + } + + if (pstore->path == NULL || pstore->path[0] == '\0') { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing path for ACPI ERST pstore device")); + return -1; + } + + if (pstore->size < 4 || + !VIR_IS_POW2(pstore->size)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid size of ACPI ERST pstore device")); + return -1; + } + + return 0; +} + + static int virDomainDeviceInfoValidate(const virDomainDeviceDef *dev) { @@ -3132,6 +3159,9 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_TPM: return virDomainTPMDevValidate(dev->data.tpm); + case VIR_DOMAIN_DEVICE_PSTORE: + return virDomainPstoreDefValidate(dev->data.pstore); + case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_WATCHDOG: case VIR_DOMAIN_DEVICE_HUB: diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index ab5374d5f0..200009efa5 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6255,6 +6255,28 @@ </element> </define> + <define name="pstore"> + <element name="pstore"> + <attribute name="backend"> + <value>acpi-erst</value> + </attribute> + <interleave> + <element name="path"> + <ref name="absFilePath"/> + </element> + <element name="size"> + <ref name="scaledInteger"/> + </element> + <optional> + <ref name="address"/> + </optional> + <optional> + <ref name="alias"/> + </optional> + </interleave> + </element> + </define> + <define name="hostdev"> <element name="hostdev"> <interleave> @@ -6724,6 +6746,9 @@ <optional> <ref name="vsock"/> </optional> + <optional> + <ref name="pstore"/> + </optional> </interleave> </element> </define> diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index d8e7c5278c..f18ebcca10 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -260,6 +260,8 @@ typedef struct _virDomainVsockDef virDomainVsockDef; typedef struct _virDomainCryptoDef virDomainCryptoDef; +typedef struct _virDomainPstoreDef virDomainPstoreDef; + typedef struct _virDomainWatchdogDef virDomainWatchdogDef; typedef struct _virDomainXMLOption virDomainXMLOption; diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 7580c6a06c..43ccb9cbd7 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -3133,6 +3133,7 @@ hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_INTERNAL_ERROR, _("Attaching devices of type %1$d is not implemented"), dev->type); return -1; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c35366c9e1..d15d6a6a9d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -616,6 +616,8 @@ virDomainPausedReasonTypeToString; virDomainPMSuspendedReasonTypeFromString; virDomainPMSuspendedReasonTypeToString; virDomainProcessCapsFeatureTypeToString; +virDomainPstoreBackendTypeFromString; +virDomainPstoreBackendTypeToString; virDomainRedirdevBusTypeFromString; virDomainRedirdevBusTypeToString; virDomainRedirdevDefFind; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 7dcae58413..e72553603d 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3505,6 +3505,7 @@ libxlDomainAttachDeviceLive(libxlDriverPrivate *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be attached"), virDomainDeviceTypeToString(dev->type)); @@ -3613,6 +3614,7 @@ libxlDomainAttachDeviceConfig(virDomainDef *vmdef, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); return -1; @@ -3981,6 +3983,7 @@ libxlDomainDetachDeviceLive(libxlDriverPrivate *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be detached"), virDomainDeviceTypeToString(dev->type)); @@ -4071,6 +4074,7 @@ libxlDomainDetachDeviceConfig(virDomainDef *vmdef, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent detach of device is not supported")); return -1; @@ -4133,6 +4137,7 @@ libxlDomainUpdateDeviceLive(virDomainObj *vm, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be updated"), virDomainDeviceTypeToString(dev->type)); @@ -4195,6 +4200,7 @@ libxlDomainUpdateDeviceConfig(virDomainDef *vmdef, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent update of device is not supported")); return -1; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index f76d09e8a9..534e257f30 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3056,6 +3056,7 @@ lxcDomainAttachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); break; @@ -3121,6 +3122,7 @@ lxcDomainUpdateDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent update of device is not supported")); break; @@ -3202,6 +3204,7 @@ lxcDomainDetachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_CRYPTO: case VIR_DOMAIN_DEVICE_AUDIO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent detach of device is not supported")); break; @@ -3303,6 +3306,7 @@ lxcDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UNUSED, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected device type %1$d"), data->def->type); @@ -3974,6 +3978,7 @@ lxcDomainAttachDeviceLive(virLXCDriver *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be attached"), virDomainDeviceTypeToString(dev->type)); @@ -4391,6 +4396,7 @@ lxcDomainDetachDeviceLive(virLXCDriver *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be detached"), virDomainDeviceTypeToString(dev->type)); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 684de3f701..def124cc27 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -973,6 +973,7 @@ qemuBuildVirtioDevGetConfigDev(const virDomainDeviceDef *device, case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: default: break; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 2134b11038..a8d7a1e525 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -6358,6 +6358,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: ret = 0; break; @@ -10360,6 +10361,7 @@ qemuDomainPrepareChardevSourceOne(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; } @@ -12290,6 +12292,7 @@ qemuDomainDeviceBackendChardevForeachOne(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: /* no chardev backend */ break; } diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 251f5b7e1a..970ae3949d 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -470,6 +470,7 @@ qemuDomainDeviceSupportZPCI(virDomainDeviceDef *device) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; case VIR_DOMAIN_DEVICE_NONE: @@ -1002,6 +1003,9 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDef *dev, } break; + case VIR_DOMAIN_DEVICE_PSTORE: + return pciFlags; + /* These devices don't ever connect with PCI */ case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_TPM: @@ -2424,6 +2428,13 @@ qemuDomainAssignDevicePCISlots(virDomainDef *def, } } + if (def->pstore && + virDeviceInfoPCIAddressIsWanted(&def->pstore->info)) { + if (qemuDomainPCIAddressReserveNextAddr(addrs, + &def->pstore->info) < 0) + return -1; + } + return 0; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9f3013e231..e473018ef2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6863,6 +6863,7 @@ qemuDomainAttachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent attach of device '%1$s' is not supported"), @@ -7081,6 +7082,7 @@ qemuDomainDetachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent detach of device '%1$s' is not supported"), @@ -7206,6 +7208,7 @@ qemuDomainUpdateDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent update of device '%1$s' is not supported"), diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f4620d833..af4229d507 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3452,6 +3452,7 @@ qemuDomainAttachDeviceLive(virDomainObj *vm, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live attach of device '%1$s' is not supported"), @@ -5282,6 +5283,7 @@ qemuDomainRemoveAuditDevice(virDomainObj *vm, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: /* libvirt doesn't yet support detaching these devices */ break; @@ -5386,6 +5388,7 @@ qemuDomainRemoveDevice(virQEMUDriver *driver, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("don't know how to remove a %1$s device"), @@ -6270,6 +6273,7 @@ qemuDomainDetachDeviceLive(virDomainObj *vm, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live detach of device '%1$s' is not supported"), @@ -7259,6 +7263,7 @@ qemuDomainUpdateDeviceLive(virDomainObj *vm, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("live update of device '%1$s' is not supported"), diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 8840306bfd..0e8f0f977f 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4669,6 +4669,29 @@ qemuValidateDomainDeviceDefCrypto(virDomainCryptoDef *crypto, } +static int +qemuValidateDomainDeviceDefPstore(virDomainPstoreDef *pstore, + const virDomainDef *def G_GNUC_UNUSED, + virQEMUCaps *qemuCaps) +{ + if (pstore->backend == VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ACPI_ERST)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("acpi-erst backend of pstore device is not supported")); + return -1; + } + + if (pstore->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + pstore->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("ACPI ERST device must reside on a PCI bus")); + return -1; + } + + return 0; +} + + static int qemuSoundCodecTypeToCaps(int type) { @@ -5372,6 +5395,9 @@ qemuValidateDomainDeviceDef(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_CRYPTO: return qemuValidateDomainDeviceDefCrypto(dev->data.crypto, def, qemuCaps); + case VIR_DOMAIN_DEVICE_PSTORE: + return qemuValidateDomainDeviceDefPstore(dev->data.pstore, def, qemuCaps); + case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_NONE: diff --git a/src/test/test_driver.c b/src/test/test_driver.c index df6a86ea44..7cb77f044d 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -10294,6 +10294,7 @@ testDomainUpdateDevice(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent update of device '%1$s' is not supported"), diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args new file mode 100644 index 0000000000..d7c4708acb --- /dev/null +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args @@ -0,0 +1,36 @@ +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"}' \ +-machine pc-q35-9.0,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=on \ +-accel kvm \ +-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=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \ +-device '{"driver":"pcie-pci-bridge","id":"pci.2","bus":"pci.1","addr":"0x0"}' \ +-device '{"driver":"pcie-root-port","port":9,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x1"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-global ICH9-LPC.noreboot=off \ +-watchdog-action reset \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml new file mode 120000 index 0000000000..11ade68605 --- /dev/null +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml @@ -0,0 +1 @@ +pstore-acpi-erst.xml \ No newline at end of file diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.xml b/tests/qemuxmlconfdata/pstore-acpi-erst.xml new file mode 100644 index 0000000000..9b9ba266b2 --- /dev/null +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.xml @@ -0,0 +1,53 @@ +<domain type='kvm'> + <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> + <type arch='x86_64' machine='pc-q35-9.0'>hvm</type> + <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'/> + <controller type='pci' index='1' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='1' port='0x8'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/> + </controller> + <controller type='pci' index='2' model='pcie-to-pci-bridge'> + <model name='pcie-pci-bridge'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> + </controller> + <controller type='pci' index='3' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='3' port='0x9'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <watchdog model='itco' action='reset'/> + <memballoon model='none'/> + <pstore backend='acpi-erst'> + <path>/tmp/guest_acpi_esrt</path> + <size unit='KiB'>8</size> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </pstore> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index cc984440ea..68d1127cf4 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2949,6 +2949,7 @@ mymain(void) DO_TEST_CAPS_LATEST("mtp-usb-device") DO_TEST_CAPS_LATEST("net-usb") DO_TEST_CAPS_LATEST("sound-device-virtio") + DO_TEST_CAPS_LATEST("pstore-acpi-erst") DO_TEST_CAPS_LATEST_FAILURE("disk-network-iscsi-zero-hosts-invalid") DO_TEST_CAPS_LATEST_PARSE_ERROR("hostdev-scsi-vhost-rawio-invalid") -- 2.44.2

On Mon, Jul 22, 2024 at 2:32 PM Michal Privoznik <mprivozn@redhat.com> wrote:
The aim of pstore device is to provide a bit of NVRAM storage for guest kernel to record oops/panic logs just before the it crashes. Typical usage includes usage in combination with a watchdog so that the logs can be inspected after the watchdog rebooted the machine. While Linux kernel (and possibly Windows too) support many backends, in QEMU there's just 'acpi-erst' device so stick with that for now. The device must be attached to a PCI bus and needs two additional values (well, corresponding memory-backend-file needs them): size and path. Despite using memory-backeng-file this does NOT add any additional RAM to the
*memory-backend-file this does.... guest and thus I've decided to expose it as another device type
instead of memory model.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.rst | 32 ++++ src/ch/ch_domain.c | 1 + src/conf/domain_conf.c | 153 ++++++++++++++++++ src/conf/domain_conf.h | 19 +++ src/conf/domain_postparse.c | 1 + src/conf/domain_validate.c | 30 ++++ src/conf/schemas/domaincommon.rng | 25 +++ src/conf/virconftypes.h | 2 + src/hyperv/hyperv_driver.c | 1 + src/libvirt_private.syms | 2 + src/libxl/libxl_driver.c | 6 + src/lxc/lxc_driver.c | 6 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain.c | 3 + src/qemu/qemu_domain_address.c | 11 ++ src/qemu/qemu_driver.c | 3 + src/qemu/qemu_hotplug.c | 5 + src/qemu/qemu_validate.c | 26 +++ src/test/test_driver.c | 1 + .../pstore-acpi-erst.x86_64-latest.args | 36 +++++ .../pstore-acpi-erst.x86_64-latest.xml | 1 + tests/qemuxmlconfdata/pstore-acpi-erst.xml | 53 ++++++ tests/qemuxmlconftest.c | 1 + 23 files changed, 419 insertions(+) create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args create mode 120000 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 10584dfe83..8b35005c9e 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8659,6 +8659,38 @@ The optional attribute ``backend`` is required if the ``type`` is ``qemu``, the ...
+Pstore +~~~~~~~~~ + +Pstore is an oops/panic logger that writes its logs to a block device and +non-block device before the system crashes. Currently only ACPI Error Record +Serialization Table, ERST, is supported. This feature is designed for storing +error records in persistent storage for future reference and/or debugging. +:since:`Since v10.6.0` + +:: + + ... + <pstore backend='acpi-erst'> + <path>/tmp/guest_acpi_esrt</path> + <size unit='KiB'>8</size> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </pstore> + ... + +The ``pstore`` element has one mandatory attribute ``backend`` which selects +desired backend (only ``acpi-erst`` is accepted for now). Then it has the +following child elements: + +``path`` + Represents a path in the host that backs the pstore device in the guest. It + is mandatory. + +``size`` + Configures the size of the persistent storage available to the guest. It is + mandatory. + + Security label --------------
diff --git a/src/ch/ch_domain.c b/src/ch/ch_domain.c index 8e3e205c8c..e1e14554a8 100644 --- a/src/ch/ch_domain.c +++ b/src/ch/ch_domain.c @@ -180,6 +180,7 @@ chValidateDomainDeviceDef(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Cloud-Hypervisor doesn't support '%1$s' device"), virDomainDeviceTypeToString(dev->type)); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6733857a3a..12ae8a9526 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -337,6 +337,7 @@ VIR_ENUM_IMPL(virDomainDevice, "vsock", "audio", "crypto", + "pstore", );
VIR_ENUM_IMPL(virDomainDiskDevice, @@ -1513,6 +1514,11 @@ VIR_ENUM_IMPL(virDomainLaunchSecurity, "s390-pv", );
+VIR_ENUM_IMPL(virDomainPstoreBackend, + VIR_DOMAIN_PSTORE_BACKEND_LAST, + "acpi-erst", +); + typedef enum { VIR_DOMAIN_NET_VHOSTUSER_MODE_NONE, VIR_DOMAIN_NET_VHOSTUSER_MODE_CLIENT, @@ -3548,6 +3554,16 @@ void virDomainMemoryDefFree(virDomainMemoryDef *def) g_free(def); }
+void virDomainPstoreDefFree(virDomainPstoreDef *def) +{ + if (!def) + return; + + g_free(def->path); + virDomainDeviceInfoClear(&def->info); + g_free(def); +} + void virDomainDeviceDefFree(virDomainDeviceDef *def) { if (!def) @@ -3632,6 +3648,9 @@ void virDomainDeviceDefFree(virDomainDeviceDef *def) case VIR_DOMAIN_DEVICE_CRYPTO: virDomainCryptoDefFree(def->data.crypto); break; + case VIR_DOMAIN_DEVICE_PSTORE: + virDomainPstoreDefFree(def->data.pstore); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -3997,6 +4016,8 @@ void virDomainDefFree(virDomainDef *def)
virDomainIOMMUDefFree(def->iommu);
+ virDomainPstoreDefFree(def->pstore); + g_free(def->idmap.uidmap); g_free(def->idmap.gidmap);
@@ -4554,6 +4575,8 @@ virDomainDeviceGetInfo(const virDomainDeviceDef *device) return &device->data.vsock->info; case VIR_DOMAIN_DEVICE_CRYPTO: return &device->data.crypto->info; + case VIR_DOMAIN_DEVICE_PSTORE: + return &device->data.pstore->info;
/* The following devices do not contain virDomainDeviceInfo */ case VIR_DOMAIN_DEVICE_LEASE: @@ -4659,6 +4682,9 @@ virDomainDeviceSetData(virDomainDeviceDef *device, case VIR_DOMAIN_DEVICE_CRYPTO: device->data.crypto = devicedata; break; + case VIR_DOMAIN_DEVICE_PSTORE: + device->data.pstore = devicedata; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -4877,6 +4903,13 @@ virDomainDeviceInfoIterateFlags(virDomainDef *def, return rc; }
+ device.type = VIR_DOMAIN_DEVICE_PSTORE; + if (def->pstore) { + device.data.pstore = def->pstore; + if ((rc = cb(def, &device, &def->pstore->info, opaque)) != 0) + return rc; + } + /* If the flag below is set, make sure @cb can handle @info being NULL */ if (iteratorFlags & DOMAIN_DEVICE_ITERATE_MISSING_INFO) { device.type = VIR_DOMAIN_DEVICE_GRAPHICS; @@ -4936,6 +4969,7 @@ virDomainDeviceInfoIterateFlags(virDomainDef *def, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; } #endif @@ -14006,6 +14040,40 @@ virDomainCryptoDefParseXML(virDomainXMLOption *xmlopt, }
+static virDomainPstoreDef * +virDomainPstoreDefParseXML(virDomainXMLOption *xmlopt, + xmlNodePtr node, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + g_autoptr(virDomainPstoreDef) def = NULL; + VIR_XPATH_NODE_AUTORESTORE(ctxt) + + def = g_new0(virDomainPstoreDef, 1); + + ctxt->node = node; + + if (virXMLPropEnum(node, "backend", + virDomainPstoreBackendTypeFromString, + VIR_XML_PROP_REQUIRED, + &def->backend) < 0) { + return NULL; + } + + def->path = virXPathString("string(./path)", ctxt); + + if (virDomainParseMemory("./size", "./size/@unit", ctxt, + &def->size, true, false) < 0) { + return NULL; + } + + if (virDomainDeviceInfoParseXML(xmlopt, node, ctxt, &def->info, flags) < 0) + return NULL; + + return g_steal_pointer(&def); +} + + static int virDomainDeviceDefParseType(const char *typestr, virDomainDeviceType *type) @@ -14185,6 +14253,12 @@ virDomainDeviceDefParse(const char *xmlStr, flags))) return NULL; break; + case VIR_DOMAIN_DEVICE_PSTORE: + if (!(dev->data.pstore = virDomainPstoreDefParseXML(xmlopt, node, + ctxt, flags))) { + return NULL; + } + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -19532,6 +19606,22 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt, } VIR_FREE(nodes);
+ if ((n = virXPathNodeSet("./devices/pstore", ctxt, &nodes)) < 0) + return NULL; + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only a single pstore device is supported")); + return NULL; + } + + if (n > 0) { + if (!(def->pstore = virDomainPstoreDefParseXML(xmlopt, nodes[0], + ctxt, flags))) + return NULL; + } + VIR_FREE(nodes); + /* analysis of the user namespace mapping */ if ((n = virXPathNodeSet("./idmap/uid", ctxt, &nodes)) < 0) return NULL; @@ -21404,6 +21494,33 @@ virDomainVsockDefCheckABIStability(virDomainVsockDef *src, }
+static bool +virDomainPstoreDefCheckABIStability(virDomainPstoreDef *src, + virDomainPstoreDef *dst) +{ + if (src->backend != dst->backend) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target pstore device backend '%1$s' does not match source '%2$s'"), + virDomainPstoreBackendTypeToString(dst->backend), + virDomainPstoreBackendTypeToString(src->backend)); + return false; + } + + if (src->size != dst->size) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target pstore size '%1$llu' does not match source '%2$llu'"), + dst->size, + src->size); + return false; + } + + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + return false; + + return true; +} + + static bool virDomainDefVcpuCheckAbiStability(virDomainDef *src, virDomainDef *dst) @@ -21863,6 +21980,17 @@ virDomainDefCheckABIStabilityFlags(virDomainDef *src, !virDomainVsockDefCheckABIStability(src->vsock, dst->vsock)) goto error;
+ if (!!src->pstore != !!dst->pstore) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Target domain pstore device count does not match source")); + goto error; + } + + if (src->pstore && + !virDomainPstoreDefCheckABIStability(src->pstore, dst->pstore)) { + goto error; + } + if (xmlopt && xmlopt->abi.domain && !xmlopt->abi.domain(src, dst)) goto error; @@ -21903,6 +22031,7 @@ virDomainDefCheckABIStabilityFlags(virDomainDef *src, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; } #endif @@ -27849,6 +27978,26 @@ virDomainDefFormatFeatures(virBuffer *buf, return 0; }
+static int +virDomainPstoreDefFormat(virBuffer *buf, + virDomainPstoreDef *pstore, + unsigned int flags) +{ + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); + + virBufferAsprintf(&attrBuf, " backend='%s'", + virDomainPstoreBackendTypeToString(pstore->backend)); + + virBufferAsprintf(&childBuf, "<path>%s</path>\n", pstore->path); + virBufferAsprintf(&childBuf, "<size unit='KiB'>%llu</size>\n", pstore->size); + virDomainDeviceInfoFormat(&childBuf, &pstore->info, flags); + + virXMLFormatElement(buf, "pstore", &attrBuf, &childBuf); + return 0; +} + + int virDomainDefFormatInternal(virDomainDef *def, virDomainXMLOption *xmlopt, @@ -28320,6 +28469,9 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def, if (def->vsock) virDomainVsockDefFormat(buf, def->vsock);
+ if (def->pstore) + virDomainPstoreDefFormat(buf, def->pstore, flags); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</devices>\n");
@@ -28479,6 +28631,7 @@ virDomainDeviceIsUSB(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; }
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8283493dfc..546c14e7b0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -87,6 +87,7 @@ typedef enum { VIR_DOMAIN_DEVICE_VSOCK, VIR_DOMAIN_DEVICE_AUDIO, VIR_DOMAIN_DEVICE_CRYPTO, + VIR_DOMAIN_DEVICE_PSTORE,
VIR_DOMAIN_DEVICE_LAST } virDomainDeviceType; @@ -120,6 +121,7 @@ struct _virDomainDeviceDef { virDomainVsockDef *vsock; virDomainAudioDef *audio; virDomainCryptoDef *crypto; + virDomainPstoreDef *pstore; } data; };
@@ -2983,6 +2985,19 @@ struct _virDomainVirtioOptions { virTristateSwitch page_per_vq; };
+typedef enum { + VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST, + + VIR_DOMAIN_PSTORE_BACKEND_LAST +} virDomainPstoreBackend; + +struct _virDomainPstoreDef { + virDomainPstoreBackend backend; + unsigned long long size; + char *path; + virDomainDeviceInfo info; +}; +
#define SCSI_SUPER_WIDE_BUS_MAX_CONT_UNIT 64 #define SCSI_WIDE_BUS_MAX_CONT_UNIT 16 @@ -3159,6 +3174,7 @@ struct _virDomainDef { virDomainRedirFilterDef *redirfilter; virDomainIOMMUDef *iommu; virDomainVsockDef *vsock; + virDomainPstoreDef *pstore;
void *namespaceData; virXMLNamespace ns; @@ -3598,6 +3614,8 @@ void virDomainVsockDefFree(virDomainVsockDef *vsock); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainVsockDef, virDomainVsockDefFree); void virDomainCryptoDefFree(virDomainCryptoDef *def); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainCryptoDef, virDomainCryptoDefFree); +void virDomainPstoreDefFree(virDomainPstoreDef *def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainPstoreDef, virDomainPstoreDefFree); void virDomainNetTeamingInfoFree(virDomainNetTeamingInfo *teaming); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainNetTeamingInfo, virDomainNetTeamingInfoFree); void virDomainNetPortForwardFree(virDomainNetPortForward *pf); @@ -4268,6 +4286,7 @@ VIR_ENUM_DECL(virDomainCryptoBackend); VIR_ENUM_DECL(virDomainShmemModel); VIR_ENUM_DECL(virDomainShmemRole); VIR_ENUM_DECL(virDomainLaunchSecurity); +VIR_ENUM_DECL(virDomainPstoreBackend); /* from libvirt.h */ VIR_ENUM_DECL(virDomainState); VIR_ENUM_DECL(virDomainNostateReason); diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c index 112795ea65..bf33f29638 100644 --- a/src/conf/domain_postparse.c +++ b/src/conf/domain_postparse.c @@ -757,6 +757,7 @@ virDomainDeviceDefPostParseCommon(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: ret = 0; break;
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 8209e8fdae..39b8d67928 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -3022,6 +3022,33 @@ virDomainTPMDevValidate(const virDomainTPMDef *tpm) }
+static int +virDomainPstoreDefValidate(const virDomainPstoreDef *pstore) +{ + if (pstore->backend != VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported backend for pstore device: %1$s"), + virDomainPstoreBackendTypeToString(pstore->backend)); + return -1; + } + + if (pstore->path == NULL || pstore->path[0] == '\0') { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing path for ACPI ERST pstore device")); + return -1; + } + + if (pstore->size < 4 || + !VIR_IS_POW2(pstore->size)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid size of ACPI ERST pstore device")); + return -1; + } + + return 0; +} + + static int virDomainDeviceInfoValidate(const virDomainDeviceDef *dev) { @@ -3132,6 +3159,9 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_TPM: return virDomainTPMDevValidate(dev->data.tpm);
+ case VIR_DOMAIN_DEVICE_PSTORE: + return virDomainPstoreDefValidate(dev->data.pstore); + case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_WATCHDOG: case VIR_DOMAIN_DEVICE_HUB: diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index ab5374d5f0..200009efa5 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6255,6 +6255,28 @@ </element> </define>
+ <define name="pstore"> + <element name="pstore"> + <attribute name="backend"> + <value>acpi-erst</value> + </attribute> + <interleave> + <element name="path"> + <ref name="absFilePath"/> + </element> + <element name="size"> + <ref name="scaledInteger"/> + </element> + <optional> + <ref name="address"/> + </optional> + <optional> + <ref name="alias"/> + </optional> + </interleave> + </element> + </define> + <define name="hostdev"> <element name="hostdev"> <interleave> @@ -6724,6 +6746,9 @@ <optional> <ref name="vsock"/> </optional> + <optional> + <ref name="pstore"/> + </optional> </interleave> </element> </define> diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index d8e7c5278c..f18ebcca10 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -260,6 +260,8 @@ typedef struct _virDomainVsockDef virDomainVsockDef;
typedef struct _virDomainCryptoDef virDomainCryptoDef;
+typedef struct _virDomainPstoreDef virDomainPstoreDef; + typedef struct _virDomainWatchdogDef virDomainWatchdogDef;
typedef struct _virDomainXMLOption virDomainXMLOption; diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 7580c6a06c..43ccb9cbd7 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -3133,6 +3133,7 @@ hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_INTERNAL_ERROR, _("Attaching devices of type %1$d is not implemented"), dev->type); return -1; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c35366c9e1..d15d6a6a9d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -616,6 +616,8 @@ virDomainPausedReasonTypeToString; virDomainPMSuspendedReasonTypeFromString; virDomainPMSuspendedReasonTypeToString; virDomainProcessCapsFeatureTypeToString; +virDomainPstoreBackendTypeFromString; +virDomainPstoreBackendTypeToString; virDomainRedirdevBusTypeFromString; virDomainRedirdevBusTypeToString; virDomainRedirdevDefFind; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 7dcae58413..e72553603d 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3505,6 +3505,7 @@ libxlDomainAttachDeviceLive(libxlDriverPrivate *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be attached"), virDomainDeviceTypeToString(dev->type)); @@ -3613,6 +3614,7 @@ libxlDomainAttachDeviceConfig(virDomainDef *vmdef, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); return -1; @@ -3981,6 +3983,7 @@ libxlDomainDetachDeviceLive(libxlDriverPrivate *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be detached"), virDomainDeviceTypeToString(dev->type)); @@ -4071,6 +4074,7 @@ libxlDomainDetachDeviceConfig(virDomainDef *vmdef, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent detach of device is not supported")); return -1; @@ -4133,6 +4137,7 @@ libxlDomainUpdateDeviceLive(virDomainObj *vm, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be updated"), virDomainDeviceTypeToString(dev->type)); @@ -4195,6 +4200,7 @@ libxlDomainUpdateDeviceConfig(virDomainDef *vmdef, virDomainDeviceDef *dev) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent update of device is not supported")); return -1; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index f76d09e8a9..534e257f30 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3056,6 +3056,7 @@ lxcDomainAttachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent attach of device is not supported")); break; @@ -3121,6 +3122,7 @@ lxcDomainUpdateDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent update of device is not supported")); break; @@ -3202,6 +3204,7 @@ lxcDomainDetachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_CRYPTO: case VIR_DOMAIN_DEVICE_AUDIO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("persistent detach of device is not supported")); break; @@ -3303,6 +3306,7 @@ lxcDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UNUSED, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected device type %1$d"), data->def->type); @@ -3974,6 +3978,7 @@ lxcDomainAttachDeviceLive(virLXCDriver *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be attached"), virDomainDeviceTypeToString(dev->type)); @@ -4391,6 +4396,7 @@ lxcDomainDetachDeviceLive(virLXCDriver *driver, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%1$s' cannot be detached"), virDomainDeviceTypeToString(dev->type)); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 684de3f701..def124cc27 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -973,6 +973,7 @@ qemuBuildVirtioDevGetConfigDev(const virDomainDeviceDef *device, case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: default: break; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 2134b11038..a8d7a1e525 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -6358,6 +6358,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: ret = 0; break;
@@ -10360,6 +10361,7 @@ qemuDomainPrepareChardevSourceOne(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break; }
@@ -12290,6 +12292,7 @@ qemuDomainDeviceBackendChardevForeachOne(virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: /* no chardev backend */ break; } diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 251f5b7e1a..970ae3949d 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -470,6 +470,7 @@ qemuDomainDeviceSupportZPCI(virDomainDeviceDef *device) case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: break;
case VIR_DOMAIN_DEVICE_NONE: @@ -1002,6 +1003,9 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDef *dev, } break;
+ case VIR_DOMAIN_DEVICE_PSTORE: + return pciFlags; + /* These devices don't ever connect with PCI */ case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_TPM: @@ -2424,6 +2428,13 @@ qemuDomainAssignDevicePCISlots(virDomainDef *def, } }
+ if (def->pstore && + virDeviceInfoPCIAddressIsWanted(&def->pstore->info)) { + if (qemuDomainPCIAddressReserveNextAddr(addrs, + &def->pstore->info) < 0) + return -1; + } + return 0; }
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9f3013e231..e473018ef2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6863,6 +6863,7 @@ qemuDomainAttachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent attach of device '%1$s' is not supported"), @@ -7081,6 +7082,7 @@ qemuDomainDetachDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent detach of device '%1$s' is not supported"), @@ -7206,6 +7208,7 @@ qemuDomainUpdateDeviceConfig(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent update of device '%1$s' is not supported"), diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f4620d833..af4229d507 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3452,6 +3452,7 @@ qemuDomainAttachDeviceLive(virDomainObj *vm, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live attach of device '%1$s' is not supported"), @@ -5282,6 +5283,7 @@ qemuDomainRemoveAuditDevice(virDomainObj *vm, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: /* libvirt doesn't yet support detaching these devices */ break; @@ -5386,6 +5388,7 @@ qemuDomainRemoveDevice(virQEMUDriver *driver, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("don't know how to remove a %1$s device"), @@ -6270,6 +6273,7 @@ qemuDomainDetachDeviceLive(virDomainObj *vm, case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live detach of device '%1$s' is not supported"), @@ -7259,6 +7263,7 @@ qemuDomainUpdateDeviceLive(virDomainObj *vm, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("live update of device '%1$s' is not supported"), diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 8840306bfd..0e8f0f977f 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4669,6 +4669,29 @@ qemuValidateDomainDeviceDefCrypto(virDomainCryptoDef *crypto, }
+static int +qemuValidateDomainDeviceDefPstore(virDomainPstoreDef *pstore, + const virDomainDef *def G_GNUC_UNUSED, + virQEMUCaps *qemuCaps) +{ + if (pstore->backend == VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ACPI_ERST)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("acpi-erst backend of pstore device is not supported")); + return -1; + } + + if (pstore->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + pstore->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("ACPI ERST device must reside on a PCI bus")); + return -1; + } + + return 0; +} + + static int qemuSoundCodecTypeToCaps(int type) { @@ -5372,6 +5395,9 @@ qemuValidateDomainDeviceDef(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_CRYPTO: return qemuValidateDomainDeviceDefCrypto(dev->data.crypto, def, qemuCaps);
+ case VIR_DOMAIN_DEVICE_PSTORE: + return qemuValidateDomainDeviceDefPstore(dev->data.pstore, def, qemuCaps); + case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_NONE: diff --git a/src/test/test_driver.c b/src/test/test_driver.c index df6a86ea44..7cb77f044d 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -10294,6 +10294,7 @@ testDomainUpdateDevice(virDomainDef *vmdef, case VIR_DOMAIN_DEVICE_VSOCK: case VIR_DOMAIN_DEVICE_AUDIO: case VIR_DOMAIN_DEVICE_CRYPTO: + case VIR_DOMAIN_DEVICE_PSTORE: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent update of device '%1$s' is not supported"), diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args new file mode 100644 index 0000000000..d7c4708acb --- /dev/null +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args @@ -0,0 +1,36 @@ +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"}' \ +-machine pc-q35-9.0,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=on \ +-accel kvm \ +-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=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \ +-device '{"driver":"pcie-pci-bridge","id":"pci.2","bus":"pci.1","addr":"0x0"}' \ +-device '{"driver":"pcie-root-port","port":9,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x1"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-global ICH9-LPC.noreboot=off \ +-watchdog-action reset \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml new file mode 120000 index 0000000000..11ade68605 --- /dev/null +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml @@ -0,0 +1 @@ +pstore-acpi-erst.xml \ No newline at end of file diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.xml b/tests/qemuxmlconfdata/pstore-acpi-erst.xml new file mode 100644 index 0000000000..9b9ba266b2 --- /dev/null +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.xml @@ -0,0 +1,53 @@ +<domain type='kvm'> + <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> + <type arch='x86_64' machine='pc-q35-9.0'>hvm</type> + <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'/> + <controller type='pci' index='1' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='1' port='0x8'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/> + </controller> + <controller type='pci' index='2' model='pcie-to-pci-bridge'> + <model name='pcie-pci-bridge'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> + </controller> + <controller type='pci' index='3' model='pcie-root-port'> + <model name='pcie-root-port'/> + <target chassis='3' port='0x9'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <watchdog model='itco' action='reset'/> + <memballoon model='none'/> + <pstore backend='acpi-erst'> + <path>/tmp/guest_acpi_esrt</path> + <size unit='KiB'>8</size> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </pstore> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index cc984440ea..68d1127cf4 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2949,6 +2949,7 @@ mymain(void) DO_TEST_CAPS_LATEST("mtp-usb-device") DO_TEST_CAPS_LATEST("net-usb") DO_TEST_CAPS_LATEST("sound-device-virtio") + DO_TEST_CAPS_LATEST("pstore-acpi-erst")
DO_TEST_CAPS_LATEST_FAILURE("disk-network-iscsi-zero-hosts-invalid") DO_TEST_CAPS_LATEST_PARSE_ERROR("hostdev-scsi-vhost-rawio-invalid") -- 2.44.2
Reviewed-by: Kristina Hanicova <khanicov@redhat.com> Kristina

On Mon, Jul 22, 2024 at 02:23:30PM GMT, Michal Privoznik wrote:
+Pstore +~~~~~~~~~ + +Pstore is an oops/panic logger that writes its logs to a block device and +non-block device before the system crashes. Currently only ACPI Error Record +Serialization Table, ERST, is supported. This feature is designed for storing +error records in persistent storage for future reference and/or debugging. +:since:`Since v10.6.0` + +:: + + ... + <pstore backend='acpi-erst'> + <path>/tmp/guest_acpi_esrt</path> + <size unit='KiB'>8</size> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </pstore> + ... + +The ``pstore`` element has one mandatory attribute ``backend`` which selects +desired backend (only ``acpi-erst`` is accepted for now). Then it has the +following child elements: + +``path`` + Represents a path in the host that backs the pstore device in the guest. It + is mandatory.
I know, very late feedback, but couldn't we fill this in for the user if no value has been provided, e.g. /var/lib/libvirt/qemu/nvram/mydomain_PSTORE.raw or something like that? I suppose using a well-known directory would help avoid file permission issues and SELinux denials. Also IIUC we're leaving creation of the file to QEMU instead of handling that part ourselves, which is what we do in other cases such as UEFI VARS storage. Are we okay with that? -- Andrea Bolognani / Red Hat / Virtualization

Nothing special going on here. Resolves: https://issues.redhat.com/browse/RHEL-24746 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_alias.c | 10 ++++ src/qemu/qemu_command.c | 51 +++++++++++++++++++ .../pstore-acpi-erst.x86_64-latest.args | 2 + 3 files changed, 63 insertions(+) diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 872d940595..3e6bced4a8 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -658,6 +658,14 @@ qemuAssignDeviceCryptoAlias(virDomainDef *def, } +static void +qemuAssignDevicePstoreAlias(virDomainPstoreDef *pstore) +{ + if (!pstore->info.alias) + pstore->info.alias = g_strdup("pstore0"); +} + + int qemuAssignDeviceAliases(virDomainDef *def) { @@ -747,6 +755,8 @@ qemuAssignDeviceAliases(virDomainDef *def) for (i = 0; i < def->ncryptos; i++) { qemuAssignDeviceCryptoAlias(def, def->cryptos[i]); } + if (def->pstore) + qemuAssignDevicePstoreAlias(def->pstore); return 0; } diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index def124cc27..f15e6bda1e 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -10341,6 +10341,53 @@ qemuBuildCryptoCommandLine(virCommand *cmd, } +static int +qemuBuildPstoreCommandLine(virCommand *cmd, + const virDomainDef *def, + virDomainPstoreDef *pstore, + virQEMUCaps *qemuCaps) +{ + g_autoptr(virJSONValue) devProps = NULL; + g_autoptr(virJSONValue) memProps = NULL; + g_autofree char *memAlias = NULL; + + if (!pstore->info.alias) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("pstore device is missing alias")); + return -1; + } + + memAlias = g_strdup_printf("mem%s", pstore->info.alias); + + if (qemuMonitorCreateObjectProps(&memProps, + "memory-backend-file", + memAlias, + "s:mem-path", pstore->path, + "U:size", pstore->size * 1024, + "b:share", true, + NULL) < 0) { + return -1; + } + + if (virJSONValueObjectAdd(&devProps, + "s:driver", "acpi-erst", + "s:id", pstore->info.alias, + "s:memdev", memAlias, + NULL) < 0) { + return -1; + } + + if (qemuBuildDeviceAddressProps(devProps, def, &pstore->info) < 0) + return -1; + + if (qemuBuildObjectCommandlineFromJSON(cmd, memProps, qemuCaps) < 0 || + qemuBuildDeviceCommandlineFromJSON(cmd, devProps, def, qemuCaps) < 0) + return -1; + + return 0; +} + + static int qemuBuildAsyncTeardownCommandLine(virCommand *cmd, const virDomainDef *def, @@ -10699,6 +10746,10 @@ qemuBuildCommandLine(virDomainObj *vm, if (qemuBuildCryptoCommandLine(cmd, def, qemuCaps) < 0) return NULL; + if (def->pstore && + qemuBuildPstoreCommandLine(cmd, def, def->pstore, qemuCaps) < 0) + return NULL; + if (qemuBuildAsyncTeardownCommandLine(cmd, def, qemuCaps) < 0) return NULL; diff --git a/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args index d7c4708acb..2e217f6c83 100644 --- a/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args +++ b/tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args @@ -33,4 +33,6 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \ -global ICH9-LPC.noreboot=off \ -watchdog-action reset \ -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-object '{"qom-type":"memory-backend-file","id":"mempstore0","mem-path":"/tmp/guest_acpi_esrt","size":8192,"share":true}' \ +-device '{"driver":"acpi-erst","id":"pstore0","memdev":"mempstore0","bus":"pci.2","addr":"0x1"}' \ -msg timestamp=on -- 2.44.2

The acpi-erst backend for pstore device exposes a path in the host accessible to the guest and as such we must set seclabels on it to grant QEMU RW access. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/security/security_dac.c | 10 ++++++++++ src/security/security_selinux.c | 9 +++++++++ src/security/virt-aa-helper.c | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 1a3b51a298..59fc5b840f 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -1995,6 +1995,10 @@ virSecurityDACRestoreAllLabel(virSecurityManager *mgr, virSecurityDACRestoreFileLabel(mgr, def->os.slic_table) < 0) rc = -1; + if (def->pstore && + virSecurityDACRestoreFileLabel(mgr, def->pstore->path) < 0) + rc = -1; + return rc; } @@ -2240,6 +2244,12 @@ virSecurityDACSetAllLabel(virSecurityManager *mgr, user, group, true) < 0) return -1; + if (def->pstore && + virSecurityDACSetOwnership(mgr, NULL, + def->pstore->path, + user, group, true) < 0) + return -1; + return 0; } diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index e29f627bc2..ba0ce8fb9d 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -2911,6 +2911,10 @@ virSecuritySELinuxRestoreAllLabel(virSecurityManager *mgr, virSecuritySELinuxRestoreFileLabel(mgr, def->os.slic_table, true) < 0) rc = -1; + if (def->pstore && + virSecuritySELinuxRestoreFileLabel(mgr, def->pstore->path, true) < 0) + rc = -1; + return rc; } @@ -3335,6 +3339,11 @@ virSecuritySELinuxSetAllLabel(virSecurityManager *mgr, data->content_context, true) < 0) return -1; + if (def->pstore && + virSecuritySELinuxSetFilecon(mgr, def->pstore->path, + data->content_context, true) < 0) + return -1; + return 0; } diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 4d2b8ac4ab..067a17f331 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -1002,6 +1002,10 @@ get_files(vahControl * ctl) if (vah_add_file(&buf, ctl->def->os.slic_table, "r") != 0) goto cleanup; + if (ctl->def->pstore) + if (vah_add_file(&buf, ctl->def->pstore->path, "rw") != 0) + goto cleanup; + if (ctl->def->os.loader && ctl->def->os.loader->path) { bool readonly = false; virTristateBoolToBool(ctl->def->os.loader->readonly, &readonly); -- 2.44.2

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 2fdb52c607..d31fa15867 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -19,6 +19,13 @@ v10.6.0 (unreleased) * qemu: Add support for the 'pauth' Arm CPU feature + * Introduce pstore device + + The aim of pstore device is to provide a bit of NVRAM storage for guest + kernel to record oops/panic logs just before the it crashes. Typical usage + includes usage in combination with a watchdog so that the logs can be + inspected after the watchdog rebooted the machine. + * **Improvements** * **Bug fixes** -- 2.44.2

On Mon, Jul 22, 2024 at 2:27 PM Michal Privoznik <mprivozn@redhat.com> wrote:
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/NEWS.rst b/NEWS.rst index 2fdb52c607..d31fa15867 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -19,6 +19,13 @@ v10.6.0 (unreleased)
* qemu: Add support for the 'pauth' Arm CPU feature
+ * Introduce pstore device + + The aim of pstore device is to provide a bit of NVRAM storage for guest + kernel to record oops/panic logs just before the it crashes. Typical usage
*before it crashes + includes usage in combination with a watchdog so that the logs can be
+ inspected after the watchdog rebooted the machine. + * **Improvements**
* **Bug fixes** -- 2.44.2
Reviewed-by: Kristina Hanicova <khanicov@redhat.com> Kristina

On Mon, Jul 22, 2024 at 2:24 PM Michal Privoznik <mprivozn@redhat.com> wrote:
This is a rebased version of:
https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/UAUKL...
which was a rebased version of:
https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/VWR67...
Let's hope no more resends will be needed.
Michal Prívozník (5): qemu_capabilities: Introduce QEMU_CAPS_DEVICE_ACPI_ERST conf: Introduce pstore device qemu: Build cmd line for pstore device security: Set seclabels for pstore device NEWS: Document pstore device addition
NEWS.rst | 7 + docs/formatdomain.rst | 32 ++++ src/ch/ch_domain.c | 1 + src/conf/domain_conf.c | 153 ++++++++++++++++++ src/conf/domain_conf.h | 19 +++ src/conf/domain_postparse.c | 1 + src/conf/domain_validate.c | 30 ++++ src/conf/schemas/domaincommon.rng | 25 +++ src/conf/virconftypes.h | 2 + src/hyperv/hyperv_driver.c | 1 + src/libvirt_private.syms | 2 + src/libxl/libxl_driver.c | 6 + src/lxc/lxc_driver.c | 6 + src/qemu/qemu_alias.c | 10 ++ src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 52 ++++++ src/qemu/qemu_domain.c | 3 + src/qemu/qemu_domain_address.c | 11 ++ src/qemu/qemu_driver.c | 3 + src/qemu/qemu_hotplug.c | 5 + src/qemu/qemu_validate.c | 26 +++ src/security/security_dac.c | 10 ++ src/security/security_selinux.c | 9 ++ src/security/virt-aa-helper.c | 4 + src/test/test_driver.c | 1 + .../caps_7.0.0_aarch64+hvf.xml | 1 + .../caps_7.0.0_aarch64.xml | 1 + .../qemucapabilitiesdata/caps_7.0.0_ppc64.xml | 1 + .../caps_7.0.0_x86_64.xml | 1 + .../qemucapabilitiesdata/caps_7.1.0_ppc64.xml | 1 + .../caps_7.1.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml | 1 + .../caps_7.2.0_x86_64+hvf.xml | 1 + .../caps_7.2.0_x86_64.xml | 1 + .../caps_8.0.0_x86_64.xml | 1 + .../caps_8.1.0_x86_64.xml | 1 + .../caps_8.2.0_aarch64.xml | 1 + .../caps_8.2.0_armv7l.xml | 1 + .../caps_8.2.0_loongarch64.xml | 1 + .../caps_8.2.0_x86_64.xml | 1 + .../caps_9.0.0_x86_64.xml | 1 + .../caps_9.1.0_x86_64.xml | 1 + .../pstore-acpi-erst.x86_64-latest.args | 38 +++++ .../pstore-acpi-erst.x86_64-latest.xml | 1 + tests/qemuxmlconfdata/pstore-acpi-erst.xml | 53 ++++++ tests/qemuxmlconftest.c | 1 + 47 files changed, 532 insertions(+) create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.args create mode 120000 tests/qemuxmlconfdata/pstore-acpi-erst.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/pstore-acpi-erst.xml
-- 2.44.2
Reviewed-by: Kristina Hanicova <khanicov@redhat.com> Kristina
participants (3)
-
Andrea Bolognani
-
Kristina Hanicova
-
Michal Privoznik