* XML serialization and deserialization of PCI VPD;
* PCI VPD capability flags added and used in relevant places;
* XML to XML tests for the added capability.

Signed-off-by: Dmitrii Shcherbakov <dmitrii.shcherba...@canonical.com>
---
 docs/schemas/nodedev.rng                      |  96 ++++++
 include/libvirt/libvirt-nodedev.h             |   1 +
 src/conf/node_device_conf.c                   | 309 ++++++++++++++++++
 src/conf/node_device_conf.h                   |   7 +-
 src/conf/virnodedeviceobj.c                   |   7 +-
 src/node_device/node_device_driver.c          |   2 +
 src/node_device/node_device_udev.c            |   2 +
 .../pci_0000_42_00_0_vpd.xml                  |  42 +++
 .../pci_0000_42_00_0_vpd.xml                  |   1 +
 tests/nodedevxml2xmltest.c                    |   1 +
 tools/virsh-nodedev.c                         |   3 +
 11 files changed, 469 insertions(+), 2 deletions(-)
 create mode 100644 tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml
 create mode 120000 tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml

diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng
index e089e66858..e4733f0804 100644
--- a/docs/schemas/nodedev.rng
+++ b/docs/schemas/nodedev.rng
@@ -223,6 +223,10 @@
       <ref name="mdev_types"/>
     </optional>
 
+    <optional>
+      <ref name="vpd"/>
+    </optional>
+
     <optional>
       <element name="iommuGroup">
         <attribute name="number">
@@ -770,6 +774,80 @@
     </element>
   </define>
 
+  <define name="vpd">
+    <element name="capability">
+      <attribute name="type">
+        <value>vpd</value>
+      </attribute>
+      <element name="name">
+        <ref name="vpdFieldValueFormat"/>
+      </element>
+      <optional>
+        <element name="fields">
+          <attribute name="access">
+            <value>readonly</value>
+          </attribute>
+          <optional>
+            <element name="change_level">
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="manufacture_id">
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="part_number">
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="serial_number">
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </optional>
+          <zeroOrMore>
+            <element name="vendor_field">
+              <attribute name="index">
+                <ref name="vendorVPDFieldIndex"/>
+              </attribute>
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </zeroOrMore>
+        </element>
+      </optional>
+      <optional>
+        <element name="fields">
+          <attribute name="access">
+            <value>readwrite</value>
+          </attribute>
+          <optional>
+            <element name="asset_tag">
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </optional>
+          <zeroOrMore>
+            <element name="vendor_field">
+              <attribute name="index">
+                <ref name="vendorVPDFieldIndex"/>
+              </attribute>
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </zeroOrMore>
+          <zeroOrMore>
+            <element name="system_field">
+              <attribute name="index">
+                <ref name="systemVPDFieldIndex"/>
+              </attribute>
+              <ref name="vpdFieldValueFormat"/>
+            </element>
+          </zeroOrMore>
+        </element>
+      </optional>
+    </element>
+  </define>
+
   <define name="apDomainRange">
     <choice>
       <data type="string">
@@ -782,4 +860,22 @@
     </choice>
   </define>
 
+  <define name="vpdFieldValueFormat">
+    <data type="string">
+      <param name="pattern">[0-9a-zA-F -_,.:;=]{0,255}</param>
+    </data>
+  </define>
+
+  <define name="vendorVPDFieldIndex">
+    <data type="string">
+      <param name="pattern">[0-9A-Z]{1}</param>
+    </data>
+  </define>
+
+  <define name="systemVPDFieldIndex">
+    <data type="string">
+      <param name="pattern">[0-9B-Z]{1}</param>
+    </data>
+  </define>
+
 </grammar>
