// SPDX-License-Identifier: GPL-2.0 /* * Low-level direct PCI config space access via ECAM - common code between * i386 and x86-64. * * This code does: * - known chipset handling * - ACPI decoding and validation * * Per-architecture code takes care of the mappings and accesses * themselves.
*/
/* Indicate if the ECAM resources have been placed into the resource table */ staticbool pci_mmcfg_running_state; staticbool pci_mmcfg_arch_init_failed; static DEFINE_MUTEX(pci_mmcfg_lock); #define pci_mmcfg_lock_held() lock_is_held(&(pci_mmcfg_lock).dep_map)
/* * Don't try to do this check unless configuration type 1 * is available. How about type 2?
*/
/* * 946f2ee5c731 ("Check that MCFG points to an e820 * reserved area") added this E820 check in 2006 to work * around BIOS defects. * * Per PCI Firmware r3.3, sec 4.1.2, ECAM space must be * reserved by a PNP0C02 resource, but it need not be * mentioned in E820. Before the ACPI interpreter is * available, we can't check for PNP0C02 resources, so * there's no reliable way to verify the region in this * early check. Keep it only for the old machines that * motivated 946f2ee5c731.
*/ if (dmi_get_bios_year() < 2016 && raw_pci_ops) return is_mmconf_reserved(e820__mapped_all, cfg, dev, "E820 entry");
returntrue;
}
if (!acpi_disabled) { if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, "ACPI motherboard resource")) returntrue;
if (dev)
dev_info(dev, FW_INFO "ECAM %pR not reserved in ACPI motherboard resources\n",
&cfg->res); else
pr_info(FW_INFO "ECAM %pR not reserved in ACPI motherboard resources\n",
&cfg->res);
if (is_efi_mmio(&cfg->res)) {
pr_info("ECAM %pR is EfiMemoryMappedIO; assuming valid\n",
&cfg->res);
conflict = insert_resource_conflict(&iomem_resource,
&cfg->res); if (conflict)
pr_warn("ECAM %pR conflicts with %s %pR\n",
&cfg->res, conflict->name, conflict); else
pr_info("ECAM %pR reserved to work around lack of ACPI motherboard _CRS\n",
&cfg->res); returntrue;
}
}
/* * e820__mapped_all() is marked as __init. * All entries from ACPI MCFG table have been checked at boot time. * For MCFG information constructed from hotpluggable host bridge's * _CBA method, just assume it's reserved.
*/ return pci_mmcfg_running_state;
}
/* how many config structures do we have */
free_all_mmcfg();
entries = 0;
i = header->length - sizeof(struct acpi_table_mcfg); while (i >= sizeof(struct acpi_mcfg_allocation)) {
entries++;
i -= sizeof(struct acpi_mcfg_allocation);
} if (entries == 0) {
pr_err("MCFG has no entries\n"); return -ENODEV;
}
cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; for (i = 0; i < entries; i++) {
cfg = &cfg_table[i]; if (!acpi_mcfg_valid_entry(mcfg, cfg)) {
free_all_mmcfg(); return -ENODEV;
}
if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
cfg->end_bus_number, cfg->address) == NULL) {
pr_warn("no memory for MCFG entries\n");
free_all_mmcfg(); return -ENOMEM;
}
}
/* If we are not using ECAM, don't insert the resources. */ if ((pci_probe & PCI_PROBE_MMCONF) == 0) return 1;
/* * Attempt to insert the mmcfg resources but not with the busy flag * marked so it won't cause request errors when __request_region is * called.
*/
list_for_each_entry(cfg, &pci_mmcfg_list, list) { if (!cfg->res.parent) {
pr_debug("%s() insert %pR\n", __func__, &cfg->res);
insert_resource(&iomem_resource, &cfg->res);
}
}
return 0;
}
/* * Perform ECAM resource insertion after PCI initialization to allow for * misprogrammed MCFG tables that state larger sizes but actually conflict * with other system resources.
*/
late_initcall(pci_mmcfg_late_insert_resources);
/* Add ECAM information for host bridges */ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
phys_addr_t addr)
{ int rc; struct resource *tmp = NULL; struct pci_mmcfg_region *cfg;
if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) return -ENODEV;
if (start > end) return -EINVAL;
mutex_lock(&pci_mmcfg_lock);
cfg = pci_mmconfig_lookup(seg, start); if (cfg) { if (cfg->end_bus < end)
dev_info(dev, FW_INFO "ECAM %pR for domain %04x [bus %02x-%02x] only partially covers this bridge\n",
&cfg->res, cfg->segment, cfg->start_bus,
cfg->end_bus);
mutex_unlock(&pci_mmcfg_lock); return -EEXIST;
}
/* * Don't move earlier; we must return -EEXIST, not -EINVAL, if * pci_mmconfig_lookup() finds something
*/ if (!addr) {
mutex_unlock(&pci_mmcfg_lock); return -EINVAL;
}
rc = -EBUSY;
cfg = pci_mmconfig_alloc(seg, start, end, addr); if (cfg == NULL) {
dev_warn(dev, "fail to add ECAM (out of memory)\n");
rc = -ENOMEM;
} elseif (!pci_mmcfg_reserved(dev, cfg, 0)) {
dev_warn(dev, FW_BUG "ECAM %pR isn't reserved\n",
&cfg->res);
} else { /* Insert resource if it's not in boot stage */ if (pci_mmcfg_running_state)
tmp = insert_resource_conflict(&iomem_resource,
&cfg->res);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.