]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Add PCI VPD-related helper functions to virpci
authorDmitrii Shcherbakov <dmitrii.shcherbakov@canonical.com>
Wed, 20 Oct 2021 08:30:32 +0000 (11:30 +0300)
committerDaniel P. Berrangé <berrange@redhat.com>
Thu, 21 Oct 2021 16:34:04 +0000 (17:34 +0100)
Add helper functions to virpci to provide means of checking for a VPD
file presence and for VPD resource retrieval using the PCI VPD parser.

The added test assesses the basic functionality of VPD retrieval while
the full parser is tested by virpcivpdtest.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Dmitrii Shcherbakov <dmitrii.shcherbakov@canonical.com>
src/libvirt_private.syms
src/util/virpci.c
src/util/virpci.h
tests/virpcimock.c
tests/virpcitest.c

index 444b51c880a44208c4bd4da5e0a15c6b7cbb3d48..55ae7d5b6f0aac8bc873c98f9b6fd55977b81bb6 100644 (file)
@@ -2994,7 +2994,9 @@ virPCIDeviceGetReprobe;
 virPCIDeviceGetStubDriver;
 virPCIDeviceGetUnbindFromStub;
 virPCIDeviceGetUsedBy;
+virPCIDeviceGetVPD;
 virPCIDeviceHasPCIExpressLink;
+virPCIDeviceHasVPD;
 virPCIDeviceIsAssignable;
 virPCIDeviceIsPCIExpress;
 virPCIDeviceListAdd;
index f307580a53119c416909c57ded0ec5a5c36d0e9d..e746a2b25be5ed3a537c3ae370d40dfe06fcd719 100644 (file)
@@ -37,6 +37,7 @@
 #include "virkmod.h"
 #include "virstring.h"
 #include "viralloc.h"
+#include "virpcivpd.h"
 
 VIR_LOG_INIT("util.pci");
 
@@ -2640,6 +2641,61 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path,
     return 0;
 }
 
+
+bool
+virPCIDeviceHasVPD(virPCIDevice *dev)
+{
+    g_autofree char *vpdPath = NULL;
+
+    vpdPath = virPCIFile(dev->name, "vpd");
+    if (!virFileExists(vpdPath)) {
+        VIR_INFO("Device VPD file does not exist %s", vpdPath);
+        return false;
+    } else if (!virFileIsRegular(vpdPath)) {
+        VIR_WARN("VPD path does not point to a regular file %s", vpdPath);
+        return false;
+    }
+    return true;
+}
+
+/**
+ * virPCIDeviceGetVPD:
+ * @dev: a PCI device to get a PCI VPD for.
+ *
+ * Obtain a PCI device's Vital Product Data (VPD). VPD is optional in
+ * both PCI Local Bus and PCIe specifications so there is no guarantee it
+ * will be there for a particular device.
+ *
+ * Returns: a pointer to virPCIVPDResource which needs to be freed by the caller
+ * or NULL if getting it failed for some reason (e.g. invalid format, I/O error).
+ */
+virPCIVPDResource *
+virPCIDeviceGetVPD(virPCIDevice *dev)
+{
+    g_autofree char *vpdPath = NULL;
+    int fd;
+    g_autoptr(virPCIVPDResource) res = NULL;
+
+    vpdPath = virPCIFile(dev->name, "vpd");
+    if (!virPCIDeviceHasVPD(dev)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("Device %s does not have a VPD"),
+                virPCIDeviceGetName(dev));
+        return NULL;
+    }
+    if ((fd = open(vpdPath, O_RDONLY)) < 0) {
+        virReportSystemError(-fd, _("Failed to open a VPD file '%s'"), vpdPath);
+        return NULL;
+    }
+    res = virPCIVPDParse(fd);
+
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno, _("Unable to close the VPD file, fd: %d"), fd);
+        return NULL;
+    }
+
+    return g_steal_pointer(&res);
+}
+
 #else
 static const char *unsupported = N_("not supported on non-linux platforms");
 
@@ -2713,6 +2769,20 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path G_GNUC_UNUSED,
     virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
     return -1;
 }
+
+bool
+virPCIDeviceHasVPD(virPCIDevice *dev G_GNUC_UNUSED)
+{
+    virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
+    return NULL;
+}
+
+virPCIVPDResource *
+virPCIDeviceGetVPD(virPCIDevice *dev G_GNUC_UNUSED)
+{
+    virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
+    return NULL;
+}
 #endif /* __linux__ */
 
 int
index 9a3db6c6d8d5669a4f45facdbeee8675eda8f004..3346321ec9fb5882c51e6bbc3473312f177d6ebf 100644 (file)
@@ -24,6 +24,7 @@
 #include "virmdev.h"
 #include "virobject.h"
 #include "virenum.h"
+#include "virpcivpd.h"
 
 typedef struct _virPCIDevice virPCIDevice;
 typedef struct _virPCIDeviceAddress virPCIDeviceAddress;
@@ -269,6 +270,9 @@ int virPCIGetVirtualFunctionInfo(const char *vf_sysfs_device_path,
                                  char **pfname,
                                  int *vf_index);
 
