virPCIDeviceGetStubDriver;
virPCIDeviceGetUnbindFromStub;
virPCIDeviceGetUsedBy;
+virPCIDeviceGetVPD;
virPCIDeviceHasPCIExpressLink;
+virPCIDeviceHasVPD;
virPCIDeviceIsAssignable;
virPCIDeviceIsPCIExpress;
virPCIDeviceListAdd;
#include "virkmod.h"
#include "virstring.h"
#include "viralloc.h"
+#include "virpcivpd.h"
VIR_LOG_INIT("util.pci");
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");
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
#include "virmdev.h"
#include "virobject.h"
#include "virenum.h"
+#include "virpcivpd.h"
typedef struct _virPCIDevice virPCIDevice;
typedef struct _virPCIDeviceAddress virPCIDeviceAddress;
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,
#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"
};
# 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;
int iommuGroup;
const char *physfn;
struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */
+ struct pciVPD vpd;
};
struct fdCallback {
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);
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");
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);
}
*/
#include <config.h>
+#include "internal.h"
#include "testutils.h"
# include <sys/stat.h>
# include <fcntl.h>
# include <virpci.h>
+# include <virpcivpd.h>
# define VIR_FROM_THIS VIR_FROM_NONE
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
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);