in pci/vfio_pci_config.c [1552:1648]
static int vfio_ecap_init(struct vfio_pci_core_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
u8 *map = vdev->pci_config_map;
u16 epos;
__le32 *prev = NULL;
int loops, ret, ecaps = 0;
if (!vdev->extended_caps)
return 0;
epos = PCI_CFG_SPACE_SIZE;
loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF;
while (loops-- && epos >= PCI_CFG_SPACE_SIZE) {
u32 header;
u16 ecap;
int i, len = 0;
bool hidden = false;
ret = pci_read_config_dword(pdev, epos, &header);
if (ret)
return ret;
ecap = PCI_EXT_CAP_ID(header);
if (ecap <= PCI_EXT_CAP_ID_MAX) {
len = pci_ext_cap_length[ecap];
if (len == 0xFF) {
len = vfio_ext_cap_len(vdev, ecap, epos);
if (len < 0)
return len;
}
}
if (!len) {
pci_info(pdev, "%s: hiding ecap %#x@%#x\n",
__func__, ecap, epos);
/* If not the first in the chain, we can skip over it */
if (prev) {
u32 val = epos = PCI_EXT_CAP_NEXT(header);
*prev &= cpu_to_le32(~(0xffcU << 20));
*prev |= cpu_to_le32(val << 20);
continue;
}
/*
* Otherwise, fill in a placeholder, the direct
* readfn will virtualize this automatically
*/
len = PCI_CAP_SIZEOF;
hidden = true;
}
for (i = 0; i < len; i++) {
if (likely(map[epos + i] == PCI_CAP_ID_INVALID))
continue;
pci_warn(pdev, "%s: PCI config conflict @%#x, was ecap %#x now ecap %#x\n",
__func__, epos + i, map[epos + i], ecap);
}
/*
* Even though ecap is 2 bytes, we're currently a long way
* from exceeding 1 byte capabilities. If we ever make it
* up to 0xFE we'll need to up this to a two-byte, byte map.
*/
BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID_VIRT);
memset(map + epos, ecap, len);
ret = vfio_fill_vconfig_bytes(vdev, epos, len);
if (ret)
return ret;
/*
* If we're just using this capability to anchor the list,
* hide the real ID. Only count real ecaps. XXX PCI spec
* indicates to use cap id = 0, version = 0, next = 0 if
* ecaps are absent, hope users check all the way to next.
*/
if (hidden)
*(__le32 *)&vdev->vconfig[epos] &=
cpu_to_le32((0xffcU << 20));
else
ecaps++;
prev = (__le32 *)&vdev->vconfig[epos];
epos = PCI_EXT_CAP_NEXT(header);
}
if (!ecaps)
*(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0;
return 0;
}