[PATCH 2/5] conf: Introduce pstore device

Michal Privoznik posted 5 patches 1 month, 3 weeks ago
[PATCH 2/5] conf: Introduce pstore device
Posted by Michal Privoznik 1 month, 3 weeks ago
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
Re: [PATCH 2/5] conf: Introduce pstore device
Posted by Andrea Bolognani 1 month, 3 weeks ago
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
Re: [PATCH 2/5] conf: Introduce pstore device
Posted by Kristina Hanicova 1 month, 3 weeks ago
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