diff --git a/include/libvirt/libvirt-nodedev.h 
b/include/libvirt/libvirt-nodedev.h
index e492634217..245365b07f 100644
--- a/include/libvirt/libvirt-nodedev.h
+++ b/include/libvirt/libvirt-nodedev.h
@@ -84,6 +84,7 @@ typedef enum {
     VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_CARD       = 1 << 18, /* s390 AP Card 
device */
     VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE      = 1 << 19, /* s390 AP 
Queue */
     VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX     = 1 << 20, /* s390 AP 
Matrix */
+    VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD           = 1 << 21, /* Device with 
VPD */
 
     /* filter the devices by active state */
     VIR_CONNECT_LIST_NODE_DEVICES_INACTIVE          = 1 << 30, /* Inactive 
devices */
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index 1f39e2cbfd..9a24041022 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -36,6 +36,7 @@
 #include "virrandom.h"
 #include "virlog.h"
 #include "virfcp.h"
+#include "virpcivpd.h"
 
 #define VIR_FROM_THIS VIR_FROM_NODEDEV
 
@@ -70,6 +71,7 @@ VIR_ENUM_IMPL(virNodeDevCap,
               "ap_card",
               "ap_queue",
               "ap_matrix",
+              "vpd",
 );
 
 VIR_ENUM_IMPL(virNodeDevNetCap,
@@ -240,6 +242,80 @@ virNodeDeviceCapMdevTypesFormat(virBuffer *buf,
     }
 }
 
+static void
+virNodeDeviceCapVPDFormatCustomVendorField(virPCIVPDResourceCustom *field, 
virBuffer *buf)
+{
+    if (field == NULL || field->value == NULL) {
+        return;
+    }
+    virBufferAsprintf(buf, "<vendor_field index='%c'>%s</vendor_field>\n", 
field->idx,
+                      field->value);
+}
+
+static void
+virNodeDeviceCapVPDFormatCustomSystemField(virPCIVPDResourceCustom *field, 
virBuffer *buf)
+{
+    if (field == NULL || field->value == NULL) {
+        return;
+    }
+    virBufferAsprintf(buf, "<system_field index='%c'>%s</system_field>\n", 
field->idx,
+                      field->value);
+}
+
+static inline void
+virNodeDeviceCapVPDFormatRegularField(virBuffer *buf, const char *keyword, 
const char *value)
+{
+    if (keyword == NULL || value == NULL) {
+        return;
+    }
+    virBufferAsprintf(buf, "<%s>%s</%s>\n", keyword, value, keyword);
+}
+
+static void
+virNodeDeviceCapVPDFormat(virBuffer *buf, virPCIVPDResource *res)
+{
+    if (res == NULL) {
+        return;
+    }
+
+    virBufferAddLit(buf, "<capability type='vpd'>\n");
+    virBufferAdjustIndent(buf, 2);
+    if (res->name != NULL) {
+        virBufferEscapeString(buf, "<name>%s</name>\n", res->name);
+    }
+
+    if (res->ro != NULL) {
+        virBufferEscapeString(buf, "<fields access='%s'>\n", "readonly");
+
+        virBufferAdjustIndent(buf, 2);
+        virNodeDeviceCapVPDFormatRegularField(buf, "change_level", 
res->ro->change_level);
+        virNodeDeviceCapVPDFormatRegularField(buf, "manufacture_id", 
res->ro->manufacture_id);
+        virNodeDeviceCapVPDFormatRegularField(buf, "part_number", 
res->ro->part_number);
+        virNodeDeviceCapVPDFormatRegularField(buf, "serial_number", 
res->ro->serial_number);
+        g_ptr_array_foreach(res->ro->vendor_specific,
+                            (GFunc)virNodeDeviceCapVPDFormatCustomVendorField, 
buf);
+        virBufferAdjustIndent(buf, -2);
+
+        virBufferAddLit(buf, "</fields>\n");
+    }
+
+    if (res->rw != NULL) {
+        virBufferEscapeString(buf, "<fields access='%s'>\n", "readwrite");
+
+        virBufferAdjustIndent(buf, 2);
+        virNodeDeviceCapVPDFormatRegularField(buf, "asset_tag", 
res->rw->asset_tag);
+        g_ptr_array_foreach(res->rw->vendor_specific,
+                            (GFunc)virNodeDeviceCapVPDFormatCustomVendorField, 
buf);
+        g_ptr_array_foreach(res->rw->system_specific,
+                            (GFunc)virNodeDeviceCapVPDFormatCustomSystemField, 
buf);
+        virBufferAdjustIndent(buf, -2);
+
+        virBufferAddLit(buf, "</fields>\n");
+    }
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</capability>\n");
+}
 
 static void
 virNodeDeviceCapPCIDefFormat(virBuffer *buf,
@@ -315,6 +391,9 @@ virNodeDeviceCapPCIDefFormat(virBuffer *buf,
                                         data->pci_dev.mdev_types,
                                         data->pci_dev.nmdev_types);
     }
+    if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) {
+        virNodeDeviceCapVPDFormat(buf, data->pci_dev.vpd);
+    }
     if (data->pci_dev.nIommuGroupDevices) {
         virBufferAsprintf(buf, "<iommuGroup number='%d'>\n",
                           data->pci_dev.iommuGroupNumber);
@@ -673,6 +752,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def)
         case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_FC_HOST:
         case VIR_NODE_DEV_CAP_VPORTS:
