162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Low level x86 E820 memory map handling functions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The firmware and bootloader passes us the "E820 table", which is the primary 662306a36Sopenharmony_ci * physical memory layout description available about x86 systems. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * The kernel takes the E820 memory layout and optionally modifies it with 962306a36Sopenharmony_ci * quirks and other tweaks, and feeds that into the generic Linux memory 1062306a36Sopenharmony_ci * allocation code routines via a platform independent interface (memblock, etc.). 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <linux/crash_dump.h> 1362306a36Sopenharmony_ci#include <linux/memblock.h> 1462306a36Sopenharmony_ci#include <linux/suspend.h> 1562306a36Sopenharmony_ci#include <linux/acpi.h> 1662306a36Sopenharmony_ci#include <linux/firmware-map.h> 1762306a36Sopenharmony_ci#include <linux/sort.h> 1862306a36Sopenharmony_ci#include <linux/memory_hotplug.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/e820/api.h> 2162306a36Sopenharmony_ci#include <asm/setup.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * We organize the E820 table into three main data structures: 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * - 'e820_table_firmware': the original firmware version passed to us by the 2762306a36Sopenharmony_ci * bootloader - not modified by the kernel. It is composed of two parts: 2862306a36Sopenharmony_ci * the first 128 E820 memory entries in boot_params.e820_table and the remaining 2962306a36Sopenharmony_ci * (if any) entries of the SETUP_E820_EXT nodes. We use this to: 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * - inform the user about the firmware's notion of memory layout 3262306a36Sopenharmony_ci * via /sys/firmware/memmap 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * - the hibernation code uses it to generate a kernel-independent CRC32 3562306a36Sopenharmony_ci * checksum of the physical memory layout of a system. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * - 'e820_table_kexec': a slightly modified (by the kernel) firmware version 3862306a36Sopenharmony_ci * passed to us by the bootloader - the major difference between 3962306a36Sopenharmony_ci * e820_table_firmware[] and this one is that, the latter marks the setup_data 4062306a36Sopenharmony_ci * list created by the EFI boot stub as reserved, so that kexec can reuse the 4162306a36Sopenharmony_ci * setup_data information in the second kernel. Besides, e820_table_kexec[] 4262306a36Sopenharmony_ci * might also be modified by the kexec itself to fake a mptable. 4362306a36Sopenharmony_ci * We use this to: 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * - kexec, which is a bootloader in disguise, uses the original E820 4662306a36Sopenharmony_ci * layout to pass to the kexec-ed kernel. This way the original kernel 4762306a36Sopenharmony_ci * can have a restricted E820 map while the kexec()-ed kexec-kernel 4862306a36Sopenharmony_ci * can have access to full memory - etc. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * - 'e820_table': this is the main E820 table that is massaged by the 5162306a36Sopenharmony_ci * low level x86 platform code, or modified by boot parameters, before 5262306a36Sopenharmony_ci * passed on to higher level MM layers. 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * Once the E820 map has been converted to the standard Linux memory layout 5562306a36Sopenharmony_ci * information its role stops - modifying it has no effect and does not get 5662306a36Sopenharmony_ci * re-propagated. So its main role is a temporary bootstrap storage of firmware 5762306a36Sopenharmony_ci * specific memory layout data during early bootup. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic struct e820_table e820_table_init __initdata; 6062306a36Sopenharmony_cistatic struct e820_table e820_table_kexec_init __initdata; 6162306a36Sopenharmony_cistatic struct e820_table e820_table_firmware_init __initdata; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct e820_table *e820_table __refdata = &e820_table_init; 6462306a36Sopenharmony_cistruct e820_table *e820_table_kexec __refdata = &e820_table_kexec_init; 6562306a36Sopenharmony_cistruct e820_table *e820_table_firmware __refdata = &e820_table_firmware_init; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* For PCI or other memory-mapped resources */ 6862306a36Sopenharmony_ciunsigned long pci_mem_start = 0xaeedbabe; 6962306a36Sopenharmony_ci#ifdef CONFIG_PCI 7062306a36Sopenharmony_ciEXPORT_SYMBOL(pci_mem_start); 7162306a36Sopenharmony_ci#endif 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * This function checks if any part of the range <start,end> is mapped 7562306a36Sopenharmony_ci * with type. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic bool _e820__mapped_any(struct e820_table *table, 7862306a36Sopenharmony_ci u64 start, u64 end, enum e820_type type) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int i; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (i = 0; i < table->nr_entries; i++) { 8362306a36Sopenharmony_ci struct e820_entry *entry = &table->entries[i]; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (type && entry->type != type) 8662306a36Sopenharmony_ci continue; 8762306a36Sopenharmony_ci if (entry->addr >= end || entry->addr + entry->size <= start) 8862306a36Sopenharmony_ci continue; 8962306a36Sopenharmony_ci return true; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci return false; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cibool e820__mapped_raw_any(u64 start, u64 end, enum e820_type type) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci return _e820__mapped_any(e820_table_firmware, start, end, type); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(e820__mapped_raw_any); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cibool e820__mapped_any(u64 start, u64 end, enum e820_type type) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci return _e820__mapped_any(e820_table, start, end, type); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(e820__mapped_any); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * This function checks if the entire <start,end> range is mapped with 'type'. 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * Note: this function only works correctly once the E820 table is sorted and 11062306a36Sopenharmony_ci * not-overlapping (at least for the range specified), which is the case normally. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cistatic struct e820_entry *__e820__mapped_all(u64 start, u64 end, 11362306a36Sopenharmony_ci enum e820_type type) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int i; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 11862306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (type && entry->type != type) 12162306a36Sopenharmony_ci continue; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Is the region (part) in overlap with the current region? */ 12462306a36Sopenharmony_ci if (entry->addr >= end || entry->addr + entry->size <= start) 12562306a36Sopenharmony_ci continue; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * If the region is at the beginning of <start,end> we move 12962306a36Sopenharmony_ci * 'start' to the end of the region since it's ok until there 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (entry->addr <= start) 13262306a36Sopenharmony_ci start = entry->addr + entry->size; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * If 'start' is now at or beyond 'end', we're done, full 13662306a36Sopenharmony_ci * coverage of the desired range exists: 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci if (start >= end) 13962306a36Sopenharmony_ci return entry; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return NULL; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * This function checks if the entire range <start,end> is mapped with type. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cibool __init e820__mapped_all(u64 start, u64 end, enum e820_type type) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci return __e820__mapped_all(start, end, type); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * This function returns the type associated with the range <start,end>. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ciint e820__get_entry_type(u64 start, u64 end) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct e820_entry *entry = __e820__mapped_all(start, end, 0); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return entry ? entry->type : -EINVAL; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Add a memory region to the kernel E820 map. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic void __init __e820__range_add(struct e820_table *table, u64 start, u64 size, enum e820_type type) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci int x = table->nr_entries; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (x >= ARRAY_SIZE(table->entries)) { 17162306a36Sopenharmony_ci pr_err("too many entries; ignoring [mem %#010llx-%#010llx]\n", 17262306a36Sopenharmony_ci start, start + size - 1); 17362306a36Sopenharmony_ci return; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci table->entries[x].addr = start; 17762306a36Sopenharmony_ci table->entries[x].size = size; 17862306a36Sopenharmony_ci table->entries[x].type = type; 17962306a36Sopenharmony_ci table->nr_entries++; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_civoid __init e820__range_add(u64 start, u64 size, enum e820_type type) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci __e820__range_add(e820_table, start, size, type); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void __init e820_print_type(enum e820_type type) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci switch (type) { 19062306a36Sopenharmony_ci case E820_TYPE_RAM: /* Fall through: */ 19162306a36Sopenharmony_ci case E820_TYPE_RESERVED_KERN: pr_cont("usable"); break; 19262306a36Sopenharmony_ci case E820_TYPE_RESERVED: pr_cont("reserved"); break; 19362306a36Sopenharmony_ci case E820_TYPE_SOFT_RESERVED: pr_cont("soft reserved"); break; 19462306a36Sopenharmony_ci case E820_TYPE_ACPI: pr_cont("ACPI data"); break; 19562306a36Sopenharmony_ci case E820_TYPE_NVS: pr_cont("ACPI NVS"); break; 19662306a36Sopenharmony_ci case E820_TYPE_UNUSABLE: pr_cont("unusable"); break; 19762306a36Sopenharmony_ci case E820_TYPE_PMEM: /* Fall through: */ 19862306a36Sopenharmony_ci case E820_TYPE_PRAM: pr_cont("persistent (type %u)", type); break; 19962306a36Sopenharmony_ci default: pr_cont("type %u", type); break; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_civoid __init e820__print_table(char *who) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int i; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 20862306a36Sopenharmony_ci pr_info("%s: [mem %#018Lx-%#018Lx] ", 20962306a36Sopenharmony_ci who, 21062306a36Sopenharmony_ci e820_table->entries[i].addr, 21162306a36Sopenharmony_ci e820_table->entries[i].addr + e820_table->entries[i].size - 1); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci e820_print_type(e820_table->entries[i].type); 21462306a36Sopenharmony_ci pr_cont("\n"); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * Sanitize an E820 map. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * Some E820 layouts include overlapping entries. The following 22262306a36Sopenharmony_ci * replaces the original E820 map with a new one, removing overlaps, 22362306a36Sopenharmony_ci * and resolving conflicting memory types in favor of highest 22462306a36Sopenharmony_ci * numbered type. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * The input parameter 'entries' points to an array of 'struct 22762306a36Sopenharmony_ci * e820_entry' which on entry has elements in the range [0, *nr_entries) 22862306a36Sopenharmony_ci * valid, and which has space for up to max_nr_entries entries. 22962306a36Sopenharmony_ci * On return, the resulting sanitized E820 map entries will be in 23062306a36Sopenharmony_ci * overwritten in the same location, starting at 'entries'. 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * The integer pointed to by nr_entries must be valid on entry (the 23362306a36Sopenharmony_ci * current number of valid entries located at 'entries'). If the 23462306a36Sopenharmony_ci * sanitizing succeeds the *nr_entries will be updated with the new 23562306a36Sopenharmony_ci * number of valid entries (something no more than max_nr_entries). 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * The return value from e820__update_table() is zero if it 23862306a36Sopenharmony_ci * successfully 'sanitized' the map entries passed in, and is -1 23962306a36Sopenharmony_ci * if it did nothing, which can happen if either of (1) it was 24062306a36Sopenharmony_ci * only passed one map entry, or (2) any of the input map entries 24162306a36Sopenharmony_ci * were invalid (start + size < start, meaning that the size was 24262306a36Sopenharmony_ci * so big the described memory range wrapped around through zero.) 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * Visually we're performing the following 24562306a36Sopenharmony_ci * (1,2,3,4 = memory types)... 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Sample memory map (w/overlaps): 24862306a36Sopenharmony_ci * ____22__________________ 24962306a36Sopenharmony_ci * ______________________4_ 25062306a36Sopenharmony_ci * ____1111________________ 25162306a36Sopenharmony_ci * _44_____________________ 25262306a36Sopenharmony_ci * 11111111________________ 25362306a36Sopenharmony_ci * ____________________33__ 25462306a36Sopenharmony_ci * ___________44___________ 25562306a36Sopenharmony_ci * __________33333_________ 25662306a36Sopenharmony_ci * ______________22________ 25762306a36Sopenharmony_ci * ___________________2222_ 25862306a36Sopenharmony_ci * _________111111111______ 25962306a36Sopenharmony_ci * _____________________11_ 26062306a36Sopenharmony_ci * _________________4______ 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * Sanitized equivalent (no overlap): 26362306a36Sopenharmony_ci * 1_______________________ 26462306a36Sopenharmony_ci * _44_____________________ 26562306a36Sopenharmony_ci * ___1____________________ 26662306a36Sopenharmony_ci * ____22__________________ 26762306a36Sopenharmony_ci * ______11________________ 26862306a36Sopenharmony_ci * _________1______________ 26962306a36Sopenharmony_ci * __________3_____________ 27062306a36Sopenharmony_ci * ___________44___________ 27162306a36Sopenharmony_ci * _____________33_________ 27262306a36Sopenharmony_ci * _______________2________ 27362306a36Sopenharmony_ci * ________________1_______ 27462306a36Sopenharmony_ci * _________________4______ 27562306a36Sopenharmony_ci * ___________________2____ 27662306a36Sopenharmony_ci * ____________________33__ 27762306a36Sopenharmony_ci * ______________________4_ 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistruct change_member { 28062306a36Sopenharmony_ci /* Pointer to the original entry: */ 28162306a36Sopenharmony_ci struct e820_entry *entry; 28262306a36Sopenharmony_ci /* Address for this change point: */ 28362306a36Sopenharmony_ci unsigned long long addr; 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic struct change_member change_point_list[2*E820_MAX_ENTRIES] __initdata; 28762306a36Sopenharmony_cistatic struct change_member *change_point[2*E820_MAX_ENTRIES] __initdata; 28862306a36Sopenharmony_cistatic struct e820_entry *overlap_list[E820_MAX_ENTRIES] __initdata; 28962306a36Sopenharmony_cistatic struct e820_entry new_entries[E820_MAX_ENTRIES] __initdata; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int __init cpcompare(const void *a, const void *b) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct change_member * const *app = a, * const *bpp = b; 29462306a36Sopenharmony_ci const struct change_member *ap = *app, *bp = *bpp; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * Inputs are pointers to two elements of change_point[]. If their 29862306a36Sopenharmony_ci * addresses are not equal, their difference dominates. If the addresses 29962306a36Sopenharmony_ci * are equal, then consider one that represents the end of its region 30062306a36Sopenharmony_ci * to be greater than one that does not. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (ap->addr != bp->addr) 30362306a36Sopenharmony_ci return ap->addr > bp->addr ? 1 : -1; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return (ap->addr != ap->entry->addr) - (bp->addr != bp->entry->addr); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic bool e820_nomerge(enum e820_type type) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * These types may indicate distinct platform ranges aligned to 31262306a36Sopenharmony_ci * numa node, protection domain, performance domain, or other 31362306a36Sopenharmony_ci * boundaries. Do not merge them. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (type == E820_TYPE_PRAM) 31662306a36Sopenharmony_ci return true; 31762306a36Sopenharmony_ci if (type == E820_TYPE_SOFT_RESERVED) 31862306a36Sopenharmony_ci return true; 31962306a36Sopenharmony_ci return false; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ciint __init e820__update_table(struct e820_table *table) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct e820_entry *entries = table->entries; 32562306a36Sopenharmony_ci u32 max_nr_entries = ARRAY_SIZE(table->entries); 32662306a36Sopenharmony_ci enum e820_type current_type, last_type; 32762306a36Sopenharmony_ci unsigned long long last_addr; 32862306a36Sopenharmony_ci u32 new_nr_entries, overlap_entries; 32962306a36Sopenharmony_ci u32 i, chg_idx, chg_nr; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* If there's only one memory region, don't bother: */ 33262306a36Sopenharmony_ci if (table->nr_entries < 2) 33362306a36Sopenharmony_ci return -1; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci BUG_ON(table->nr_entries > max_nr_entries); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Bail out if we find any unreasonable addresses in the map: */ 33862306a36Sopenharmony_ci for (i = 0; i < table->nr_entries; i++) { 33962306a36Sopenharmony_ci if (entries[i].addr + entries[i].size < entries[i].addr) 34062306a36Sopenharmony_ci return -1; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Create pointers for initial change-point information (for sorting): */ 34462306a36Sopenharmony_ci for (i = 0; i < 2 * table->nr_entries; i++) 34562306a36Sopenharmony_ci change_point[i] = &change_point_list[i]; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * Record all known change-points (starting and ending addresses), 34962306a36Sopenharmony_ci * omitting empty memory regions: 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci chg_idx = 0; 35262306a36Sopenharmony_ci for (i = 0; i < table->nr_entries; i++) { 35362306a36Sopenharmony_ci if (entries[i].size != 0) { 35462306a36Sopenharmony_ci change_point[chg_idx]->addr = entries[i].addr; 35562306a36Sopenharmony_ci change_point[chg_idx++]->entry = &entries[i]; 35662306a36Sopenharmony_ci change_point[chg_idx]->addr = entries[i].addr + entries[i].size; 35762306a36Sopenharmony_ci change_point[chg_idx++]->entry = &entries[i]; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci chg_nr = chg_idx; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Sort change-point list by memory addresses (low -> high): */ 36362306a36Sopenharmony_ci sort(change_point, chg_nr, sizeof(*change_point), cpcompare, NULL); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Create a new memory map, removing overlaps: */ 36662306a36Sopenharmony_ci overlap_entries = 0; /* Number of entries in the overlap table */ 36762306a36Sopenharmony_ci new_nr_entries = 0; /* Index for creating new map entries */ 36862306a36Sopenharmony_ci last_type = 0; /* Start with undefined memory type */ 36962306a36Sopenharmony_ci last_addr = 0; /* Start with 0 as last starting address */ 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Loop through change-points, determining effect on the new map: */ 37262306a36Sopenharmony_ci for (chg_idx = 0; chg_idx < chg_nr; chg_idx++) { 37362306a36Sopenharmony_ci /* Keep track of all overlapping entries */ 37462306a36Sopenharmony_ci if (change_point[chg_idx]->addr == change_point[chg_idx]->entry->addr) { 37562306a36Sopenharmony_ci /* Add map entry to overlap list (> 1 entry implies an overlap) */ 37662306a36Sopenharmony_ci overlap_list[overlap_entries++] = change_point[chg_idx]->entry; 37762306a36Sopenharmony_ci } else { 37862306a36Sopenharmony_ci /* Remove entry from list (order independent, so swap with last): */ 37962306a36Sopenharmony_ci for (i = 0; i < overlap_entries; i++) { 38062306a36Sopenharmony_ci if (overlap_list[i] == change_point[chg_idx]->entry) 38162306a36Sopenharmony_ci overlap_list[i] = overlap_list[overlap_entries-1]; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci overlap_entries--; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * If there are overlapping entries, decide which 38762306a36Sopenharmony_ci * "type" to use (larger value takes precedence -- 38862306a36Sopenharmony_ci * 1=usable, 2,3,4,4+=unusable) 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci current_type = 0; 39162306a36Sopenharmony_ci for (i = 0; i < overlap_entries; i++) { 39262306a36Sopenharmony_ci if (overlap_list[i]->type > current_type) 39362306a36Sopenharmony_ci current_type = overlap_list[i]->type; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Continue building up new map based on this information: */ 39762306a36Sopenharmony_ci if (current_type != last_type || e820_nomerge(current_type)) { 39862306a36Sopenharmony_ci if (last_type) { 39962306a36Sopenharmony_ci new_entries[new_nr_entries].size = change_point[chg_idx]->addr - last_addr; 40062306a36Sopenharmony_ci /* Move forward only if the new size was non-zero: */ 40162306a36Sopenharmony_ci if (new_entries[new_nr_entries].size != 0) 40262306a36Sopenharmony_ci /* No more space left for new entries? */ 40362306a36Sopenharmony_ci if (++new_nr_entries >= max_nr_entries) 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci if (current_type) { 40762306a36Sopenharmony_ci new_entries[new_nr_entries].addr = change_point[chg_idx]->addr; 40862306a36Sopenharmony_ci new_entries[new_nr_entries].type = current_type; 40962306a36Sopenharmony_ci last_addr = change_point[chg_idx]->addr; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci last_type = current_type; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Copy the new entries into the original location: */ 41662306a36Sopenharmony_ci memcpy(entries, new_entries, new_nr_entries*sizeof(*entries)); 41762306a36Sopenharmony_ci table->nr_entries = new_nr_entries; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int __init __append_e820_table(struct boot_e820_entry *entries, u32 nr_entries) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct boot_e820_entry *entry = entries; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci while (nr_entries) { 42762306a36Sopenharmony_ci u64 start = entry->addr; 42862306a36Sopenharmony_ci u64 size = entry->size; 42962306a36Sopenharmony_ci u64 end = start + size - 1; 43062306a36Sopenharmony_ci u32 type = entry->type; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Ignore the entry on 64-bit overflow: */ 43362306a36Sopenharmony_ci if (start > end && likely(size)) 43462306a36Sopenharmony_ci return -1; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci e820__range_add(start, size, type); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci entry++; 43962306a36Sopenharmony_ci nr_entries--; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* 44562306a36Sopenharmony_ci * Copy the BIOS E820 map into a safe place. 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Sanity-check it while we're at it.. 44862306a36Sopenharmony_ci * 44962306a36Sopenharmony_ci * If we're lucky and live on a modern system, the setup code 45062306a36Sopenharmony_ci * will have given us a memory map that we can use to properly 45162306a36Sopenharmony_ci * set up memory. If we aren't, we'll fake a memory map. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_cistatic int __init append_e820_table(struct boot_e820_entry *entries, u32 nr_entries) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci /* Only one memory region (or negative)? Ignore it */ 45662306a36Sopenharmony_ci if (nr_entries < 2) 45762306a36Sopenharmony_ci return -1; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return __append_e820_table(entries, nr_entries); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic u64 __init 46362306a36Sopenharmony_ci__e820__range_update(struct e820_table *table, u64 start, u64 size, enum e820_type old_type, enum e820_type new_type) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci u64 end; 46662306a36Sopenharmony_ci unsigned int i; 46762306a36Sopenharmony_ci u64 real_updated_size = 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci BUG_ON(old_type == new_type); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (size > (ULLONG_MAX - start)) 47262306a36Sopenharmony_ci size = ULLONG_MAX - start; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci end = start + size; 47562306a36Sopenharmony_ci printk(KERN_DEBUG "e820: update [mem %#010Lx-%#010Lx] ", start, end - 1); 47662306a36Sopenharmony_ci e820_print_type(old_type); 47762306a36Sopenharmony_ci pr_cont(" ==> "); 47862306a36Sopenharmony_ci e820_print_type(new_type); 47962306a36Sopenharmony_ci pr_cont("\n"); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (i = 0; i < table->nr_entries; i++) { 48262306a36Sopenharmony_ci struct e820_entry *entry = &table->entries[i]; 48362306a36Sopenharmony_ci u64 final_start, final_end; 48462306a36Sopenharmony_ci u64 entry_end; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (entry->type != old_type) 48762306a36Sopenharmony_ci continue; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci entry_end = entry->addr + entry->size; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* Completely covered by new range? */ 49262306a36Sopenharmony_ci if (entry->addr >= start && entry_end <= end) { 49362306a36Sopenharmony_ci entry->type = new_type; 49462306a36Sopenharmony_ci real_updated_size += entry->size; 49562306a36Sopenharmony_ci continue; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* New range is completely covered? */ 49962306a36Sopenharmony_ci if (entry->addr < start && entry_end > end) { 50062306a36Sopenharmony_ci __e820__range_add(table, start, size, new_type); 50162306a36Sopenharmony_ci __e820__range_add(table, end, entry_end - end, entry->type); 50262306a36Sopenharmony_ci entry->size = start - entry->addr; 50362306a36Sopenharmony_ci real_updated_size += size; 50462306a36Sopenharmony_ci continue; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Partially covered: */ 50862306a36Sopenharmony_ci final_start = max(start, entry->addr); 50962306a36Sopenharmony_ci final_end = min(end, entry_end); 51062306a36Sopenharmony_ci if (final_start >= final_end) 51162306a36Sopenharmony_ci continue; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci __e820__range_add(table, final_start, final_end - final_start, new_type); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci real_updated_size += final_end - final_start; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* 51862306a36Sopenharmony_ci * Left range could be head or tail, so need to update 51962306a36Sopenharmony_ci * its size first: 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci entry->size -= final_end - final_start; 52262306a36Sopenharmony_ci if (entry->addr < final_start) 52362306a36Sopenharmony_ci continue; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci entry->addr = final_end; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci return real_updated_size; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ciu64 __init e820__range_update(u64 start, u64 size, enum e820_type old_type, enum e820_type new_type) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci return __e820__range_update(e820_table, start, size, old_type, new_type); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic u64 __init e820__range_update_kexec(u64 start, u64 size, enum e820_type old_type, enum e820_type new_type) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci return __e820__range_update(e820_table_kexec, start, size, old_type, new_type); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* Remove a range of memory from the E820 table: */ 54162306a36Sopenharmony_ciu64 __init e820__range_remove(u64 start, u64 size, enum e820_type old_type, bool check_type) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci int i; 54462306a36Sopenharmony_ci u64 end; 54562306a36Sopenharmony_ci u64 real_removed_size = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (size > (ULLONG_MAX - start)) 54862306a36Sopenharmony_ci size = ULLONG_MAX - start; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci end = start + size; 55162306a36Sopenharmony_ci printk(KERN_DEBUG "e820: remove [mem %#010Lx-%#010Lx] ", start, end - 1); 55262306a36Sopenharmony_ci if (check_type) 55362306a36Sopenharmony_ci e820_print_type(old_type); 55462306a36Sopenharmony_ci pr_cont("\n"); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 55762306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 55862306a36Sopenharmony_ci u64 final_start, final_end; 55962306a36Sopenharmony_ci u64 entry_end; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (check_type && entry->type != old_type) 56262306a36Sopenharmony_ci continue; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci entry_end = entry->addr + entry->size; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Completely covered? */ 56762306a36Sopenharmony_ci if (entry->addr >= start && entry_end <= end) { 56862306a36Sopenharmony_ci real_removed_size += entry->size; 56962306a36Sopenharmony_ci memset(entry, 0, sizeof(*entry)); 57062306a36Sopenharmony_ci continue; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Is the new range completely covered? */ 57462306a36Sopenharmony_ci if (entry->addr < start && entry_end > end) { 57562306a36Sopenharmony_ci e820__range_add(end, entry_end - end, entry->type); 57662306a36Sopenharmony_ci entry->size = start - entry->addr; 57762306a36Sopenharmony_ci real_removed_size += size; 57862306a36Sopenharmony_ci continue; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Partially covered: */ 58262306a36Sopenharmony_ci final_start = max(start, entry->addr); 58362306a36Sopenharmony_ci final_end = min(end, entry_end); 58462306a36Sopenharmony_ci if (final_start >= final_end) 58562306a36Sopenharmony_ci continue; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci real_removed_size += final_end - final_start; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* 59062306a36Sopenharmony_ci * Left range could be head or tail, so need to update 59162306a36Sopenharmony_ci * the size first: 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci entry->size -= final_end - final_start; 59462306a36Sopenharmony_ci if (entry->addr < final_start) 59562306a36Sopenharmony_ci continue; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci entry->addr = final_end; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci return real_removed_size; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_civoid __init e820__update_table_print(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (e820__update_table(e820_table)) 60562306a36Sopenharmony_ci return; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci pr_info("modified physical RAM map:\n"); 60862306a36Sopenharmony_ci e820__print_table("modified"); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic void __init e820__update_table_kexec(void) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci e820__update_table(e820_table_kexec); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci#define MAX_GAP_END 0x100000000ull 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci * Search for a gap in the E820 memory space from 0 to MAX_GAP_END (4GB). 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic int __init e820_search_gap(unsigned long *gapstart, unsigned long *gapsize) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci unsigned long long last = MAX_GAP_END; 62462306a36Sopenharmony_ci int i = e820_table->nr_entries; 62562306a36Sopenharmony_ci int found = 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci while (--i >= 0) { 62862306a36Sopenharmony_ci unsigned long long start = e820_table->entries[i].addr; 62962306a36Sopenharmony_ci unsigned long long end = start + e820_table->entries[i].size; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* 63262306a36Sopenharmony_ci * Since "last" is at most 4GB, we know we'll 63362306a36Sopenharmony_ci * fit in 32 bits if this condition is true: 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci if (last > end) { 63662306a36Sopenharmony_ci unsigned long gap = last - end; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (gap >= *gapsize) { 63962306a36Sopenharmony_ci *gapsize = gap; 64062306a36Sopenharmony_ci *gapstart = end; 64162306a36Sopenharmony_ci found = 1; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci if (start < last) 64562306a36Sopenharmony_ci last = start; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci return found; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* 65162306a36Sopenharmony_ci * Search for the biggest gap in the low 32 bits of the E820 65262306a36Sopenharmony_ci * memory space. We pass this space to the PCI subsystem, so 65362306a36Sopenharmony_ci * that it can assign MMIO resources for hotplug or 65462306a36Sopenharmony_ci * unconfigured devices in. 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * Hopefully the BIOS let enough space left. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci__init void e820__setup_pci_gap(void) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci unsigned long gapstart, gapsize; 66162306a36Sopenharmony_ci int found; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci gapsize = 0x400000; 66462306a36Sopenharmony_ci found = e820_search_gap(&gapstart, &gapsize); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (!found) { 66762306a36Sopenharmony_ci#ifdef CONFIG_X86_64 66862306a36Sopenharmony_ci gapstart = (max_pfn << PAGE_SHIFT) + 1024*1024; 66962306a36Sopenharmony_ci pr_err("Cannot find an available gap in the 32-bit address range\n"); 67062306a36Sopenharmony_ci pr_err("PCI devices with unassigned 32-bit BARs may not work!\n"); 67162306a36Sopenharmony_ci#else 67262306a36Sopenharmony_ci gapstart = 0x10000000; 67362306a36Sopenharmony_ci#endif 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* 67762306a36Sopenharmony_ci * e820__reserve_resources_late() protects stolen RAM already: 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_ci pci_mem_start = gapstart; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci pr_info("[mem %#010lx-%#010lx] available for PCI devices\n", 68262306a36Sopenharmony_ci gapstart, gapstart + gapsize - 1); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/* 68662306a36Sopenharmony_ci * Called late during init, in free_initmem(). 68762306a36Sopenharmony_ci * 68862306a36Sopenharmony_ci * Initial e820_table and e820_table_kexec are largish __initdata arrays. 68962306a36Sopenharmony_ci * 69062306a36Sopenharmony_ci * Copy them to a (usually much smaller) dynamically allocated area that is 69162306a36Sopenharmony_ci * sized precisely after the number of e820 entries. 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * This is done after we've performed all the fixes and tweaks to the tables. 69462306a36Sopenharmony_ci * All functions which modify them are __init functions, which won't exist 69562306a36Sopenharmony_ci * after free_initmem(). 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci__init void e820__reallocate_tables(void) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct e820_table *n; 70062306a36Sopenharmony_ci int size; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci size = offsetof(struct e820_table, entries) + sizeof(struct e820_entry)*e820_table->nr_entries; 70362306a36Sopenharmony_ci n = kmemdup(e820_table, size, GFP_KERNEL); 70462306a36Sopenharmony_ci BUG_ON(!n); 70562306a36Sopenharmony_ci e820_table = n; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci size = offsetof(struct e820_table, entries) + sizeof(struct e820_entry)*e820_table_kexec->nr_entries; 70862306a36Sopenharmony_ci n = kmemdup(e820_table_kexec, size, GFP_KERNEL); 70962306a36Sopenharmony_ci BUG_ON(!n); 71062306a36Sopenharmony_ci e820_table_kexec = n; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci size = offsetof(struct e820_table, entries) + sizeof(struct e820_entry)*e820_table_firmware->nr_entries; 71362306a36Sopenharmony_ci n = kmemdup(e820_table_firmware, size, GFP_KERNEL); 71462306a36Sopenharmony_ci BUG_ON(!n); 71562306a36Sopenharmony_ci e820_table_firmware = n; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* 71962306a36Sopenharmony_ci * Because of the small fixed size of struct boot_params, only the first 72062306a36Sopenharmony_ci * 128 E820 memory entries are passed to the kernel via boot_params.e820_table, 72162306a36Sopenharmony_ci * the remaining (if any) entries are passed via the SETUP_E820_EXT node of 72262306a36Sopenharmony_ci * struct setup_data, which is parsed here. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_civoid __init e820__memory_setup_extended(u64 phys_addr, u32 data_len) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci int entries; 72762306a36Sopenharmony_ci struct boot_e820_entry *extmap; 72862306a36Sopenharmony_ci struct setup_data *sdata; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci sdata = early_memremap(phys_addr, data_len); 73162306a36Sopenharmony_ci entries = sdata->len / sizeof(*extmap); 73262306a36Sopenharmony_ci extmap = (struct boot_e820_entry *)(sdata->data); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci __append_e820_table(extmap, entries); 73562306a36Sopenharmony_ci e820__update_table(e820_table); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci memcpy(e820_table_kexec, e820_table, sizeof(*e820_table_kexec)); 73862306a36Sopenharmony_ci memcpy(e820_table_firmware, e820_table, sizeof(*e820_table_firmware)); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci early_memunmap(sdata, data_len); 74162306a36Sopenharmony_ci pr_info("extended physical RAM map:\n"); 74262306a36Sopenharmony_ci e820__print_table("extended"); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/* 74662306a36Sopenharmony_ci * Find the ranges of physical addresses that do not correspond to 74762306a36Sopenharmony_ci * E820 RAM areas and register the corresponding pages as 'nosave' for 74862306a36Sopenharmony_ci * hibernation (32-bit) or software suspend and suspend to RAM (64-bit). 74962306a36Sopenharmony_ci * 75062306a36Sopenharmony_ci * This function requires the E820 map to be sorted and without any 75162306a36Sopenharmony_ci * overlapping entries. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_civoid __init e820__register_nosave_regions(unsigned long limit_pfn) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci int i; 75662306a36Sopenharmony_ci unsigned long pfn = 0; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 75962306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (pfn < PFN_UP(entry->addr)) 76262306a36Sopenharmony_ci register_nosave_region(pfn, PFN_UP(entry->addr)); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci pfn = PFN_DOWN(entry->addr + entry->size); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (entry->type != E820_TYPE_RAM && entry->type != E820_TYPE_RESERVED_KERN) 76762306a36Sopenharmony_ci register_nosave_region(PFN_UP(entry->addr), pfn); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (pfn >= limit_pfn) 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci#ifdef CONFIG_ACPI 77562306a36Sopenharmony_ci/* 77662306a36Sopenharmony_ci * Register ACPI NVS memory regions, so that we can save/restore them during 77762306a36Sopenharmony_ci * hibernation and the subsequent resume: 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_cistatic int __init e820__register_nvs_regions(void) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci int i; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 78462306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (entry->type == E820_TYPE_NVS) 78762306a36Sopenharmony_ci acpi_nvs_register(entry->addr, entry->size); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_cicore_initcall(e820__register_nvs_regions); 79362306a36Sopenharmony_ci#endif 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* 79662306a36Sopenharmony_ci * Allocate the requested number of bytes with the requested alignment 79762306a36Sopenharmony_ci * and return (the physical address) to the caller. Also register this 79862306a36Sopenharmony_ci * range in the 'kexec' E820 table as a reserved range. 79962306a36Sopenharmony_ci * 80062306a36Sopenharmony_ci * This allows kexec to fake a new mptable, as if it came from the real 80162306a36Sopenharmony_ci * system. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ciu64 __init e820__memblock_alloc_reserved(u64 size, u64 align) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci u64 addr; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci addr = memblock_phys_alloc(size, align); 80862306a36Sopenharmony_ci if (addr) { 80962306a36Sopenharmony_ci e820__range_update_kexec(addr, size, E820_TYPE_RAM, E820_TYPE_RESERVED); 81062306a36Sopenharmony_ci pr_info("update e820_table_kexec for e820__memblock_alloc_reserved()\n"); 81162306a36Sopenharmony_ci e820__update_table_kexec(); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return addr; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci#ifdef CONFIG_X86_32 81862306a36Sopenharmony_ci# ifdef CONFIG_X86_PAE 81962306a36Sopenharmony_ci# define MAX_ARCH_PFN (1ULL<<(36-PAGE_SHIFT)) 82062306a36Sopenharmony_ci# else 82162306a36Sopenharmony_ci# define MAX_ARCH_PFN (1ULL<<(32-PAGE_SHIFT)) 82262306a36Sopenharmony_ci# endif 82362306a36Sopenharmony_ci#else /* CONFIG_X86_32 */ 82462306a36Sopenharmony_ci# define MAX_ARCH_PFN MAXMEM>>PAGE_SHIFT 82562306a36Sopenharmony_ci#endif 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci/* 82862306a36Sopenharmony_ci * Find the highest page frame number we have available 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_cistatic unsigned long __init e820_end_pfn(unsigned long limit_pfn, enum e820_type type) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int i; 83362306a36Sopenharmony_ci unsigned long last_pfn = 0; 83462306a36Sopenharmony_ci unsigned long max_arch_pfn = MAX_ARCH_PFN; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 83762306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 83862306a36Sopenharmony_ci unsigned long start_pfn; 83962306a36Sopenharmony_ci unsigned long end_pfn; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (entry->type != type) 84262306a36Sopenharmony_ci continue; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci start_pfn = entry->addr >> PAGE_SHIFT; 84562306a36Sopenharmony_ci end_pfn = (entry->addr + entry->size) >> PAGE_SHIFT; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (start_pfn >= limit_pfn) 84862306a36Sopenharmony_ci continue; 84962306a36Sopenharmony_ci if (end_pfn > limit_pfn) { 85062306a36Sopenharmony_ci last_pfn = limit_pfn; 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci if (end_pfn > last_pfn) 85462306a36Sopenharmony_ci last_pfn = end_pfn; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (last_pfn > max_arch_pfn) 85862306a36Sopenharmony_ci last_pfn = max_arch_pfn; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci pr_info("last_pfn = %#lx max_arch_pfn = %#lx\n", 86162306a36Sopenharmony_ci last_pfn, max_arch_pfn); 86262306a36Sopenharmony_ci return last_pfn; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciunsigned long __init e820__end_of_ram_pfn(void) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci return e820_end_pfn(MAX_ARCH_PFN, E820_TYPE_RAM); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ciunsigned long __init e820__end_of_low_ram_pfn(void) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci return e820_end_pfn(1UL << (32 - PAGE_SHIFT), E820_TYPE_RAM); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic void __init early_panic(char *msg) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci early_printk(msg); 87862306a36Sopenharmony_ci panic(msg); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int userdef __initdata; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* The "mem=nopentium" boot option disables 4MB page tables on 32-bit kernels: */ 88462306a36Sopenharmony_cistatic int __init parse_memopt(char *p) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci u64 mem_size; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (!p) 88962306a36Sopenharmony_ci return -EINVAL; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (!strcmp(p, "nopentium")) { 89262306a36Sopenharmony_ci#ifdef CONFIG_X86_32 89362306a36Sopenharmony_ci setup_clear_cpu_cap(X86_FEATURE_PSE); 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci#else 89662306a36Sopenharmony_ci pr_warn("mem=nopentium ignored! (only supported on x86_32)\n"); 89762306a36Sopenharmony_ci return -EINVAL; 89862306a36Sopenharmony_ci#endif 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci userdef = 1; 90262306a36Sopenharmony_ci mem_size = memparse(p, &p); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Don't remove all memory when getting "mem={invalid}" parameter: */ 90562306a36Sopenharmony_ci if (mem_size == 0) 90662306a36Sopenharmony_ci return -EINVAL; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci e820__range_remove(mem_size, ULLONG_MAX - mem_size, E820_TYPE_RAM, 1); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 91162306a36Sopenharmony_ci max_mem_size = mem_size; 91262306a36Sopenharmony_ci#endif 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ciearly_param("mem", parse_memopt); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int __init parse_memmap_one(char *p) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci char *oldp; 92162306a36Sopenharmony_ci u64 start_at, mem_size; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (!p) 92462306a36Sopenharmony_ci return -EINVAL; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (!strncmp(p, "exactmap", 8)) { 92762306a36Sopenharmony_ci e820_table->nr_entries = 0; 92862306a36Sopenharmony_ci userdef = 1; 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci oldp = p; 93362306a36Sopenharmony_ci mem_size = memparse(p, &p); 93462306a36Sopenharmony_ci if (p == oldp) 93562306a36Sopenharmony_ci return -EINVAL; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci userdef = 1; 93862306a36Sopenharmony_ci if (*p == '@') { 93962306a36Sopenharmony_ci start_at = memparse(p+1, &p); 94062306a36Sopenharmony_ci e820__range_add(start_at, mem_size, E820_TYPE_RAM); 94162306a36Sopenharmony_ci } else if (*p == '#') { 94262306a36Sopenharmony_ci start_at = memparse(p+1, &p); 94362306a36Sopenharmony_ci e820__range_add(start_at, mem_size, E820_TYPE_ACPI); 94462306a36Sopenharmony_ci } else if (*p == '$') { 94562306a36Sopenharmony_ci start_at = memparse(p+1, &p); 94662306a36Sopenharmony_ci e820__range_add(start_at, mem_size, E820_TYPE_RESERVED); 94762306a36Sopenharmony_ci } else if (*p == '!') { 94862306a36Sopenharmony_ci start_at = memparse(p+1, &p); 94962306a36Sopenharmony_ci e820__range_add(start_at, mem_size, E820_TYPE_PRAM); 95062306a36Sopenharmony_ci } else if (*p == '%') { 95162306a36Sopenharmony_ci enum e820_type from = 0, to = 0; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci start_at = memparse(p + 1, &p); 95462306a36Sopenharmony_ci if (*p == '-') 95562306a36Sopenharmony_ci from = simple_strtoull(p + 1, &p, 0); 95662306a36Sopenharmony_ci if (*p == '+') 95762306a36Sopenharmony_ci to = simple_strtoull(p + 1, &p, 0); 95862306a36Sopenharmony_ci if (*p != '\0') 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci if (from && to) 96162306a36Sopenharmony_ci e820__range_update(start_at, mem_size, from, to); 96262306a36Sopenharmony_ci else if (to) 96362306a36Sopenharmony_ci e820__range_add(start_at, mem_size, to); 96462306a36Sopenharmony_ci else if (from) 96562306a36Sopenharmony_ci e820__range_remove(start_at, mem_size, from, 1); 96662306a36Sopenharmony_ci else 96762306a36Sopenharmony_ci e820__range_remove(start_at, mem_size, 0, 0); 96862306a36Sopenharmony_ci } else { 96962306a36Sopenharmony_ci e820__range_remove(mem_size, ULLONG_MAX - mem_size, E820_TYPE_RAM, 1); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return *p == '\0' ? 0 : -EINVAL; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int __init parse_memmap_opt(char *str) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci while (str) { 97862306a36Sopenharmony_ci char *k = strchr(str, ','); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (k) 98162306a36Sopenharmony_ci *k++ = 0; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci parse_memmap_one(str); 98462306a36Sopenharmony_ci str = k; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ciearly_param("memmap", parse_memmap_opt); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* 99262306a36Sopenharmony_ci * Reserve all entries from the bootloader's extensible data nodes list, 99362306a36Sopenharmony_ci * because if present we are going to use it later on to fetch e820 99462306a36Sopenharmony_ci * entries from it: 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_civoid __init e820__reserve_setup_data(void) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci struct setup_indirect *indirect; 99962306a36Sopenharmony_ci struct setup_data *data; 100062306a36Sopenharmony_ci u64 pa_data, pa_next; 100162306a36Sopenharmony_ci u32 len; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci pa_data = boot_params.hdr.setup_data; 100462306a36Sopenharmony_ci if (!pa_data) 100562306a36Sopenharmony_ci return; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci while (pa_data) { 100862306a36Sopenharmony_ci data = early_memremap(pa_data, sizeof(*data)); 100962306a36Sopenharmony_ci if (!data) { 101062306a36Sopenharmony_ci pr_warn("e820: failed to memremap setup_data entry\n"); 101162306a36Sopenharmony_ci return; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci len = sizeof(*data); 101562306a36Sopenharmony_ci pa_next = data->next; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* 102062306a36Sopenharmony_ci * SETUP_EFI, SETUP_IMA and SETUP_RNG_SEED are supplied by 102162306a36Sopenharmony_ci * kexec and do not need to be reserved. 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_ci if (data->type != SETUP_EFI && 102462306a36Sopenharmony_ci data->type != SETUP_IMA && 102562306a36Sopenharmony_ci data->type != SETUP_RNG_SEED) 102662306a36Sopenharmony_ci e820__range_update_kexec(pa_data, 102762306a36Sopenharmony_ci sizeof(*data) + data->len, 102862306a36Sopenharmony_ci E820_TYPE_RAM, E820_TYPE_RESERVED_KERN); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (data->type == SETUP_INDIRECT) { 103162306a36Sopenharmony_ci len += data->len; 103262306a36Sopenharmony_ci early_memunmap(data, sizeof(*data)); 103362306a36Sopenharmony_ci data = early_memremap(pa_data, len); 103462306a36Sopenharmony_ci if (!data) { 103562306a36Sopenharmony_ci pr_warn("e820: failed to memremap indirect setup_data\n"); 103662306a36Sopenharmony_ci return; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci indirect = (struct setup_indirect *)data->data; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (indirect->type != SETUP_INDIRECT) { 104262306a36Sopenharmony_ci e820__range_update(indirect->addr, indirect->len, 104362306a36Sopenharmony_ci E820_TYPE_RAM, E820_TYPE_RESERVED_KERN); 104462306a36Sopenharmony_ci e820__range_update_kexec(indirect->addr, indirect->len, 104562306a36Sopenharmony_ci E820_TYPE_RAM, E820_TYPE_RESERVED_KERN); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci pa_data = pa_next; 105062306a36Sopenharmony_ci early_memunmap(data, len); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci e820__update_table(e820_table); 105462306a36Sopenharmony_ci e820__update_table(e820_table_kexec); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci pr_info("extended physical RAM map:\n"); 105762306a36Sopenharmony_ci e820__print_table("reserve setup_data"); 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci/* 106162306a36Sopenharmony_ci * Called after parse_early_param(), after early parameters (such as mem=) 106262306a36Sopenharmony_ci * have been processed, in which case we already have an E820 table filled in 106362306a36Sopenharmony_ci * via the parameter callback function(s), but it's not sorted and printed yet: 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_civoid __init e820__finish_early_params(void) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci if (userdef) { 106862306a36Sopenharmony_ci if (e820__update_table(e820_table) < 0) 106962306a36Sopenharmony_ci early_panic("Invalid user supplied memory map"); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci pr_info("user-defined physical RAM map:\n"); 107262306a36Sopenharmony_ci e820__print_table("user"); 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic const char *__init e820_type_to_string(struct e820_entry *entry) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci switch (entry->type) { 107962306a36Sopenharmony_ci case E820_TYPE_RESERVED_KERN: /* Fall-through: */ 108062306a36Sopenharmony_ci case E820_TYPE_RAM: return "System RAM"; 108162306a36Sopenharmony_ci case E820_TYPE_ACPI: return "ACPI Tables"; 108262306a36Sopenharmony_ci case E820_TYPE_NVS: return "ACPI Non-volatile Storage"; 108362306a36Sopenharmony_ci case E820_TYPE_UNUSABLE: return "Unusable memory"; 108462306a36Sopenharmony_ci case E820_TYPE_PRAM: return "Persistent Memory (legacy)"; 108562306a36Sopenharmony_ci case E820_TYPE_PMEM: return "Persistent Memory"; 108662306a36Sopenharmony_ci case E820_TYPE_RESERVED: return "Reserved"; 108762306a36Sopenharmony_ci case E820_TYPE_SOFT_RESERVED: return "Soft Reserved"; 108862306a36Sopenharmony_ci default: return "Unknown E820 type"; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic unsigned long __init e820_type_to_iomem_type(struct e820_entry *entry) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci switch (entry->type) { 109562306a36Sopenharmony_ci case E820_TYPE_RESERVED_KERN: /* Fall-through: */ 109662306a36Sopenharmony_ci case E820_TYPE_RAM: return IORESOURCE_SYSTEM_RAM; 109762306a36Sopenharmony_ci case E820_TYPE_ACPI: /* Fall-through: */ 109862306a36Sopenharmony_ci case E820_TYPE_NVS: /* Fall-through: */ 109962306a36Sopenharmony_ci case E820_TYPE_UNUSABLE: /* Fall-through: */ 110062306a36Sopenharmony_ci case E820_TYPE_PRAM: /* Fall-through: */ 110162306a36Sopenharmony_ci case E820_TYPE_PMEM: /* Fall-through: */ 110262306a36Sopenharmony_ci case E820_TYPE_RESERVED: /* Fall-through: */ 110362306a36Sopenharmony_ci case E820_TYPE_SOFT_RESERVED: /* Fall-through: */ 110462306a36Sopenharmony_ci default: return IORESOURCE_MEM; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic unsigned long __init e820_type_to_iores_desc(struct e820_entry *entry) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci switch (entry->type) { 111162306a36Sopenharmony_ci case E820_TYPE_ACPI: return IORES_DESC_ACPI_TABLES; 111262306a36Sopenharmony_ci case E820_TYPE_NVS: return IORES_DESC_ACPI_NV_STORAGE; 111362306a36Sopenharmony_ci case E820_TYPE_PMEM: return IORES_DESC_PERSISTENT_MEMORY; 111462306a36Sopenharmony_ci case E820_TYPE_PRAM: return IORES_DESC_PERSISTENT_MEMORY_LEGACY; 111562306a36Sopenharmony_ci case E820_TYPE_RESERVED: return IORES_DESC_RESERVED; 111662306a36Sopenharmony_ci case E820_TYPE_SOFT_RESERVED: return IORES_DESC_SOFT_RESERVED; 111762306a36Sopenharmony_ci case E820_TYPE_RESERVED_KERN: /* Fall-through: */ 111862306a36Sopenharmony_ci case E820_TYPE_RAM: /* Fall-through: */ 111962306a36Sopenharmony_ci case E820_TYPE_UNUSABLE: /* Fall-through: */ 112062306a36Sopenharmony_ci default: return IORES_DESC_NONE; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic bool __init do_mark_busy(enum e820_type type, struct resource *res) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci /* this is the legacy bios/dos rom-shadow + mmio region */ 112762306a36Sopenharmony_ci if (res->start < (1ULL<<20)) 112862306a36Sopenharmony_ci return true; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci /* 113162306a36Sopenharmony_ci * Treat persistent memory and other special memory ranges like 113262306a36Sopenharmony_ci * device memory, i.e. reserve it for exclusive use of a driver 113362306a36Sopenharmony_ci */ 113462306a36Sopenharmony_ci switch (type) { 113562306a36Sopenharmony_ci case E820_TYPE_RESERVED: 113662306a36Sopenharmony_ci case E820_TYPE_SOFT_RESERVED: 113762306a36Sopenharmony_ci case E820_TYPE_PRAM: 113862306a36Sopenharmony_ci case E820_TYPE_PMEM: 113962306a36Sopenharmony_ci return false; 114062306a36Sopenharmony_ci case E820_TYPE_RESERVED_KERN: 114162306a36Sopenharmony_ci case E820_TYPE_RAM: 114262306a36Sopenharmony_ci case E820_TYPE_ACPI: 114362306a36Sopenharmony_ci case E820_TYPE_NVS: 114462306a36Sopenharmony_ci case E820_TYPE_UNUSABLE: 114562306a36Sopenharmony_ci default: 114662306a36Sopenharmony_ci return true; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/* 115162306a36Sopenharmony_ci * Mark E820 reserved areas as busy for the resource manager: 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic struct resource __initdata *e820_res; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_civoid __init e820__reserve_resources(void) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci int i; 115962306a36Sopenharmony_ci struct resource *res; 116062306a36Sopenharmony_ci u64 end; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci res = memblock_alloc(sizeof(*res) * e820_table->nr_entries, 116362306a36Sopenharmony_ci SMP_CACHE_BYTES); 116462306a36Sopenharmony_ci if (!res) 116562306a36Sopenharmony_ci panic("%s: Failed to allocate %zu bytes\n", __func__, 116662306a36Sopenharmony_ci sizeof(*res) * e820_table->nr_entries); 116762306a36Sopenharmony_ci e820_res = res; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 117062306a36Sopenharmony_ci struct e820_entry *entry = e820_table->entries + i; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci end = entry->addr + entry->size - 1; 117362306a36Sopenharmony_ci if (end != (resource_size_t)end) { 117462306a36Sopenharmony_ci res++; 117562306a36Sopenharmony_ci continue; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci res->start = entry->addr; 117862306a36Sopenharmony_ci res->end = end; 117962306a36Sopenharmony_ci res->name = e820_type_to_string(entry); 118062306a36Sopenharmony_ci res->flags = e820_type_to_iomem_type(entry); 118162306a36Sopenharmony_ci res->desc = e820_type_to_iores_desc(entry); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* 118462306a36Sopenharmony_ci * Don't register the region that could be conflicted with 118562306a36Sopenharmony_ci * PCI device BAR resources and insert them later in 118662306a36Sopenharmony_ci * pcibios_resource_survey(): 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_ci if (do_mark_busy(entry->type, res)) { 118962306a36Sopenharmony_ci res->flags |= IORESOURCE_BUSY; 119062306a36Sopenharmony_ci insert_resource(&iomem_resource, res); 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci res++; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* Expose the bootloader-provided memory layout to the sysfs. */ 119662306a36Sopenharmony_ci for (i = 0; i < e820_table_firmware->nr_entries; i++) { 119762306a36Sopenharmony_ci struct e820_entry *entry = e820_table_firmware->entries + i; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci firmware_map_add_early(entry->addr, entry->addr + entry->size, e820_type_to_string(entry)); 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci/* 120462306a36Sopenharmony_ci * How much should we pad the end of RAM, depending on where it is? 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_cistatic unsigned long __init ram_alignment(resource_size_t pos) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci unsigned long mb = pos >> 20; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* To 64kB in the first megabyte */ 121162306a36Sopenharmony_ci if (!mb) 121262306a36Sopenharmony_ci return 64*1024; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* To 1MB in the first 16MB */ 121562306a36Sopenharmony_ci if (mb < 16) 121662306a36Sopenharmony_ci return 1024*1024; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* To 64MB for anything above that */ 121962306a36Sopenharmony_ci return 64*1024*1024; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci#define MAX_RESOURCE_SIZE ((resource_size_t)-1) 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_civoid __init e820__reserve_resources_late(void) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci int i; 122762306a36Sopenharmony_ci struct resource *res; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci res = e820_res; 123062306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 123162306a36Sopenharmony_ci if (!res->parent && res->end) 123262306a36Sopenharmony_ci insert_resource_expand_to_fit(&iomem_resource, res); 123362306a36Sopenharmony_ci res++; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* 123762306a36Sopenharmony_ci * Try to bump up RAM regions to reasonable boundaries, to 123862306a36Sopenharmony_ci * avoid stolen RAM: 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 124162306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 124262306a36Sopenharmony_ci u64 start, end; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (entry->type != E820_TYPE_RAM) 124562306a36Sopenharmony_ci continue; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci start = entry->addr + entry->size; 124862306a36Sopenharmony_ci end = round_up(start, ram_alignment(start)) - 1; 124962306a36Sopenharmony_ci if (end > MAX_RESOURCE_SIZE) 125062306a36Sopenharmony_ci end = MAX_RESOURCE_SIZE; 125162306a36Sopenharmony_ci if (start >= end) 125262306a36Sopenharmony_ci continue; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci printk(KERN_DEBUG "e820: reserve RAM buffer [mem %#010llx-%#010llx]\n", start, end); 125562306a36Sopenharmony_ci reserve_region_with_split(&iomem_resource, start, end, "RAM buffer"); 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci/* 126062306a36Sopenharmony_ci * Pass the firmware (bootloader) E820 map to the kernel and process it: 126162306a36Sopenharmony_ci */ 126262306a36Sopenharmony_cichar *__init e820__memory_setup_default(void) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci char *who = "BIOS-e820"; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* 126762306a36Sopenharmony_ci * Try to copy the BIOS-supplied E820-map. 126862306a36Sopenharmony_ci * 126962306a36Sopenharmony_ci * Otherwise fake a memory map; one section from 0k->640k, 127062306a36Sopenharmony_ci * the next section from 1mb->appropriate_mem_k 127162306a36Sopenharmony_ci */ 127262306a36Sopenharmony_ci if (append_e820_table(boot_params.e820_table, boot_params.e820_entries) < 0) { 127362306a36Sopenharmony_ci u64 mem_size; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci /* Compare results from other methods and take the one that gives more RAM: */ 127662306a36Sopenharmony_ci if (boot_params.alt_mem_k < boot_params.screen_info.ext_mem_k) { 127762306a36Sopenharmony_ci mem_size = boot_params.screen_info.ext_mem_k; 127862306a36Sopenharmony_ci who = "BIOS-88"; 127962306a36Sopenharmony_ci } else { 128062306a36Sopenharmony_ci mem_size = boot_params.alt_mem_k; 128162306a36Sopenharmony_ci who = "BIOS-e801"; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci e820_table->nr_entries = 0; 128562306a36Sopenharmony_ci e820__range_add(0, LOWMEMSIZE(), E820_TYPE_RAM); 128662306a36Sopenharmony_ci e820__range_add(HIGH_MEMORY, mem_size << 10, E820_TYPE_RAM); 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* We just appended a lot of ranges, sanitize the table: */ 129062306a36Sopenharmony_ci e820__update_table(e820_table); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return who; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci/* 129662306a36Sopenharmony_ci * Calls e820__memory_setup_default() in essence to pick up the firmware/bootloader 129762306a36Sopenharmony_ci * E820 map - with an optional platform quirk available for virtual platforms 129862306a36Sopenharmony_ci * to override this method of boot environment processing: 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_civoid __init e820__memory_setup(void) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci char *who; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci /* This is a firmware interface ABI - make sure we don't break it: */ 130562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct boot_e820_entry) != 20); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci who = x86_init.resources.memory_setup(); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci memcpy(e820_table_kexec, e820_table, sizeof(*e820_table_kexec)); 131062306a36Sopenharmony_ci memcpy(e820_table_firmware, e820_table, sizeof(*e820_table_firmware)); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci pr_info("BIOS-provided physical RAM map:\n"); 131362306a36Sopenharmony_ci e820__print_table(who); 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_civoid __init e820__memblock_setup(void) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci int i; 131962306a36Sopenharmony_ci u64 end; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* 132262306a36Sopenharmony_ci * The bootstrap memblock region count maximum is 128 entries 132362306a36Sopenharmony_ci * (INIT_MEMBLOCK_REGIONS), but EFI might pass us more E820 entries 132462306a36Sopenharmony_ci * than that - so allow memblock resizing. 132562306a36Sopenharmony_ci * 132662306a36Sopenharmony_ci * This is safe, because this call happens pretty late during x86 setup, 132762306a36Sopenharmony_ci * so we know about reserved memory regions already. (This is important 132862306a36Sopenharmony_ci * so that memblock resizing does no stomp over reserved areas.) 132962306a36Sopenharmony_ci */ 133062306a36Sopenharmony_ci memblock_allow_resize(); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci for (i = 0; i < e820_table->nr_entries; i++) { 133362306a36Sopenharmony_ci struct e820_entry *entry = &e820_table->entries[i]; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci end = entry->addr + entry->size; 133662306a36Sopenharmony_ci if (end != (resource_size_t)end) 133762306a36Sopenharmony_ci continue; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (entry->type == E820_TYPE_SOFT_RESERVED) 134062306a36Sopenharmony_ci memblock_reserve(entry->addr, entry->size); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (entry->type != E820_TYPE_RAM && entry->type != E820_TYPE_RESERVED_KERN) 134362306a36Sopenharmony_ci continue; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci memblock_add(entry->addr, entry->size); 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* Throw away partial pages: */ 134962306a36Sopenharmony_ci memblock_trim_memory(PAGE_SIZE); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci memblock_dump_all(); 135262306a36Sopenharmony_ci} 1353