+bool virPCIDeviceHasVPD(virPCIDevice *dev);
+virPCIVPDResource * virPCIDeviceGetVPD(virPCIDevice *dev);
+
 int virPCIDeviceUnbind(virPCIDevice *dev);
 int virPCIDeviceRebind(virPCIDevice *dev);
 int virPCIDeviceGetDriverPathAndName(virPCIDevice *dev,
index d4d43aac51ce40fd8c1493bef0202b72e1e2c11b..0f8d5ad54f266ecc40557855feea4c6c26ed3846 100644 (file)
 
 #include <config.h>
 
+#define LIBVIRT_VIRPCIVPDPRIV_H_ALLOW
+
+#include "virpcivpdpriv.h"
+
 #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
 # define VIR_MOCK_LOOKUP_MAIN
 # include "virmock.h"
@@ -123,6 +127,13 @@ struct pciDeviceAddress {
 };
 # define ADDR_STR_FMT "%04x:%02x:%02x.%u"
 
+struct pciVPD {
+    /* PCI VPD contents (binary, may contain NULLs), NULL if not present. */
+    const char *data;
+    /* VPD length in bytes. */
+    size_t vpd_len;
+};
+
 struct pciDevice {
     struct pciDeviceAddress addr;
     int vendor;
@@ -131,6 +142,7 @@ struct pciDevice {
     int iommuGroup;
     const char *physfn;
     struct pciDriver *driver;   /* Driver attached. NULL if attached to no driver */
+    struct pciVPD vpd;
 };
 
 struct fdCallback {
@@ -537,6 +549,9 @@ pci_device_new_from_stub(const struct pciDevice *data)
         make_symlink(devpath, "physfn", tmp);
     }
 
+    if (dev->vpd.data && dev->vpd.vpd_len)
+        make_file(devpath, "vpd", dev->vpd.data, dev->vpd.vpd_len);
+
     if (pci_device_autobind(dev) < 0)
         ABORT("Unable to bind: %s", devid);
 
@@ -942,6 +957,20 @@ static void
 init_env(void)
 {
     g_autofree char *tmp = NULL;
+    const char fullVPDExampleData[] = {
+        PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLAG, 0x08, 0x00,
+        't', 'e', 's', 't', 'n', 'a', 'm', 'e',
+        PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x16, 0x00,
+        'P', 'N', 0x02, '4', '2',
+        'E', 'C', 0x04, '4', '2', '4', '2',
+        'V', 'A', 0x02, 'E', 'X',
+        'R', 'V', 0x02, 0x31, 0x00,
+        PCI_VPD_RESOURCE_END_VAL
+    };
+    struct pciVPD exampleVPD = {
+        .data = fullVPDExampleData,
+        .vpd_len = sizeof(fullVPDExampleData) / sizeof(fullVPDExampleData[0]),
+    };
 
     if (!(fakerootdir = getenv("LIBVIRT_FAKE_ROOT_DIR")))
         ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
@@ -1008,6 +1037,8 @@ init_env(void)
 
     MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass = 0x010802);
     MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass = 0x010802);
+
+    MAKE_PCI_DEVICE("0000:03:00.0", 0x15b3, 0xa2d6, 16, .vpd = exampleVPD);
 }
 
 
index 6fe9b7d13a60d940a2216e0ef53dc49a1a77c8f8..4c0f0b91c3367ce6a20d5df37440dae66de657e6 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <config.h>
+#include "internal.h"
 
 #include "testutils.h"
 
@@ -26,6 +27,7 @@
 # include <sys/stat.h>
 # include <fcntl.h>
 # include <virpci.h>
+# include <virpcivpd.h>
 
 # define VIR_FROM_THIS VIR_FROM_NONE
 
@@ -328,6 +330,39 @@ testVirPCIDeviceUnbind(const void *opaque)
     return ret;
 }
 
+
+static int
+testVirPCIDeviceGetVPD(const void *opaque)
+{
+    const struct testPCIDevData *data = opaque;
+    g_autoptr(virPCIDevice) dev = NULL;
+    virPCIDeviceAddress devAddr = {.domain = data->domain, .bus = data->bus,
+                                   .slot = data->slot, .function = data->function};
+    g_autoptr(virPCIVPDResource) res = NULL;
+
+    dev = virPCIDeviceNew(&devAddr);
+    if (!dev)
+        return -1;
+
+    res = virPCIDeviceGetVPD(dev);
+
+    /* Only basic checks - full parser validation is done elsewhere. */
+    if (res->ro == NULL)
+        return -1;
+
+    if (STRNEQ(res->name, "testname")) {
+        VIR_TEST_DEBUG("Unexpected name present in VPD: %s", res->name);
+        return -1;
+    }
+
+    if (STRNEQ(res->ro->part_number, "42")) {
+        VIR_TEST_DEBUG("Unexpected part number value present in VPD: %s", res->ro->part_number);
+        return -1;
+    }
+
+    return 0;
+}
+
 # define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX"
 
 static int
@@ -409,6 +444,8 @@ mymain(void)
     DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 3, 0);
     DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL);
 
+    DO_TEST_PCI(testVirPCIDeviceGetVPD, 0, 0x03, 0, 0);
+
     if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
         virFileDeleteTree(fakerootdir);