// SPDX-License-Identifier: GPL-2.0 /* * Firmware replacement code. * * Work around broken BIOSes that don't set an aperture, only set the * aperture in the AGP bridge, or set too small aperture. * * If all fails map the aperture over some low memory. This is cheaper than * doing bounce buffering. The memory is lost. This is done at early boot * because only the bootmem allocator can allocate 32+MB. * * Copyright 2002 Andi Kleen, SuSE Labs.
*/ #define pr_fmt(fmt) "AGP: " fmt
/* * Using 512M as goal, in case kexec will load kernel_big * that will do the on-position decompress, and could overlap with * the gart aperture that is used. * Sequence: * kernel_small * ==> kexec (with kdump trigger path or gart still enabled) * ==> kernel_small (gart area become e820_reserved) * ==> kexec (with kdump trigger path or gart still enabled) * ==> kerne_big (uncompressed size will be big than 64M or 128M) * So don't use 512M below as gart iommu, leave the space for kernel * code for safe.
*/ #define GART_MIN_ADDR (512ULL << 20) #define GART_MAX_ADDR (1ULL << 32)
int gart_iommu_aperture; int gart_iommu_aperture_disabled __initdata; int gart_iommu_aperture_allowed __initdata;
int fallback_aper_order __initdata = 1; /* 64MB */ int fallback_aper_force __initdata;
int fix_aperture __initdata = 1;
#ifdefined(CONFIG_PROC_VMCORE) || defined(CONFIG_PROC_KCORE) /* * If the first kernel maps the aperture over e820 RAM, the kdump kernel will * use the same range because it will remain configured in the northbridge. * Trying to dump this area via /proc/vmcore may crash the machine, so exclude * it from vmcore.
*/ staticunsignedlong aperture_pfn_start, aperture_page_count;
/* * Aperture has to be naturally aligned. This means a 2GB aperture * won't have much chance of finding a place in the lower 4GB of * memory. Unfortunately we cannot move it up because that would * make the IOMMU useless.
*/
addr = memblock_phys_alloc_range(aper_size, aper_size,
GART_MIN_ADDR, GART_MAX_ADDR); if (!addr) {
pr_err("Cannot allocate aperture memory hole [mem %#010lx-%#010lx] (%uKB)\n",
addr, addr + aper_size - 1, aper_size >> 10); return 0;
}
pr_info("Mapping aperture over RAM [mem %#010lx-%#010lx] (%uKB)\n",
addr, addr + aper_size - 1, aper_size >> 10);
register_nosave_region(addr >> PAGE_SHIFT,
(addr+aper_size) >> PAGE_SHIFT);
return (u32)addr;
}
/* Find a PCI capability */ static u32 __init find_cap(int bus, int slot, int func, int cap)
{ int bytes;
u8 pos;
if (!(read_pci_config_16(bus, slot, func, PCI_STATUS) &
PCI_STATUS_CAP_LIST)) return 0;
/* * Look for an AGP bridge. Windows only expects the aperture in the * AGP bridge and some BIOS forget to initialize the Northbridge too. * Work around this here. * * Do an PCI bus scan by hand because we're running before the PCI * subsystem. * * All AMD AGP bridges are AGPv3 compliant, so we can do this scan * generically. It's probably overkill to always scan all slots because * the AGP bridges should be always an own bus on the HT hierarchy, * but do it here for future safety.
*/ static u32 __init search_agp_bridge(u32 *order, int *valid_agp)
{ int bus, slot, func;
/* Poor man's PCI discovery */ for (bus = 0; bus < 256; bus++) { for (slot = 0; slot < 32; slot++) { for (func = 0; func < 8; func++) {
u32 class, cap;
u8 type; class = read_pci_config(bus, slot, func,
PCI_CLASS_REVISION); if (class == 0xffffffff) break;
switch (class >> 16) { case PCI_CLASS_BRIDGE_HOST: case PCI_CLASS_BRIDGE_OTHER: /* needed? */ /* AGP bridge? */
cap = find_cap(bus, slot, func,
PCI_CAP_ID_AGP); if (!cap) break;
*valid_agp = 1; return read_agp(bus, slot, func, cap,
order);
}
type = read_pci_config_byte(bus, slot, func,
PCI_HEADER_TYPE); if (!(type & PCI_HEADER_TYPE_MFD)) break;
}
}
}
pr_info("No AGP bridge found\n");
/* * With kexec/kdump, if the first kernel doesn't shut down the GART and the * second kernel allocates a different GART region, there might be two * overlapping GART regions present: * * - the first still used by the GART initialized in the first kernel. * - (sub-)set of it used as normal RAM by the second kernel. * * which leads to memory corruptions and a kernel panic eventually. * * This can also happen if the BIOS has forgotten to mark the GART region * as reserved. * * Try to update the e820 map to mark that new region as reserved.
*/ void __init early_gart_iommu_check(void)
{
u32 agp_aper_order = 0; int i, fix, slot, valid_agp = 0;
u32 ctl;
u32 aper_size = 0, aper_order = 0, last_aper_order = 0;
u64 aper_base = 0, last_aper_base = 0; int aper_enabled = 0, last_aper_enabled = 0, last_valid = 0;
if (!amd_gart_present()) return;
if (!early_pci_allowed()) return;
/* This is mostly duplicate of iommu_hole_init */
search_agp_bridge(&agp_aper_order, &valid_agp);
fix = 0; for (i = 0; amd_nb_bus_dev_ranges[i].dev_limit; i++) { int bus; int dev_base, dev_limit;
bus = amd_nb_bus_dev_ranges[i].bus;
dev_base = amd_nb_bus_dev_ranges[i].dev_base;
dev_limit = amd_nb_bus_dev_ranges[i].dev_limit;
for (slot = dev_base; slot < dev_limit; slot++) { if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00))) continue;
if (gart_fix_e820 && !fix && aper_enabled) { if (e820__mapped_any(aper_base, aper_base + aper_size,
E820_TYPE_RAM)) { /* reserve it, so we can reuse it in second kernel */
pr_info("e820: reserve [mem %#010Lx-%#010Lx] for GART\n",
aper_base, aper_base + aper_size - 1);
e820__range_add(aper_base, aper_size, E820_TYPE_RESERVED);
e820__update_table_print();
}
}
if (valid_agp) return;
/* disable them all at first */ for (i = 0; i < amd_nb_bus_dev_ranges[i].dev_limit; i++) { int bus; int dev_base, dev_limit;
bus = amd_nb_bus_dev_ranges[i].bus;
dev_base = amd_nb_bus_dev_ranges[i].dev_base;
dev_limit = amd_nb_bus_dev_ranges[i].dev_limit;
for (slot = dev_base; slot < dev_limit; slot++) { if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00))) continue;
/* * Before we do anything else disable the GART. It may * still be enabled if we boot into a crash-kernel here. * Reconfiguring the GART while it is enabled could have * unknown side-effects.
*/
ctl &= ~GARTEN;
write_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL, ctl);
if (!aperture_valid(aper_base, aper_size, 64<<20)) { if (valid_agp && agp_aper_base &&
agp_aper_base == aper_base &&
agp_aper_order == aper_order) { /* the same between two setting from NB and agp */ if (!no_iommu &&
max_pfn > MAX_DMA32_PFN &&
!printed_gart_size_msg) {
pr_err("you are using iommu with agp, but GART size is less than 64MB\n");
pr_err("please increase GART size in your BIOS setup\n");
pr_err("if BIOS doesn't have that option, contact your HW vendor!\n");
printed_gart_size_msg = 1;
}
} else {
fix = 1; goto out;
}
}
out: if (!fix && !fallback_aper_force) { if (last_aper_base) { /* * If this is the kdump kernel, the first kernel * may have allocated the range over its e820 RAM * and fixed up the northbridge
*/
exclude_from_core(last_aper_base, last_aper_order);
} return;
}
if (!fallback_aper_force) {
aper_alloc = agp_aper_base;
aper_order = agp_aper_order;
}
if (aper_alloc) { /* Got the aperture from the AGP bridge */
} elseif ((!no_iommu && max_pfn > MAX_DMA32_PFN) ||
force_iommu ||
valid_agp ||
fallback_aper_force) {
pr_info("Your BIOS doesn't leave an aperture memory hole\n");
pr_info("Please enable the IOMMU option in the BIOS setup\n");
pr_info("This costs you %dMB of RAM\n",
32 << fallback_aper_order);
aper_order = fallback_aper_order;
aper_alloc = allocate_aperture(); if (!aper_alloc) { /* * Could disable AGP and IOMMU here, but it's * probably not worth it. But the later users * cannot deal with bad apertures and turning * on the aperture over memory causes very * strange problems, so it's better to panic * early.
*/
panic("Not enough memory for aperture");
}
} else { return;
}
/* * If this is the kdump kernel _and_ the first kernel did not * configure the aperture in the northbridge, this range may * overlap with the first kernel's memory. We can't access the * range through vmcore even though it should be part of the dump.
*/
exclude_from_core(aper_alloc, aper_order);
/* Fix up the north bridges */ for (i = 0; i < amd_nb_bus_dev_ranges[i].dev_limit; i++) { int bus, dev_base, dev_limit;
/* * Don't enable translation yet but enable GART IO and CPU * accesses and set DISTLBWALKPRB since GART table memory is UC.
*/
u32 ctl = aper_order << 1;
bus = amd_nb_bus_dev_ranges[i].bus;
dev_base = amd_nb_bus_dev_ranges[i].dev_base;
dev_limit = amd_nb_bus_dev_ranges[i].dev_limit; for (slot = dev_base; slot < dev_limit; slot++) { if (!early_is_amd_nb(read_pci_config(bus, slot, 3, 0x00))) continue;
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.