162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fake_mem.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 FUJITSU LIMITED 662306a36Sopenharmony_ci * Author: Taku Izumi <izumi.taku@jp.fujitsu.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This code introduces new boot option named "efi_fake_mem" 962306a36Sopenharmony_ci * By specifying this parameter, you can add arbitrary attribute to 1062306a36Sopenharmony_ci * specific memory range by updating original (firmware provided) EFI 1162306a36Sopenharmony_ci * memmap. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/efi.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/memblock.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/sort.h> 2062306a36Sopenharmony_ci#include <asm/e820/api.h> 2162306a36Sopenharmony_ci#include <asm/efi.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; 2662306a36Sopenharmony_cistatic int nr_fake_mem; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int __init cmp_fake_mem(const void *x1, const void *x2) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci const struct efi_mem_range *m1 = x1; 3162306a36Sopenharmony_ci const struct efi_mem_range *m2 = x2; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (m1->range.start < m2->range.start) 3462306a36Sopenharmony_ci return -1; 3562306a36Sopenharmony_ci if (m1->range.start > m2->range.start) 3662306a36Sopenharmony_ci return 1; 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void __init efi_fake_range(struct efi_mem_range *efi_range) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct efi_memory_map_data data = { 0 }; 4362306a36Sopenharmony_ci int new_nr_map = efi.memmap.nr_map; 4462306a36Sopenharmony_ci efi_memory_desc_t *md; 4562306a36Sopenharmony_ci void *new_memmap; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* count up the number of EFI memory descriptor */ 4862306a36Sopenharmony_ci for_each_efi_memory_desc(md) 4962306a36Sopenharmony_ci new_nr_map += efi_memmap_split_count(md, &efi_range->range); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* allocate memory for new EFI memmap */ 5262306a36Sopenharmony_ci if (efi_memmap_alloc(new_nr_map, &data) != 0) 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* create new EFI memmap */ 5662306a36Sopenharmony_ci new_memmap = early_memremap(data.phys_map, data.size); 5762306a36Sopenharmony_ci if (!new_memmap) { 5862306a36Sopenharmony_ci __efi_memmap_free(data.phys_map, data.size, data.flags); 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci efi_memmap_insert(&efi.memmap, new_memmap, efi_range); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* swap into new EFI memmap */ 6562306a36Sopenharmony_ci early_memunmap(new_memmap, data.size); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci efi_memmap_install(&data); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_civoid __init efi_fake_memmap(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int i; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) 7562306a36Sopenharmony_ci return; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci for (i = 0; i < nr_fake_mem; i++) 7862306a36Sopenharmony_ci efi_fake_range(&efi_fake_mems[i]); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* print new EFI memmap */ 8162306a36Sopenharmony_ci efi_print_memmap(); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int __init setup_fake_mem(char *p) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u64 start = 0, mem_size = 0, attribute = 0; 8762306a36Sopenharmony_ci int i; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!p) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci while (*p != '\0') { 9362306a36Sopenharmony_ci mem_size = memparse(p, &p); 9462306a36Sopenharmony_ci if (*p == '@') 9562306a36Sopenharmony_ci start = memparse(p+1, &p); 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (*p == ':') 10062306a36Sopenharmony_ci attribute = simple_strtoull(p+1, &p, 0); 10162306a36Sopenharmony_ci else 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (nr_fake_mem >= EFI_MAX_FAKEMEM) 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci efi_fake_mems[nr_fake_mem].range.start = start; 10862306a36Sopenharmony_ci efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1; 10962306a36Sopenharmony_ci efi_fake_mems[nr_fake_mem].attribute = attribute; 11062306a36Sopenharmony_ci nr_fake_mem++; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (*p == ',') 11362306a36Sopenharmony_ci p++; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), 11762306a36Sopenharmony_ci cmp_fake_mem, NULL); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < nr_fake_mem; i++) 12062306a36Sopenharmony_ci pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", 12162306a36Sopenharmony_ci efi_fake_mems[i].attribute, efi_fake_mems[i].range.start, 12262306a36Sopenharmony_ci efi_fake_mems[i].range.end); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return *p == '\0' ? 0 : -EINVAL; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciearly_param("efi_fake_mem", setup_fake_mem); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_civoid __init efi_fake_memmap_early(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int i; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * The late efi_fake_mem() call can handle all requests if 13562306a36Sopenharmony_ci * EFI_MEMORY_SP support is disabled. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (!efi_soft_reserve_enabled()) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * Given that efi_fake_memmap() needs to perform memblock 14562306a36Sopenharmony_ci * allocations it needs to run after e820__memblock_setup(). 14662306a36Sopenharmony_ci * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given 14762306a36Sopenharmony_ci * address range that potentially needs to mark the memory as 14862306a36Sopenharmony_ci * reserved prior to e820__memblock_setup(). Update e820 14962306a36Sopenharmony_ci * directly if EFI_MEMORY_SP is specified for an 15062306a36Sopenharmony_ci * EFI_CONVENTIONAL_MEMORY descriptor. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci for (i = 0; i < nr_fake_mem; i++) { 15362306a36Sopenharmony_ci struct efi_mem_range *mem = &efi_fake_mems[i]; 15462306a36Sopenharmony_ci efi_memory_desc_t *md; 15562306a36Sopenharmony_ci u64 m_start, m_end; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if ((mem->attribute & EFI_MEMORY_SP) == 0) 15862306a36Sopenharmony_ci continue; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci m_start = mem->range.start; 16162306a36Sopenharmony_ci m_end = mem->range.end; 16262306a36Sopenharmony_ci for_each_efi_memory_desc(md) { 16362306a36Sopenharmony_ci u64 start, end, size; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (md->type != EFI_CONVENTIONAL_MEMORY) 16662306a36Sopenharmony_ci continue; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci start = md->phys_addr; 16962306a36Sopenharmony_ci end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (m_start <= end && m_end >= start) 17262306a36Sopenharmony_ci /* fake range overlaps descriptor */; 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci continue; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * Trim the boundary of the e820 update to the 17862306a36Sopenharmony_ci * descriptor in case the fake range overlaps 17962306a36Sopenharmony_ci * !EFI_CONVENTIONAL_MEMORY 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci start = max(start, m_start); 18262306a36Sopenharmony_ci end = min(end, m_end); 18362306a36Sopenharmony_ci size = end - start + 1; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (end <= start) 18662306a36Sopenharmony_ci continue; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Ensure each efi_fake_mem instance results in 19062306a36Sopenharmony_ci * a unique e820 resource 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci e820__range_remove(start, size, E820_TYPE_RAM, 1); 19362306a36Sopenharmony_ci e820__range_add(start, size, E820_TYPE_SOFT_RESERVED); 19462306a36Sopenharmony_ci e820__update_table(e820_table); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci} 198