/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include static int pcie_rp_original_idx( const struct pcie_rp_group *const group, const unsigned int offset, const pci_devfn_t dev) { const uint16_t clist = pci_s_find_capability(dev, PCI_CAP_ID_PCIE); if (clist == 0) { printk(BIOS_WARNING, "%s: Can't find PCIe capapilities for PCI: 00:%02x.%x, ignoring.\n", __func__, group->slot, PCI_FUNC(PCI_DEV2DEVFN(dev))); return -1; } const uint16_t xcap = pci_s_read_config16(dev, clist + PCI_EXP_FLAGS); if ((xcap & PCI_EXP_FLAGS_TYPE) >> 4 != PCI_EXP_TYPE_ROOT_PORT) { printk(BIOS_WARNING, "%s: Non root-port found at PCI: 00:%02x.%x, ignoring.\n", __func__, group->slot, PCI_FUNC(PCI_DEV2DEVFN(dev))); return -1; } const uint32_t lcap = pci_s_read_config32(dev, clist + PCI_EXP_LNKCAP); /* Read n-based absolute port number from LCAP register. This reflects the numbering scheme that Intel uses in their documentation and what we use as index (0-based, though) in our mapping. */ const unsigned int port_num = (lcap & PCI_EXP_LNKCAP_PORT) >> 24; /* Subtract lcap_port_base from port_num to get 0-based index */ const unsigned int port_idx = port_num - group->lcap_port_base; /* Check if port_idx (0-based) is out of bounds */ if (port_idx < offset || port_idx >= offset + group->count) { printk(BIOS_WARNING, "%s: Unexpected root-port number '%u'" " at PCI: 00:%02x.%x, ignoring.\n", __func__, port_num, group->slot, PCI_FUNC(PCI_DEV2DEVFN(dev))); return -1; } return port_idx; } /* Scan actual PCI config space to reconstruct current mapping */ static void pcie_rp_scan_groups(int mapping[], const struct pcie_rp_group *const groups) { unsigned int offset = 0; const struct pcie_rp_group *group; for (group = groups; group->count; ++group) { unsigned int fn; for (fn = rp_start_fn(group); fn <= rp_end_fn(group); ++fn) { const pci_devfn_t dev = PCI_DEV(0, group->slot, fn); const uint16_t did = pci_s_read_config16(dev, PCI_DEVICE_ID); if (did == 0xffff) { if (fn == 0) break; continue; } const int rp_idx = pcie_rp_original_idx(group, offset, dev); if (rp_idx < 0) continue; if (mapping[rp_idx] != -1) { printk(BIOS_WARNING, "%s: Root Port #%u reported by PCI: " "00:%02x.%x already reported by PCI: 00:%02x.%x!\n", __func__, rp_idx + 1, group->slot, fn, group->slot, mapping[rp_idx]); continue; } printk(BIOS_INFO, "Found PCIe Root Port #%u at PCI: 00:%02x.%x.\n", rp_idx + 1, group->slot, fn); mapping[rp_idx] = fn; } offset += group->count; } } /* Returns `true` if the device should be unlinked. */ static bool pcie_rp_update_dev( struct device *const dev, const struct pcie_rp_group *const groups, const int mapping[]) { if (dev->path.type != DEVICE_PATH_PCI) return false; /* Find matching group and offset. */ unsigned int offset = 0; const struct pcie_rp_group *group; for (group = groups; group->count; ++group) { if (PCI_SLOT(dev->path.pci.devfn) == group->slot && PCI_FUNC(dev->path.pci.devfn) >= rp_start_fn(group) && PCI_FUNC(dev->path.pci.devfn) <= rp_end_fn(group)) break; offset += group->count; } if (!group->count) return false; /* Now update based on what we know. */ const int rp_idx = offset + PCI_FUNC(dev->path.pci.devfn); const int new_fn = mapping[rp_idx]; if (new_fn < 0) { if (dev->enabled) { printk(BIOS_NOTICE, "%s: Couldn't find PCIe Root Port #%u " "(originally %s) which was enabled in devicetree, removing.\n", __func__, rp_idx + 1, dev_path(dev)); } return true; } else if (PCI_FUNC(dev->path.pci.devfn) != new_fn) { printk(BIOS_INFO, "Remapping PCIe Root Port #%u from %s to new function number %u.\n", rp_idx + 1, dev_path(dev), new_fn); dev->path.pci.devfn = PCI_DEVFN(PCI_SLOT(dev->path.pci.devfn), new_fn); } return false; } void pcie_rp_update_devicetree(const struct pcie_rp_group *const groups) { /* Maps absolute root-port numbers to function numbers. Negative if disabled, new function number otherwise. */ int mapping[CONFIG_MAX_ROOT_PORTS]; unsigned int offset, i; if (!groups || !groups->count) return; struct bus *const root = pci_root_bus(); if (!root) return; offset = 0; const struct pcie_rp_group *group; for (group = groups; group->count; ++group) offset += group->count; if (offset > ARRAY_SIZE(mapping)) { printk(BIOS_ERR, "%s: Error: Group exceeds CONFIG_MAX_ROOT_PORTS.\n", __func__); return; } /* Assume everything we don't encounter later is disabled */ for (i = 0; i < ARRAY_SIZE(mapping); ++i) mapping[i] = -1; pcie_rp_scan_groups(mapping, groups); struct device *dev; struct device **link = &root->children; for (dev = *link; dev; dev = *link) { if (pcie_rp_update_dev(dev, groups, mapping)) { /* Unlink vanished device. */ *link = dev->sibling; dev->sibling = NULL; continue; } link = &dev->sibling; } }