static int vfio_cap_init()

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