From d79e45806cba72727548e1de10e65a1084f475ac Mon Sep 17 00:00:00 2001 From: Ryan Stone Date: Tue, 15 Apr 2014 23:15:54 -0400 Subject: [PATCH 08/21] Emulate the Device ID and Vendor ID registers for VFs The SR-IOV standard requires VFs to read all-ones when the VID and DID registers are read. The VMM (hypervisor) is required to emulate them instead. Make pci_read_config() do this emulation. Change pci_user.c to use pci_read_config() to read config space registers instead of going directly to the pcib so that the emulated VID/DID registers work correctly on VFs. This is required both for pciconf and bhyte PCI passthrough. --- sys/dev/pci/pci.c | 31 +++++++++++++++++++++++++++++++ sys/dev/pci/pci_user.c | 20 ++++---------------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index b08b3a2..d434c35 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -4845,6 +4845,37 @@ pci_read_config_method(device_t dev, device_t child, int reg, int width) struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; +#ifdef PCI_IOV + /* + * SR-IOV VFs don't implement the VID or DID registers, so we have to + * emulate them here. + */ + if (cfg->flags & PCICFG_VF) { + if (reg == PCIR_VENDOR) { + switch (width) { + case 4: + return (cfg->device << 16 | cfg->vendor); + case 2: + return (cfg->vendor); + case 1: + return (cfg->vendor & 0xff); + default: + return (0xffffffff); + } + } else if (reg == PCIR_DEVICE) { + switch (width) { + /* Note that an unaligned 4-byte read is an error. */ + case 2: + return (cfg->device); + case 1: + return (cfg->device & 0xff); + default: + return (0xffffffff); + } + } + } +#endif + return (PCIB_READ_CONFIG(device_get_parent(dev), cfg->bus, cfg->slot, cfg->func, reg, width)); } diff --git a/sys/dev/pci/pci_user.c b/sys/dev/pci/pci_user.c index 38290c1..e69fde2 100644 --- a/sys/dev/pci/pci_user.c +++ b/sys/dev/pci/pci_user.c @@ -492,7 +492,7 @@ pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio) static int pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { - device_t pcidev, brdev; + device_t pcidev; void *confdata; const char *name; struct devlist *devlist_head; @@ -922,37 +922,25 @@ getconfexit: io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func); if (pcidev) { - brdev = device_get_parent( - device_get_parent(pcidev)); - #ifdef PRE7_COMPAT if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD) #else if (cmd == PCIOCWRITE) #endif - PCIB_WRITE_CONFIG(brdev, - io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, + pci_write_config(pcidev, io->pi_reg, io->pi_data, io->pi_width); #ifdef PRE7_COMPAT else if (cmd == PCIOCREAD_OLD) io_old->pi_data = - PCIB_READ_CONFIG(brdev, - io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, + pci_read_config(pcidev, io->pi_reg, io->pi_width); #endif else io->pi_data = - PCIB_READ_CONFIG(brdev, - io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, + pci_read_config(pcidev, io->pi_reg, io->pi_width); error = 0; -- 1.9.2