Skip to content

Commit

Permalink
pcie_sriov: Ensure VF addr does not overflow
Browse files Browse the repository at this point in the history
pci_new() aborts when creating a VF with addr >= PCI_DEVFN_MAX.

Signed-off-by: Akihiko Odaki <[email protected]>
Message-Id: <[email protected]>
Reviewed-by: Michael S. Tsirkin <[email protected]>
Signed-off-by: Michael S. Tsirkin <[email protected]>
  • Loading branch information
akihikodaki authored and mstsirkin committed Feb 20, 2025
1 parent e8b827c commit 3391d68
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 18 deletions.
8 changes: 5 additions & 3 deletions docs/pcie_sriov.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ setting up a BAR for a VF.
...

/* Add and initialize the SR/IOV capability */
pcie_sriov_pf_init(d, 0x200, "your_virtual_dev",
vf_devid, initial_vfs, total_vfs,
fun_offset, stride);
if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev",
vf_devid, initial_vfs, total_vfs,
fun_offset, stride, errp)) {
return;
}

/* Set up individual VF BARs (parameters as for normal BARs) */
pcie_sriov_pf_init_vf_bar( ... )
Expand Down
10 changes: 7 additions & 3 deletions hw/net/igb.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,13 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp)

pcie_ari_init(pci_dev, 0x150);

pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF,
IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS,
IGB_VF_OFFSET, IGB_VF_STRIDE);
if (!pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF,
IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS,
IGB_MAX_VF_FUNCTIONS, IGB_VF_OFFSET, IGB_VF_STRIDE,
errp)) {
igb_cleanup_msix(s);
return;
}

pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX,
PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH,
Expand Down
22 changes: 14 additions & 8 deletions hw/nvme/ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -8481,7 +8481,8 @@ static uint64_t nvme_mbar_size(unsigned total_queues, unsigned total_irqs,
return pow2ceil(bar_size);
}

static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset)
static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset,
Error **errp)
{
uint16_t vf_dev_id = n->params.use_intel_id ?
PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME;
Expand All @@ -8490,12 +8491,16 @@ static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset)
le16_to_cpu(cap->vifrsm),
NULL, NULL);

pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id,
n->params.sriov_max_vfs, n->params.sriov_max_vfs,
NVME_VF_OFFSET, NVME_VF_STRIDE);
if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id,
n->params.sriov_max_vfs, n->params.sriov_max_vfs,
NVME_VF_OFFSET, NVME_VF_STRIDE, errp)) {
return false;
}

pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size);

return true;
}

static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset)
Expand Down Expand Up @@ -8620,6 +8625,11 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
return false;
}

if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs &&
!nvme_init_sriov(n, pci_dev, 0x120, errp)) {
return false;
}

nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize);

pcie_cap_deverr_init(pci_dev);
Expand Down Expand Up @@ -8649,10 +8659,6 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
nvme_init_pmr(n, pci_dev);
}

if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) {
nvme_init_sriov(n, pci_dev, 0x120);
}

return true;
}

Expand Down
14 changes: 12 additions & 2 deletions hw/pci/pcie_sriov.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@ static PCIDevice *register_vf(PCIDevice *pf, int devfn,
const char *name, uint16_t vf_num);
static void unregister_vfs(PCIDevice *dev);

void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
const char *vfname, uint16_t vf_dev_id,
uint16_t init_vfs, uint16_t total_vfs,
uint16_t vf_offset, uint16_t vf_stride)
uint16_t vf_offset, uint16_t vf_stride,
Error **errp)
{
int32_t devfn = dev->devfn + vf_offset;
uint8_t *cfg = dev->config + offset;
uint8_t *wmask;

if (total_vfs &&
(uint32_t)devfn + (uint32_t)(total_vfs - 1) * vf_stride >= PCI_DEVFN_MAX) {
error_setg(errp, "VF addr overflows");
return false;
}

pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1,
offset, PCI_EXT_CAP_SRIOV_SIZEOF);
dev->exp.sriov_cap = offset;
Expand Down Expand Up @@ -69,6 +77,8 @@ void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553);

qdev_prop_set_bit(&dev->qdev, "multifunction", true);

return true;
}

void pcie_sriov_pf_exit(PCIDevice *dev)
Expand Down
5 changes: 3 additions & 2 deletions include/hw/pci/pcie_sriov.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ typedef struct PCIESriovVF {
uint16_t vf_number; /* Logical VF number of this function */
} PCIESriovVF;

void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
const char *vfname, uint16_t vf_dev_id,
uint16_t init_vfs, uint16_t total_vfs,
uint16_t vf_offset, uint16_t vf_stride);
uint16_t vf_offset, uint16_t vf_stride,
Error **errp);
void pcie_sriov_pf_exit(PCIDevice *dev);

/* Set up a VF bar in the SR/IOV bar area */
Expand Down

0 comments on commit 3391d68

Please sign in to comment.