DM: refuse passthrough PCIe without reset support
Check reset method for PCIe dev according to 'reset' in sysfs, which
indicates reset capability, like FLR and secondary bus reset. PCIe dev
without reset capability is refused for passthrough to avoid failure
after UOS reboot.
Acked-by: Kevin Tian
#include <stdbool.h>
#include <string.h>
#include <err.h>
+#include <errno.h>
#include <fcntl.h>
#include <sysexits.h>
#include <unistd.h>
@@ -102,6 +103,7 @@ struct passthru_dev {
int capoff;
int table_size;
} msix;
+ bool pcie_cap;
struct pcisel sel;
int phys_pin;
uint16_t phys_bdf;
@@ -318,7 +320,7 @@ clear_mmc_mme(uint32_t *val)
#endif
static int
-cfginitmsi(struct vmctx *ctx, struct passthru_dev *ptdev)
+cfginit_cap(struct vmctx *ctx, struct passthru_dev *ptdev)
{
int i, ptr, capptr, cap, sts, caplen, table_size;
uint32_t u32;
@@ -393,7 +395,9 @@ cfginitmsi(struct vmctx *ctx, struct passthru_dev *ptdev)
caplen -= 4;
capptr += 4;
}
- }
+ } else if (cap == PCIY_EXPRESS)
+ ptdev->pcie_cap = true;
+
ptr = read_config(phys_dev, ptr + PCICAP_NEXTPTR, 1);
}
}
@@ -439,10 +443,6 @@ cfginitmsi(struct vmctx *ctx, struct passthru_dev *ptdev)
vm_set_ptdev_msix_info(ctx, &ptirq);
}
- /* Make sure one of the capabilities is present */
- if (ptdev->msi.capoff == 0 && ptdev->msix.capoff == 0)
- return -1;
- else
return 0;
}
@@ -809,18 +809,47 @@ cfginit(struct vmctx *ctx, struct passthru_dev *ptdev, int bus,
int slot, int func)
{
int irq_type = IRQ_MSI;
+ char reset_path[60];
+ FILE *f;
bzero(&ptdev->sel, sizeof(struct pcisel));
ptdev->sel.bus = bus;
ptdev->sel.dev = slot;
ptdev->sel.func = func;
- if (cfginitmsi(ctx, ptdev) != 0) {
+ if (cfginit_cap(ctx, ptdev) != 0) {
+ warnx("Capability check fails for PCI %x/%x/%x",
+ bus, slot, func);
+ return -1;
+ }
+
+ /* Check MSI or MSIX capabilities */
+ if (ptdev->msi.capoff == 0 && ptdev->msix.capoff == 0) {
warnx("MSI not supported for PCI %x/%x/%x",
bus, slot, func);
irq_type = IRQ_INTX;
}
+ /* Check reset method for PCIe dev. If SOS kernel provides 'reset'
+ * entry in sysfs, related dev has some reset capability, e.g. FLR, or
+ * secondary bus reset. PCIe dev without any reset capability is
+ * refused for passthrough.
+ */
+ if (ptdev->pcie_cap) {
+ snprintf(reset_path, 40,
+ "/sys/bus/pci/devices/0000:%02x:%02x.%x/reset",
+ bus, slot, func);
+
+ if ((f = fopen(reset_path, "r")))
+ fclose(f);
+ else if (errno == ENOENT) {
+ warnx("No reset capability for PCIe %x/%x/%x, "
+ "remove it from ptdev list!!\n",
+ bus, slot, func);
+ return -1;
+ }
+ }
+
if (cfginitbar(ctx, ptdev) != 0) {
warnx("failed to initialize BARs for PCI %x/%x/%x",
bus, slot, func);
