diff --git a/devicemodel/hw/pci/passthrough.c b/devicemodel/hw/pci/passthrough.c index f0c47586f..091deffd9 100644 --- a/devicemodel/hw/pci/passthrough.c +++ b/devicemodel/hw/pci/passthrough.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -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,11 +443,7 @@ 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; + return 0; } static uint64_t @@ -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);