18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fake_mem.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 FUJITSU LIMITED 68c2ecf20Sopenharmony_ci * Author: Taku Izumi <izumi.taku@jp.fujitsu.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This code introduces new boot option named "efi_fake_mem" 98c2ecf20Sopenharmony_ci * By specifying this parameter, you can add arbitrary attribute to 108c2ecf20Sopenharmony_ci * specific memory range by updating original (firmware provided) EFI 118c2ecf20Sopenharmony_ci * memmap. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/efi.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/memblock.h> 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/sort.h> 208c2ecf20Sopenharmony_ci#include "fake_mem.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; 238c2ecf20Sopenharmony_ciint nr_fake_mem; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int __init cmp_fake_mem(const void *x1, const void *x2) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci const struct efi_mem_range *m1 = x1; 288c2ecf20Sopenharmony_ci const struct efi_mem_range *m2 = x2; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (m1->range.start < m2->range.start) 318c2ecf20Sopenharmony_ci return -1; 328c2ecf20Sopenharmony_ci if (m1->range.start > m2->range.start) 338c2ecf20Sopenharmony_ci return 1; 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void __init efi_fake_range(struct efi_mem_range *efi_range) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct efi_memory_map_data data = { 0 }; 408c2ecf20Sopenharmony_ci int new_nr_map = efi.memmap.nr_map; 418c2ecf20Sopenharmony_ci efi_memory_desc_t *md; 428c2ecf20Sopenharmony_ci void *new_memmap; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* count up the number of EFI memory descriptor */ 458c2ecf20Sopenharmony_ci for_each_efi_memory_desc(md) 468c2ecf20Sopenharmony_ci new_nr_map += efi_memmap_split_count(md, &efi_range->range); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* allocate memory for new EFI memmap */ 498c2ecf20Sopenharmony_ci if (efi_memmap_alloc(new_nr_map, &data) != 0) 508c2ecf20Sopenharmony_ci return; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* create new EFI memmap */ 538c2ecf20Sopenharmony_ci new_memmap = early_memremap(data.phys_map, data.size); 548c2ecf20Sopenharmony_ci if (!new_memmap) { 558c2ecf20Sopenharmony_ci __efi_memmap_free(data.phys_map, data.size, data.flags); 568c2ecf20Sopenharmony_ci return; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci efi_memmap_insert(&efi.memmap, new_memmap, efi_range); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* swap into new EFI memmap */ 628c2ecf20Sopenharmony_ci early_memunmap(new_memmap, data.size); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci efi_memmap_install(&data); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid __init efi_fake_memmap(void) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci int i; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) 728c2ecf20Sopenharmony_ci return; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (i = 0; i < nr_fake_mem; i++) 758c2ecf20Sopenharmony_ci efi_fake_range(&efi_fake_mems[i]); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* print new EFI memmap */ 788c2ecf20Sopenharmony_ci efi_print_memmap(); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int __init setup_fake_mem(char *p) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u64 start = 0, mem_size = 0, attribute = 0; 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!p) 878c2ecf20Sopenharmony_ci return -EINVAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci while (*p != '\0') { 908c2ecf20Sopenharmony_ci mem_size = memparse(p, &p); 918c2ecf20Sopenharmony_ci if (*p == '@') 928c2ecf20Sopenharmony_ci start = memparse(p+1, &p); 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (*p == ':') 978c2ecf20Sopenharmony_ci attribute = simple_strtoull(p+1, &p, 0); 988c2ecf20Sopenharmony_ci else 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (nr_fake_mem >= EFI_MAX_FAKEMEM) 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci efi_fake_mems[nr_fake_mem].range.start = start; 1058c2ecf20Sopenharmony_ci efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1; 1068c2ecf20Sopenharmony_ci efi_fake_mems[nr_fake_mem].attribute = attribute; 1078c2ecf20Sopenharmony_ci nr_fake_mem++; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (*p == ',') 1108c2ecf20Sopenharmony_ci p++; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), 1148c2ecf20Sopenharmony_ci cmp_fake_mem, NULL); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci for (i = 0; i < nr_fake_mem; i++) 1178c2ecf20Sopenharmony_ci pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", 1188c2ecf20Sopenharmony_ci efi_fake_mems[i].attribute, efi_fake_mems[i].range.start, 1198c2ecf20Sopenharmony_ci efi_fake_mems[i].range.end); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return *p == '\0' ? 0 : -EINVAL; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciearly_param("efi_fake_mem", setup_fake_mem); 125