+        case VIR_NODE_DEV_CAP_VPD:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -859,6 +939,181 @@ virNodeDevCapMdevTypesParseXML(xmlXPathContextPtr ctxt,
     return ret;
 }
 
+static int
+virNodeDeviceCapVPDParseCustomFields(xmlXPathContextPtr ctxt, 
virPCIVPDResource *res, bool readOnly)
+{
+    int nfields = -1;
+    g_autofree char *index = NULL, *value = NULL, *keyword = NULL;
+    g_autofree xmlNodePtr *nodes = NULL;
+    xmlNodePtr orignode = NULL;
+    size_t i = 0;
+
+    orignode = ctxt->node;
+    if ((nfields = virXPathNodeSet("./vendor_field[@index]", ctxt, &nodes)) < 
0) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                _("failed to evaluate <vendor_field> elements"));
+        ctxt->node = orignode;
+        return -1;
+    }
+    for (i = 0; i < nfields; i++) {
+        ctxt->node = nodes[i];
+        if (!(index = virXPathStringLimit("string(./@index[1])", 2, ctxt))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                    _("<vendor_field> evaluation has failed"));
+            continue;
+        }
+        if (!(value = virXPathString("string(./text())", ctxt))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                    _("<vendor_field> value evaluation has failed"));
+            continue;
+        }
+        keyword = g_strdup_printf("V%c", index[0]);
+        virPCIVPDResourceUpdateKeyword(res, readOnly, keyword, value);
+        g_free(g_steal_pointer(&index));
+        g_free(g_steal_pointer(&keyword));
+        g_free(g_steal_pointer(&value));
+    }
+    g_free(g_steal_pointer(&nodes));
+    ctxt->node = orignode;
+
+    if (!readOnly) {
+        if ((nfields = virXPathNodeSet("./system_field[@index]", ctxt, 
&nodes)) < 0) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                    _("failed to evaluate <system_field> elements"));
+            ctxt->node = orignode;
+            return -1;
+        }
+        for (i = 0; i < nfields; i++) {
+            ctxt->node = nodes[i];
+            if (!(index = virXPathStringLimit("string(./@index[1])", 2, 
ctxt))) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                        _("<system_field> evaluation has failed"));
+                continue;
+            }
+            if (!(value = virXPathString("string(./text())", ctxt))) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                        _("<system_field> value evaluation has failed"));
+                continue;
+            }
+            keyword = g_strdup_printf("Y%c", index[0]);
+            virPCIVPDResourceUpdateKeyword(res, readOnly, keyword, value);
+            g_free(g_steal_pointer(&index));
+            g_free(g_steal_pointer(&keyword));
+            g_free(g_steal_pointer(&value));
+        }
+        ctxt->node = orignode;
+    }
+
+    return 0;
+}
+
+static int
+virNodeDeviceCapVPDParseReadOnlyFields(xmlXPathContextPtr ctxt, 
virPCIVPDResource *res)
+{
+    const char *keywords[] = {"change_level", "manufacture_id",
+                              "serial_number", "part_number", NULL};
+    g_autofree char *expression = NULL;
+    g_autofree char *result = NULL;
+    size_t i = 0;
+
+    if (res == NULL) {
+        return -1;
+    }
+    res->ro = virPCIVPDResourceRONew();
+
+    while (keywords[i]) {
+        expression = g_strdup_printf("string(./%s)", keywords[i]);
+        result = virXPathString(expression, ctxt);
+        virPCIVPDResourceUpdateKeyword(res, true, keywords[i], result);
+        g_free(g_steal_pointer(&expression));
+        g_free(g_steal_pointer(&result));
+        ++i;
+    }
+    if (virNodeDeviceCapVPDParseCustomFields(ctxt, res, true) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int
+virNodeDeviceCapVPDParseReadWriteFields(xmlXPathContextPtr ctxt, 
virPCIVPDResource *res)
+{
+    g_autofree char *assetTag = virXPathString("string(./asset_tag)", ctxt);
+    res->rw = virPCIVPDResourceRWNew();
+    virPCIVPDResourceUpdateKeyword(res, false, "asset_tag", assetTag);
+    if (virNodeDeviceCapVPDParseCustomFields(ctxt, res, false) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int
+virNodeDeviceCapVPDParseXML(xmlXPathContextPtr ctxt, virPCIVPDResource **res)
+{
+    xmlNodePtr orignode = NULL;
+    g_autofree xmlNodePtr *nodes = NULL;
+    int nfields = -1;
+    g_autofree char *access = NULL;
+    size_t i = 0;
+    g_autoptr(virPCIVPDResource) newres = g_new0(virPCIVPDResource, 1);
+
+    if (res == NULL) {
+        return -1;
+    }
+
+    orignode = ctxt->node;
+
+    if (!(newres->name = virXPathString("string(./name)", ctxt))) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                _("Could not read a device name from the <name> element"));
+        ctxt->node = orignode;
+        return -1;
+    }
+
+    if ((nfields = virXPathNodeSet("./fields[@access]", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                _("no VPD <fields> elements with an access type attribute 
found"));
+        ctxt->node = orignode;
+        return -1;
+    }
+
+    for (i = 0; i < nfields; i++) {
+        ctxt->node = nodes[i];
+        if (!(access = virXPathString("string(./@access[1])", ctxt))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                    _("VPD fields access type parsing has failed"));
+            ctxt->node = orignode;
+            return -1;
+        }
+
+        if (STREQ(access, "readonly")) {
+            if (virNodeDeviceCapVPDParseReadOnlyFields(ctxt, newres) < 0) {
+                virReportError(VIR_ERR_XML_ERROR,
+                        _("Could not parse %s VPD resource fields"), access);
+                return -1;
+            }
+        } else if (STREQ(access, "readwrite")) {
+            if (virNodeDeviceCapVPDParseReadWriteFields(ctxt, newres) < 0) {
+                virReportError(VIR_ERR_XML_ERROR,
+                        _("Could not parse %s VPD resource fields"), access);
+                return -1;
+            }
+        } else {
+            virReportError(VIR_ERR_XML_ERROR, _("Unsupported VPD field access 
type specified %s"),
+                           access);
+            return -1;
+        }
+        g_free(g_steal_pointer(&access));
+    }
+    ctxt->node = orignode;
+
+    /* Replace the existing VPD representation if there is one already. */
+    if (*res != NULL) {
+        virPCIVPDResourceFree(*res);
+    }
+    *res = g_steal_pointer(&newres);
+    return 0;
+}
 
 static int
 virNodeDevAPMatrixCapabilityParseXML(xmlXPathContextPtr ctxt,
@@ -1718,6 +1973,11 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt,
                                            &pci_dev->nmdev_types) < 0)
             return -1;
         pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV;
+    } else if (STREQ(type, "vpd")) {
+        if (virNodeDeviceCapVPDParseXML(ctxt, &pci_dev->vpd) < 0) {
+            return -1;
+        }
+        pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD;
     } else {
         int hdrType = virPCIHeaderTypeFromString(type);
 
@@ -2024,6 +2284,7 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt,
     case VIR_NODE_DEV_CAP_VPORTS:
     case VIR_NODE_DEV_CAP_SCSI_GENERIC:
     case VIR_NODE_DEV_CAP_VDPA:
+    case VIR_NODE_DEV_CAP_VPD:
     case VIR_NODE_DEV_CAP_LAST:
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("unknown capability type '%d' for '%s'"),
@@ -2287,6 +2548,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps)
         for (i = 0; i < data->pci_dev.nmdev_types; i++)
             virMediatedDeviceTypeFree(data->pci_dev.mdev_types[i]);
         g_free(data->pci_dev.mdev_types);
+        virPCIVPDResourceFree(g_steal_pointer(&data->pci_dev.vpd));
         break;
     case VIR_NODE_DEV_CAP_USB_DEV:
         g_free(data->usb_dev.product_name);
@@ -2352,6 +2614,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps)
     case VIR_NODE_DEV_CAP_VDPA:
     case VIR_NODE_DEV_CAP_AP_CARD:
     case VIR_NODE_DEV_CAP_AP_QUEUE:
+    case VIR_NODE_DEV_CAP_VPD:
     case VIR_NODE_DEV_CAP_LAST:
         /* This case is here to shutup the compiler */
         break;
@@ -2418,6 +2681,7 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def)
         case VIR_NODE_DEV_CAP_VDPA:
         case VIR_NODE_DEV_CAP_AP_CARD:
         case VIR_NODE_DEV_CAP_AP_QUEUE:
+        case VIR_NODE_DEV_CAP_VPD:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -2489,6 +2753,10 @@ virNodeDeviceCapsListExport(virNodeDeviceDef *def,
                 MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_MDEV_TYPES);
                 ncaps++;
             }
