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 bhyve PCI passthrough.

Differential Revision:	https://reviews.freebsd.org/D77
Reviewed by:		jhb
MFC after: 		1 month
Sponsored by:		Sandvine Inc.
This commit is contained in:
Ryan Stone 2015-03-01 00:40:19 +00:00
parent 9bfb1e36d9
commit 5060ec97d4
2 changed files with 35 additions and 16 deletions

View File

@ -4880,6 +4880,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));
}

View File

@ -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 @@ pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *t
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;