static int vfio_ecap_init()

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;
}