+            if (flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) {
+                MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_VPD);
+                ncaps++;
+            }
         }
 
         if (caps->data.type == VIR_NODE_DEV_CAP_CSS_DEV) {
@@ -2749,6 +3017,44 @@ virNodeDeviceGetMdevTypesCaps(const char *sysfspath,
 }
 
 
+/**
+ * virNodeDeviceGetPCIVPDDynamicCap:
+ * @devCapPCIDev: a virNodeDevCapPCIDev for which to add VPD resources.
+ *
+ * While VPD has a read-only portion, there may be a read-write portion per
+ * the specs which may change dynamically.
+ *
+ * Returns: 0 if the operation was successful (even if VPD is not present for
+ * that device since it is optional in the specs, -1 otherwise.
+ */
+static int
+virNodeDeviceGetPCIVPDDynamicCap(virNodeDevCapPCIDev *devCapPCIDev)
+{
+    g_autoptr(virPCIDevice) pciDev = NULL;
+    virPCIDeviceAddress devAddr;
+    g_autoptr(virPCIVPDResource) res = NULL;
+
+    devAddr.domain = devCapPCIDev->domain;
+    devAddr.bus = devCapPCIDev->bus;
+    devAddr.slot = devCapPCIDev->slot;
+    devAddr.function = devCapPCIDev->function;
+
+    if (!(pciDev = virPCIDeviceNew(&devAddr)))
+        return -1;
+
+    if (virPCIDeviceHasVPD(pciDev)) {
+        /* VPD is optional in PCI(e) specs. If it is there, attempt to add it. 
*/
+        if ((res = virPCIDeviceGetVPD(pciDev))) {
+            devCapPCIDev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD;
+            devCapPCIDev->vpd = g_steal_pointer(&res);
+        } else {
+            virPCIVPDResourceFree(g_steal_pointer(&devCapPCIDev->vpd));
+        }
+    }
+    return 0;
+}
+
+
 /* virNodeDeviceGetPCIDynamicCaps() get info that is stored in sysfs
  * about devices related to this device, i.e. things that can change
  * without this device itself changing. These must be refreshed
@@ -2771,6 +3077,9 @@ virNodeDeviceGetPCIDynamicCaps(const char *sysfsPath,
     if (pci_dev->nmdev_types > 0)
         pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV;
 
+    if (virNodeDeviceGetPCIVPDDynamicCap(pci_dev) < 0)
+        return -1;
+
     return 0;
 }
 
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index 5a4d9c7a55..e4d1f67d53 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -24,6 +24,7 @@
 
 #include "internal.h"
 #include "virbitmap.h"
+#include "virpcivpd.h"
 #include "virscsihost.h"
 #include "virpci.h"
 #include "virvhba.h"
@@ -69,6 +70,7 @@ typedef enum {
     VIR_NODE_DEV_CAP_AP_CARD,           /* s390 AP Card device */
     VIR_NODE_DEV_CAP_AP_QUEUE,          /* s390 AP Queue */
     VIR_NODE_DEV_CAP_AP_MATRIX,         /* s390 AP Matrix device */
+    VIR_NODE_DEV_CAP_VPD,               /* Device provides VPD */
 
     VIR_NODE_DEV_CAP_LAST
 } virNodeDevCapType;
