return (buf[0] << 0) | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
+static int
+virPCIDeviceReadClass(virPCIDevicePtr dev, uint16_t *device_class)
+{
+ char *path = NULL;
+ char *id_str = NULL;
+ int ret = -1;
+ unsigned int value;
+
+ if (virPCIFile(&path, dev->name, "class") < 0)
+ return ret;
+
+ /* class string is '0xNNNNNN\n' ... i.e. 9 bytes */
+ if (virFileReadAll(path, 9, &id_str) < 0)
+ goto cleanup;
+
+ id_str[8] = '\0';
+ if (virStrToLong_ui(id_str, NULL, 16, &value) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unusual value in %s/devices/%s/class: %s"),
+ PCI_SYSFS, dev->name, id_str);
+ goto cleanup;
+ }
+
+ *device_class = (value >> 8) & 0xFFFF;
+ ret = 0;
+cleanup:
+ VIR_FREE(id_str);
+ VIR_FREE(path);
+ return ret;
+}
+
static int
virPCIDeviceWrite(virPCIDevicePtr dev,
int cfgfd,
return 0;
/* Is it a bridge? */
- device_class = virPCIDeviceRead16(check, fd, PCI_CLASS_DEVICE);
- if (device_class != PCI_CLASS_BRIDGE_PCI)
+ ret = virPCIDeviceReadClass(check, &device_class);
+ if (ret < 0 || device_class != PCI_CLASS_BRIDGE_PCI)
goto cleanup;
/* Is it a plane? */
unsigned int pos;
int fd;
int ret = 0;
+ uint16_t device_class;
if ((fd = virPCIDeviceConfigOpen(dev, true)) < 0)
return -1;
goto cleanup;
}
+ if (virPCIDeviceReadClass(dev, &device_class) < 0)
+ goto cleanup;
+
pos = dev->pcie_cap_pos;
- if (!pos || virPCIDeviceRead16(dev, fd, PCI_CLASS_DEVICE) != PCI_CLASS_BRIDGE_PCI)
+ if (!pos || device_class != PCI_CLASS_BRIDGE_PCI)
goto cleanup;
flags = virPCIDeviceRead16(dev, fd, pos + PCI_EXP_FLAGS);
# include <fcntl.h>
# include <sys/stat.h>
# include <stdarg.h>
+# include <dirent.h>
# include "viralloc.h"
# include "virstring.h"
# include "virfile.h"
static char *(*realcanonicalize_file_name)(const char *path);
static int (*realopen)(const char *path, int flags, ...);
static int (*realclose)(int fd);
+static DIR * (*realopendir)(const char *name);
/* Don't make static, since it causes problems with clang
* when passed as an arg to virAsprintf()
char *id;
int vendor;
int device;
+ int class;
struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */
};
ABORT("@tmp overflow");
make_file(devpath, "device", tmp, -1);
+ if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->class) < 0)
+ ABORT("@tmp overflow");
+ make_file(devpath, "class", tmp, -1);
+
if (pci_device_autobind(dev) < 0)
ABORT("Unable to bind: %s", data->id);
LOAD_SYM(canonicalize_file_name);
LOAD_SYM(open);
LOAD_SYM(close);
+ LOAD_SYM(opendir);
}
static void
MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044);
MAKE_PCI_DEVICE("0000:00:02.0", 0x8086, 0x0046);
MAKE_PCI_DEVICE("0000:00:03.0", 0x8086, 0x0048);
+ MAKE_PCI_DEVICE("0001:00:00.0", 0x1014, 0x03b9, .class = 0x060400);
+ MAKE_PCI_DEVICE("0001:01:00.0", 0x8086, 0x105e);
+ MAKE_PCI_DEVICE("0001:01:00.1", 0x8086, 0x105e);
+ MAKE_PCI_DEVICE("0005:80:00.0", 0x10b5, 0x8112, .class = 0x060400);
+ MAKE_PCI_DEVICE("0005:90:01.0", 0x1033, 0x0035);
+ MAKE_PCI_DEVICE("0005:90:01.1", 0x1033, 0x0035);
+ MAKE_PCI_DEVICE("0005:90:01.2", 0x1033, 0x00e0);
}
return ret;
}
+DIR *
+opendir(const char *path)
+{
+ DIR *ret;
+ char *newpath = NULL;
+
+ init_syms();
+
+ if (STRPREFIX(path, PCI_SYSFS_PREFIX) &&
+ getrealpath(&newpath, path) < 0)
+ return NULL;
+
+ ret = realopendir(newpath ? newpath : path);
+
+ VIR_FREE(newpath);
+ return ret;
+}
+
int
close(int fd)
{
return ret;
}
+struct testPCIDevData {
+ unsigned int domain;
+ unsigned int bus;
+ unsigned int slot;
+ unsigned int function;
+};
+
+static int
+testVirPCIDeviceIsAssignable(const void *opaque)
+{
+ const struct testPCIDevData *data = opaque;
+ int ret = -1;
+ virPCIDevicePtr dev;
+
+ if (!(dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function)))
+ goto cleanup;
+
+ if (virPCIDeviceIsAssignable(dev, true))
+ ret = 0;
+
+ virPCIDeviceFree(dev);
+cleanup:
+ return ret;
+}
+
# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX"
static int
ret = -1; \
} while (0)
+# define DO_TEST_PCI(fnc, domain, bus, slot, function) \
+ do { \
+ struct testPCIDevData data = { domain, bus, slot, function }; \
+ if (virtTestRun(#fnc, fnc, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
DO_TEST(testVirPCIDeviceNew);
DO_TEST(testVirPCIDeviceDetach);
DO_TEST(testVirPCIDeviceReset);
DO_TEST(testVirPCIDeviceReattach);
+ DO_TEST_PCI(testVirPCIDeviceIsAssignable, 5, 0x90, 1, 0);
+ DO_TEST_PCI(testVirPCIDeviceIsAssignable, 1, 1, 0, 0);
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
virFileDeleteTree(fakesysfsdir);