Skip to content

Commit

Permalink
vfio: add pcie extended capability support
Browse files Browse the repository at this point in the history
For vfio pcie device, we could expose the extended capability on
PCIE bus. due to add a new pcie capability at the tail of the chain,
in order to avoid config space overwritten, we introduce a copy config
for parsing extended caps. and rebuild the pcie extended config space.

Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
  • Loading branch information
Chen Fan authored and awilliam committed Jun 30, 2016
1 parent 4d3fc4f commit 325ae8d
Showing 1 changed file with 71 additions and 1 deletion.
72 changes: 71 additions & 1 deletion hw/vfio/pci.c
Expand Up @@ -1502,6 +1502,21 @@ static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos)
return next - pos;
}


static uint16_t vfio_ext_cap_max_size(const uint8_t *config, uint16_t pos)
{
uint16_t tmp, next = PCIE_CONFIG_SPACE_SIZE;

for (tmp = PCI_CONFIG_SPACE_SIZE; tmp;
tmp = PCI_EXT_CAP_NEXT(pci_get_long(config + tmp))) {
if (tmp > pos && tmp < next) {
next = tmp;
}
}

return next - pos;
}

static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask)
{
pci_set_word(buf, (pci_get_word(buf) & ~mask) | val);
Expand Down Expand Up @@ -1749,16 +1764,71 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
return 0;
}

static int vfio_add_ext_cap(VFIOPCIDevice *vdev)
{
PCIDevice *pdev = &vdev->pdev;
uint32_t header;
uint16_t cap_id, next, size;
uint8_t cap_ver;
uint8_t *config;

/*
* pcie_add_capability always inserts the new capability at the tail
* of the chain. Therefore to end up with a chain that matches the
* physical device, we cache the config space to avoid overwriting
* the original config space when we parse the extended capabilities.
*/
config = g_memdup(pdev->config, vdev->config_size);

for (next = PCI_CONFIG_SPACE_SIZE; next;
next = PCI_EXT_CAP_NEXT(pci_get_long(config + next))) {
header = pci_get_long(config + next);
cap_id = PCI_EXT_CAP_ID(header);
cap_ver = PCI_EXT_CAP_VER(header);

/*
* If it becomes important to configure extended capabilities to their
* actual size, use this as the default when it's something we don't
* recognize. Since QEMU doesn't actually handle many of the config
* accesses, exact size doesn't seem worthwhile.
*/
size = vfio_ext_cap_max_size(config, next);

pcie_add_capability(pdev, cap_id, cap_ver, next, size);
pci_set_long(pdev->config + next, PCI_EXT_CAP(cap_id, cap_ver, 0));

/* Use emulated next pointer to allow dropping extended caps */
pci_long_test_and_set_mask(vdev->emulated_config_bits + next,
PCI_EXT_CAP_NEXT_MASK);
}

g_free(config);
return 0;
}

static int vfio_add_capabilities(VFIOPCIDevice *vdev)
{
PCIDevice *pdev = &vdev->pdev;
int ret;

if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) ||
!pdev->config[PCI_CAPABILITY_LIST]) {
return 0; /* Nothing to add */
}

return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
if (ret) {
return ret;
}

/* on PCI bus, it doesn't make sense to expose extended capabilities. */
if (!pci_is_express(pdev) ||
!pci_bus_is_express(pdev->bus) ||
!pci_get_long(pdev->config + PCI_CONFIG_SPACE_SIZE)) {
return 0;
}

return vfio_add_ext_cap(vdev);
}

static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
Expand Down

0 comments on commit 325ae8d

Please sign in to comment.