@@ -103,6 +105,7 @@ typedef enum {
     VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION      = (1 << 1),
     VIR_NODE_DEV_CAP_FLAG_PCIE                      = (1 << 2),
     VIR_NODE_DEV_CAP_FLAG_PCI_MDEV                  = (1 << 3),
+    VIR_NODE_DEV_CAP_FLAG_PCI_VPD                   = (1 << 4),
 } virNodeDevPCICapFlags;
 
 typedef enum {
@@ -181,6 +184,7 @@ struct _virNodeDevCapPCIDev {
     int hdrType; /* enum virPCIHeaderType or -1 */
     virMediatedDeviceType **mdev_types;
     size_t nmdev_types;
+    virPCIVPDResource *vpd;
 };
 
 typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev;
@@ -418,7 +422,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNodeDevCapsDef, 
virNodeDevCapsDefFree);
                  VIR_CONNECT_LIST_NODE_DEVICES_CAP_VDPA          | \
                  VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_CARD       | \
                  VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE      | \
-                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX)
+                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX     | \
+                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD)
 
 #define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE \
     VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE | \
diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c
index 9a9841576a..165ec1f1dd 100644
--- a/src/conf/virnodedeviceobj.c
+++ b/src/conf/virnodedeviceobj.c
@@ -701,6 +701,9 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
             if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
                 (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV))
                 return true;
