@@ -27,7 +27,10 @@
static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg,
uint32_t real_offset, uint32_t *data);
-
+static int xen_pt_ext_cap_ptr_reg_init(XenPCIPassthroughState *s,
+ XenPTRegInfo *reg,
+ uint32_t real_offset,
+ uint32_t *data);
/* helper */
@@ -1928,48 +1931,68 @@ out:
return 0;
}
+#define PCIE_EXT_CAP_NEXT_SHIFT 4
+#define PCIE_EXT_CAP_VER_MASK 0xF
-/*************
- * Main
- */
-
-static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
+static int xen_pt_ext_cap_ptr_reg_init(XenPCIPassthroughState *s,
+ XenPTRegInfo *reg,
+ uint32_t real_offset,
+ uint32_t *data)
{
- uint8_t id;
- unsigned max_cap = XEN_PCI_CAP_MAX;
- uint8_t pos = PCI_CAPABILITY_LIST;
- uint8_t status = 0;
+ int i, rc;
+ XenHostPCIDevice *d = &s->real_device;
+ uint16_t reg_field;
+ uint16_t cur_offset, version, cap_id;
+ uint32_t header;
- if (xen_host_pci_get_byte(&s->real_device, PCI_STATUS, &status)) {
- return 0;
- }
- if ((status & PCI_STATUS_CAP_LIST) == 0) {
- return 0;
+ if (real_offset < 0x0010) {
+ XEN_PT_ERR(&s->dev, "Incorrect PCIe extended capability offset "
+ "encountered: 0x%04x\n", real_offset);
+ return -EINVAL;
}
- while (max_cap--) {
- if (xen_host_pci_get_byte(&s->real_device, pos, &pos)) {
- break;
- }
- if (pos < PCI_CONFIG_HEADER_SIZE) {
- break;
- }
+ rc = xen_host_pci_get_word(d, real_offset, ®_field);
+ if (rc)
+ return rc;
- pos &= ~3;
- if (xen_host_pci_get_byte(&s->real_device,
- pos + PCI_CAP_LIST_ID, &id)) {
- break;
- }
+ /* preserve version field */
+ version = reg_field & PCIE_EXT_CAP_VER_MASK;
+ cur_offset = reg_field >> PCIE_EXT_CAP_NEXT_SHIFT;
- if (id == 0xff) {
- break;
+ while (cur_offset && cur_offset != 0xFFF) {
+ rc = xen_host_pci_get_long(d, cur_offset, &header);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to read PCIe extended capability "
+ "@0x%x (rc:%d)\n", cur_offset, rc);
+ return rc;
}
- if (id == cap) {
- return pos;
+
+ cap_id = PCI_EXT_CAP_ID(header);
+
+ for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) {
+ uint32_t cur_grp_id = xen_pt_emu_reg_grps[i].grp_id;
+
+ if (!IS_PCIE_EXT_CAP_ID(cur_grp_id))
+ continue;
+
+ if (xen_pt_hide_dev_cap(d, cur_grp_id))
+ continue;
+
+ if (GET_PCIE_EXT_CAP_ID(cur_grp_id) == cap_id) {
+ if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU)
+ goto out;
+
+ /* skip TYPE_HARDWIRED capability, move the ptr to next one */
+ break;
+ }
}
- pos += PCI_CAP_LIST_NEXT;
+ /* next capability */
+ cur_offset = PCI_EXT_CAP_NEXT(header);
}
+
+out:
+ *data = (cur_offset << PCIE_EXT_CAP_NEXT_SHIFT) | version;
return 0;
}