in pci/vfio_pci_config.c [1462:1550]
static int vfio_cap_init(struct vfio_pci_core_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
u8 *map = vdev->pci_config_map;
u16 status;
u8 pos, *prev, cap;
int loops, ret, caps = 0;
/* Any capabilities? */
ret = pci_read_config_word(pdev, PCI_STATUS, &status);
if (ret)
return ret;
if (!(status & PCI_STATUS_CAP_LIST))
return 0; /* Done */
ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos);
if (ret)
return ret;
/* Mark the previous position in case we want to skip a capability */
prev = &vdev->vconfig[PCI_CAPABILITY_LIST];
/* We can bound our loop, capabilities are dword aligned */
loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF;
while (pos && loops--) {
u8 next;
int i, len = 0;
ret = pci_read_config_byte(pdev, pos, &cap);
if (ret)
return ret;
ret = pci_read_config_byte(pdev,
pos + PCI_CAP_LIST_NEXT, &next);
if (ret)
return ret;
/*
* ID 0 is a NULL capability, conflicting with our fake
* PCI_CAP_ID_BASIC. As it has no content, consider it
* hidden for now.
*/
if (cap && cap <= PCI_CAP_ID_MAX) {
len = pci_cap_length[cap];
if (len == 0xFF) { /* Variable length */
len = vfio_cap_len(vdev, cap, pos);
if (len < 0)
return len;
}
}
if (!len) {
pci_info(pdev, "%s: hiding cap %#x@%#x\n", __func__,
cap, pos);
*prev = next;
pos = next;
continue;
}
/* Sanity check, do we overlap other capabilities? */
for (i = 0; i < len; i++) {
if (likely(map[pos + i] == PCI_CAP_ID_INVALID))
continue;
pci_warn(pdev, "%s: PCI config conflict @%#x, was cap %#x now cap %#x\n",
__func__, pos + i, map[pos + i], cap);
}
BUILD_BUG_ON(PCI_CAP_ID_MAX >= PCI_CAP_ID_INVALID_VIRT);
memset(map + pos, cap, len);
ret = vfio_fill_vconfig_bytes(vdev, pos, len);
if (ret)
return ret;
prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT];
pos = next;
caps++;
}
/* If we didn't fill any capabilities, clear the status flag */
if (!caps) {
__le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS];
*vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST);
}
return 0;
}