+            if (type == VIR_NODE_DEV_CAP_VPD &&
+                (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD))
+                return true;
             break;
 
         case VIR_NODE_DEV_CAP_SCSI_HOST:
@@ -742,6 +745,7 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
         case VIR_NODE_DEV_CAP_VDPA:
         case VIR_NODE_DEV_CAP_AP_CARD:
         case VIR_NODE_DEV_CAP_AP_QUEUE:
+        case VIR_NODE_DEV_CAP_VPD:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -899,7 +903,8 @@ virNodeDeviceObjMatch(virNodeDeviceObj *obj,
               MATCH_CAP(VDPA)          ||
               MATCH_CAP(AP_CARD)       ||
               MATCH_CAP(AP_QUEUE)      ||
-              MATCH_CAP(AP_MATRIX)))
+              MATCH_CAP(AP_MATRIX)     ||
+              MATCH_CAP(VPD)))
             return false;
     }
 
diff --git a/src/node_device/node_device_driver.c 
b/src/node_device/node_device_driver.c
index 3bc6eb1c11..d19ed7d948 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -708,6 +708,7 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj)
         case VIR_NODE_DEV_CAP_VDPA:
         case VIR_NODE_DEV_CAP_AP_CARD:
         case VIR_NODE_DEV_CAP_AP_QUEUE:
+        case VIR_NODE_DEV_CAP_VPD:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -1983,6 +1984,7 @@ int nodeDeviceDefValidate(virNodeDeviceDef *def,
             case VIR_NODE_DEV_CAP_AP_CARD:
             case VIR_NODE_DEV_CAP_AP_QUEUE:
             case VIR_NODE_DEV_CAP_AP_MATRIX:
+            case VIR_NODE_DEV_CAP_VPD:
             case VIR_NODE_DEV_CAP_LAST:
                 break;
         }
diff --git a/src/node_device/node_device_udev.c 
b/src/node_device/node_device_udev.c
index 71f0bef827..7c3bb762b3 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -42,6 +42,7 @@
 #include "virnetdev.h"
 #include "virmdev.h"
 #include "virutil.h"
+#include "virpcivpd.h"
 
 #include "configmake.h"
 
