int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap) { uint32_t header = 0; int max_cap = XEN_HOST_PCI_MAX_EXT_CAP; int pos = PCI_CONFIG_SPACE_SIZE; do { if (xen_host_pci_get_long(d, pos, &header)) { break; } /* * If we have no capabilities, this is indicated by cap ID, * cap version and next pointer all being 0. */ if (header == 0) { break; } if (PCI_EXT_CAP_ID(header) == cap) { return pos; } pos = PCI_EXT_CAP_NEXT(header); if (pos < PCI_CONFIG_SPACE_SIZE) { break; } max_cap--; } while (max_cap > 0); return -1; }
static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id, uint16_t *prev_p) { uint16_t prev = 0; uint16_t next; uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE); if (!header) { /* no extended capability */ next = 0; goto out; } for (next = PCI_CONFIG_SPACE_SIZE; next; prev = next, next = PCI_EXT_CAP_NEXT(header)) { assert(next >= PCI_CONFIG_SPACE_SIZE); assert(next <= PCIE_CONFIG_SPACE_SIZE - 8); header = pci_get_long(dev->config + next); if (PCI_EXT_CAP_ID(header) == cap_id) { break; } } out: if (prev_p) { *prev_p = prev; } return next; }
/* * caller must supply valid (offset, size) * such that the range shouldn't * overlap with other capability or other registers. * This function doesn't check it. */ void pcie_add_capability(PCIDevice *dev, uint16_t cap_id, uint8_t cap_ver, uint16_t offset, uint16_t size) { uint32_t header; uint16_t next; assert(offset >= PCI_CONFIG_SPACE_SIZE); assert(offset < offset + size); assert(offset + size < PCIE_CONFIG_SPACE_SIZE); assert(size >= 8); assert(pci_is_express(dev)); if (offset == PCI_CONFIG_SPACE_SIZE) { header = pci_get_long(dev->config + offset); next = PCI_EXT_CAP_NEXT(header); } else { uint16_t prev; /* 0 is reserved cap id. use internally to find the last capability in the linked list */ next = pcie_find_capability_list(dev, 0, &prev); assert(prev >= PCI_CONFIG_SPACE_SIZE); assert(next == 0); pcie_ext_cap_set_next(dev, prev, offset); } pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next)); /* Make capability read-only by default */ memset(dev->wmask + offset, 0, size); memset(dev->w1cmask + offset, 0, size); /* Check capability by default */ memset(dev->cmask + offset, 0xFF, size); }
/** * fixed_bar_cap - return the offset of the fixed BAR cap if found * @bus: PCI bus * @devfn: device in question * * Look for the fixed BAR cap on @bus and @devfn, returning its offset * if found or 0 otherwise. */ static int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) { int pos; u32 pcie_cap = 0, cap_data; pos = PCIE_CAP_OFFSET; if (!raw_pci_ext_ops) return 0; while (pos) { if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, devfn, pos, 4, &pcie_cap)) return 0; if (PCI_EXT_CAP_ID(pcie_cap) == 0x0000 || PCI_EXT_CAP_ID(pcie_cap) == 0xffff) break; if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, devfn, pos + 4, 4, &cap_data); if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) return pos; } pos = PCI_EXT_CAP_NEXT(pcie_cap); } return 0; }