/* * 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); }
/* * Caller must supply valid (offset, size) such that the range wouldn'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) { 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) { uint16_t prev; /* * 0xffffffff is not a valid cap id (it's a 16 bit field). use * internally to find the last capability in the linked list. */ pcie_find_capability_list(dev, 0xffffffff, &prev); assert(prev >= PCI_CONFIG_SPACE_SIZE); pcie_ext_cap_set_next(dev, prev, offset); } pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, 0)); /* 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); }
bool pcie_cap_is_arifwd_enabled(const PCIDevice *dev) { if (!pci_is_express(dev)) { return false; } if (!dev->exp.exp_cap) { return false; } return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & PCI_EXP_DEVCTL2_ARI; }
/*************************************************************************** * pci express capability helper functions */ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) { int pos; uint8_t *exp_cap; assert(pci_is_express(dev)); pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER2_SIZEOF); if (pos < 0) { return pos; } dev->exp.exp_cap = pos; exp_cap = dev->config + pos; /* capability register interrupt message number defaults to 0 */ pci_set_word(exp_cap + PCI_EXP_FLAGS, ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) | PCI_EXP_FLAGS_VER2); /* device capability register * table 7-12: * roll based error reporting bit must be set by all * Functions conforming to the ECN, PCI Express Base * Specification, Revision 1.1., or subsequent PCI Express Base * Specification revisions. */ pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER); pci_set_long(exp_cap + PCI_EXP_LNKCAP, (port << PCI_EXP_LNKCAP_PN_SHIFT) | PCI_EXP_LNKCAP_ASPMS_0S | PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); pci_set_word(exp_cap + PCI_EXP_LNKSTA, PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); pci_set_long(exp_cap + PCI_EXP_DEVCAP2, PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB); return pos; }
int pcie_cap_v1_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) { /* PCIe cap v1 init */ int pos; Error *local_err = NULL; assert(pci_is_express(dev)); pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER1_SIZEOF, &local_err); if (pos < 0) { error_report_err(local_err); return pos; } dev->exp.exp_cap = pos; pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER1); return pos; }
int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port, Error **errp) { /* PCIe cap v2 init */ int pos; uint8_t *exp_cap; assert(pci_is_express(dev)); pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER2_SIZEOF, errp); if (pos < 0) { return pos; } dev->exp.exp_cap = pos; exp_cap = dev->config + pos; /* Filling values common with v1 */ pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER2); /* Fill link speed and width options */ pcie_cap_fill_slot_lnk(dev); /* Filling v2 specific values */ pci_set_long(exp_cap + PCI_EXP_DEVCAP2, PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); pci_set_word(dev->wmask + pos + PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_EETLPPB); if (dev->cap_present & QEMU_PCIE_EXTCAP_INIT) { /* read-only to behave like a 'NULL' Extended Capability Header */ pci_set_long(dev->wmask + PCI_CONFIG_SPACE_SIZE, 0); } return pos; }
/* * 6.2.6 Error Message Control Figure 6-3 * * Walk up the bus tree from the device, propagate the error message. */ static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) { uint8_t type; while (dev) { if (!pci_is_express(dev)) { /* just ignore it */ /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR? * Consider e.g. a PCI bridge above a PCI Express device. */ return; } type = pcie_cap_get_type(dev); if ((type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_UPSTREAM || type == PCI_EXP_TYPE_DOWNSTREAM) && !pcie_aer_msg_vbridge(dev, msg)) { return; } if (!pcie_aer_msg_alldev(dev, msg)) { return; } if (type == PCI_EXP_TYPE_ROOT_PORT) { pcie_aer_msg_root_port(dev, msg); /* Root port can notify system itself, or send the error message to root complex event collector. */ /* * if root port is associated with an event collector, * return the root complex event collector here. * For now root complex event collector isn't supported. */ return; } dev = pci_bridge_get_device(dev->bus); } }
/* * non-Function specific error must be recorded in all functions. * It is the responsibility of the caller of this function. * It is also caller's responsibility to determine which function should * report the error. * * 6.2.4 Error Logging * 6.2.5 Sequence of Device Error Signaling and Logging Operations * Figure 6-2: Flowchart Showing Sequence of Device Error Signaling and Logging * Operations */ int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) { uint8_t *aer_cap = NULL; uint16_t devctl = 0; uint16_t devsta = 0; uint32_t error_status = err->status; PCIEAERInject inj; if (!pci_is_express(dev)) { return -ENOSYS; } if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { error_status &= PCI_ERR_COR_SUPPORTED; } else { error_status &= PCI_ERR_UNC_SUPPORTED; } /* invalid status bit. one and only one bit must be set */ if (!error_status || (error_status & (error_status - 1))) { return -EINVAL; } if (dev->exp.aer_cap) { uint8_t *exp_cap = dev->config + dev->exp.exp_cap; aer_cap = dev->config + dev->exp.aer_cap; devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL); devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA); } inj.dev = dev; inj.aer_cap = aer_cap; inj.err = err; inj.devctl = devctl; inj.devsta = devsta; inj.error_status = error_status; inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) && err->status == PCI_ERR_UNC_UNSUP; inj.log_overflow = false; if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { if (!pcie_aer_inject_cor_error(&inj, 0, false)) { return 0; } } else { bool is_fatal = pcie_aer_uncor_default_severity(error_status) == PCI_ERR_ROOT_CMD_FATAL_EN; if (aer_cap) { is_fatal = error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER); } if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) { inj.error_status = PCI_ERR_COR_ADV_NONFATAL; if (!pcie_aer_inject_cor_error(&inj, error_status, true)) { return 0; } } else { if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) { return 0; } } } /* send up error message */ inj.msg.source_id = err->source_id; pcie_aer_msg(dev, &inj.msg); if (inj.log_overflow) { PCIEAERErr header_log_overflow = { .status = PCI_ERR_COR_HL_OVERFLOW, .flags = PCIE_AER_ERR_IS_CORRECTABLE, }; int ret = pcie_aer_inject_error(dev, &header_log_overflow); assert(!ret); }