@@ -1397,6 +1398,7 @@ udevGetDeviceDetails(struct udev_device *device,
     case VIR_NODE_DEV_CAP_AP_MATRIX:
         return udevProcessAPMatrix(device, def);
     case VIR_NODE_DEV_CAP_MDEV_TYPES:
+    case VIR_NODE_DEV_CAP_VPD:
     case VIR_NODE_DEV_CAP_SYSTEM:
     case VIR_NODE_DEV_CAP_FC_HOST:
     case VIR_NODE_DEV_CAP_VPORTS:
diff --git a/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml 
b/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml
new file mode 100644
index 0000000000..8b56e4f6b4
--- /dev/null
+++ b/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml
@@ -0,0 +1,42 @@
+<device>
+  <name>pci_0000_42_00_0</name>
+  <capability type='pci'>
+    <class>0x020000</class>
+    <domain>0</domain>
+    <bus>66</bus>
+    <slot>0</slot>
+    <function>0</function>
+    <product id='0xa2d6'>MT42822 BlueField-2 integrated ConnectX-6 Dx network 
controller</product>
+    <vendor id='0x15b3'>Mellanox Technologies</vendor>
+    <capability type='virt_functions' maxCount='16'/>
+    <capability type='vpd'>
+      <name>BlueField-2 DPU 25GbE Dual-Port SFP56, Crypto Enabled, 16GB 
on-board DDR, 1GbE OOB management, Tall Bracket</name>
+      <fields access='readonly'>
+        <change_level>B1</change_level>
+        <manufacture_id>foobar</manufacture_id>
+        <part_number>MBF2H332A-AEEOT</part_number>
+        <serial_number>MT2113X00000</serial_number>
+        <vendor_field index='0'>PCIeGen4 x8</vendor_field>
+        <vendor_field index='2'>MBF2H332A-AEEOT</vendor_field>
+        <vendor_field 
index='3'>3c53d07eec484d8aab34dabd24fe575aa</vendor_field>
+        <vendor_field 
index='A'>MLX:MN=MLNX:CSKU=V2:UUID=V3:PCI=V0:MODL=BF2H332A</vendor_field>
+      </fields>
+      <fields access='readwrite'>
+        <asset_tag>fooasset</asset_tag>
+        <vendor_field index='0'>vendorfield0</vendor_field>
+        <vendor_field index='2'>vendorfield2</vendor_field>
+        <vendor_field index='A'>vendorfieldA</vendor_field>
+        <system_field index='B'>systemfieldB</system_field>
+        <system_field index='0'>systemfield0</system_field>
+      </fields>
+    </capability>
+    <iommuGroup number='65'>
+      <address domain='0x0000' bus='0x42' slot='0x00' function='0x0'/>
+    </iommuGroup>
+    <numa node='0'/>
+    <pci-express>
+      <link validity='cap' port='0' speed='16' width='8'/>
+      <link validity='sta' speed='8' width='8'/>
+    </pci-express>
+  </capability>
+</device>
diff --git a/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml 
b/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml
new file mode 120000
index 0000000000..a0b5372ca0
--- /dev/null
+++ b/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml
@@ -0,0 +1 @@
+../nodedevschemadata/pci_0000_42_00_0_vpd.xml
\ No newline at end of file
diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c
index 9e32e7d553..557347fb07 100644
--- a/tests/nodedevxml2xmltest.c
+++ b/tests/nodedevxml2xmltest.c
@@ -121,6 +121,7 @@ mymain(void)
     DO_TEST("pci_0000_02_10_7_sriov_pf_vfs_all_header_type");
     DO_TEST("drm_renderD129");
     DO_TEST("pci_0000_02_10_7_mdev_types");
+    DO_TEST("pci_0000_42_00_0_vpd");
     DO_TEST("mdev_3627463d_b7f0_4fea_b468_f1da537d301b");
     DO_TEST("ccw_0_0_ffff");
     DO_TEST("css_0_0_ffff");
diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c
index c989a77ad2..1ad8db7a3f 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -472,6 +472,9 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd 
G_GNUC_UNUSED)
         case VIR_NODE_DEV_CAP_MDEV:
             flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV;
             break;
+        case VIR_NODE_DEV_CAP_VPD:
+            flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD;
+            break;
         case VIR_NODE_DEV_CAP_CCW_DEV:
             flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV;
             break;
-- 
2.32.0


Reply via email to