Commit 667ac704 authored by Alexandru Elisei's avatar Alexandru Elisei
Browse files

vfio: pci: Allocate correct size for MSIX table and PBA BARs

kvmtool assumes that the BAR that holds the address for the MSIX table
and PBA structure has a size which is equal to the total size of the
table and the PBA structure and it allocates memory from MMIO space
accordingly.  However, when initializing the BARs, the BAR size is set
according to the region size reported by VFIO. When the physical BAR
size is greater than what kvmtool allocated, we can have a situation
where the BAR overlaps with another BAR, in which case kvmtool will fail
to map the memory. This was found when trying to do PCI passthrough on a
PCIe Realtek r8168 NIC.  Let's fix this by allocating an amount of MMIO
memory equal to table + PBA size or BAR size, whichever is greater.
parent e07a44d2
......@@ -715,17 +715,44 @@ static int vfio_pci_fixup_cfg_space(struct vfio_device *vdev)
return 0;
}
static int vfio_pci_create_msix_table(struct kvm *kvm,
struct vfio_pci_device *pdev)
static int vfio_pci_get_region_info(struct vfio_device *vdev, u32 index,
struct vfio_region_info *info)
{
int ret;
*info = (struct vfio_region_info) {
.argsz = sizeof(*info),
.index = index,
};
ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, info);
if (ret) {
ret = -errno;
vfio_dev_err(vdev, "cannot get info for BAR %u", index);
return ret;
}
if (info->size && !is_power_of_two(info->size)) {
vfio_dev_err(vdev, "region is not power of two: 0x%llx",
info->size);
return -EINVAL;
}
return 0;
}
static int vfio_pci_create_msix_table(struct kvm *kvm, struct vfio_device *vdev)
{
int ret;
size_t i;
size_t mmio_size;
size_t mmio_size, bar_size;
size_t nr_entries;
struct vfio_pci_msi_entry *entries;
struct vfio_pci_device *pdev = &vdev->pci;
struct vfio_pci_msix_pba *pba = &pdev->msix_pba;
struct vfio_pci_msix_table *table = &pdev->msix_table;
struct msix_cap *msix = PCI_CAP(&pdev->hdr, pdev->msix.pos);
struct vfio_region_info info;
table->bar = msix->table_offset & PCI_MSIX_TABLE_BIR;
pba->bar = msix->pba_offset & PCI_MSIX_TABLE_BIR;
......@@ -744,12 +771,35 @@ static int vfio_pci_create_msix_table(struct kvm *kvm,
for (i = 0; i < nr_entries; i++)
entries[i].config.ctrl = PCI_MSIX_ENTRY_CTRL_MASKBIT;
ret = vfio_pci_get_region_info(vdev, table->bar, &info);
if (ret)
return ret;
if (!info.size)
return -EINVAL;
/* MSIX Table BAR must map Memory Space. */
if (pdev->hdr.bar[table->bar] & PCI_BASE_ADDRESS_SPACE_IO)
return -EINVAL;
bar_size = info.size;
if (table->bar != pba->bar) {
ret = vfio_pci_get_region_info(vdev, pba->bar, &info);
if (ret)
return ret;
if (!info.size)
return -EINVAL;
/* MSIX PBA BAR must map Memory Space. */
if (pdev->hdr.bar[pba->bar] & PCI_BASE_ADDRESS_SPACE_IO)
return -EINVAL;
bar_size += info.size;
}
/*
* To ease MSI-X cap configuration in case they share the same BAR,
* collapse table and pending array. The size of the BAR regions must be
* powers of two.
*/
mmio_size = roundup_pow_of_two(table->size + pba->size);
mmio_size = max(table->size + pba->size, bar_size);
mmio_size = roundup_pow_of_two(mmio_size);
table->guest_phys_addr = pci_get_mmio_block(mmio_size);
if (!table->guest_phys_addr) {
pr_err("cannot allocate IO space");
......@@ -816,26 +866,14 @@ static int vfio_pci_configure_bar(struct kvm *kvm, struct vfio_device *vdev,
region->vdev = vdev;
region->is_ioport = !!(bar & PCI_BASE_ADDRESS_SPACE_IO);
region->info = (struct vfio_region_info) {
.argsz = sizeof(region->info),
.index = nr,
};
ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &region->info);
if (ret) {
ret = -errno;
vfio_dev_err(vdev, "cannot get info for BAR %zu", nr);
ret = vfio_pci_get_region_info(vdev, nr, &region->info);
if (ret)
return ret;
}
/* Ignore invalid or unimplemented regions */
if (!region->info.size)
return 0;
if (!is_power_of_two(region->info.size)) {
vfio_dev_err(vdev, "region is not power of two: 0x%llx",
region->info.size);
return -EINVAL;
}
if (pdev->irq_modes & VFIO_PCI_IRQ_MODE_MSIX) {
/* Trap and emulate MSI-X table */
......@@ -876,7 +914,7 @@ static int vfio_pci_configure_dev_regions(struct kvm *kvm,
return ret;
if (pdev->irq_modes & VFIO_PCI_IRQ_MODE_MSIX) {
ret = vfio_pci_create_msix_table(kvm, pdev);
ret = vfio_pci_create_msix_table(kvm, vdev);
if (ret)
return ret;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment