1/* 2 * fake_mem.c 3 * 4 * Copyright (C) 2015 FUJITSU LIMITED 5 * Author: Taku Izumi <izumi.taku@jp.fujitsu.com> 6 * 7 * This code introduces new boot option named "efi_fake_mem" 8 * By specifying this parameter, you can add arbitrary attribute to 9 * specific memory range by updating original (firmware provided) EFI 10 * memmap. 11 * 12 * This program is free software; you can redistribute it and/or modify it 13 * under the terms and conditions of the GNU General Public License, 14 * version 2, as published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope it will be useful, but WITHOUT 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 19 * more details. 20 * 21 * You should have received a copy of the GNU General Public License along with 22 * this program; if not, see <http://www.gnu.org/licenses/>. 23 * 24 * The full GNU General Public License is included in this distribution in 25 * the file called "COPYING". 26 */ 27 28#include <linux/kernel.h> 29#include <linux/efi.h> 30#include <linux/init.h> 31#include <linux/memblock.h> 32#include <linux/types.h> 33#include <linux/sort.h> 34#include <asm/efi.h> 35 36#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM 37 38struct fake_mem { 39 struct range range; 40 u64 attribute; 41}; 42static struct fake_mem fake_mems[EFI_MAX_FAKEMEM]; 43static int nr_fake_mem; 44 45static int __init cmp_fake_mem(const void *x1, const void *x2) 46{ 47 const struct fake_mem *m1 = x1; 48 const struct fake_mem *m2 = x2; 49 50 if (m1->range.start < m2->range.start) 51 return -1; 52 if (m1->range.start > m2->range.start) 53 return 1; 54 return 0; 55} 56 57void __init efi_fake_memmap(void) 58{ 59 u64 start, end, m_start, m_end, m_attr; 60 int new_nr_map = memmap.nr_map; 61 efi_memory_desc_t *md; 62 phys_addr_t new_memmap_phy; 63 void *new_memmap; 64 void *old, *new; 65 int i; 66 67 if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP)) 68 return; 69 70 /* count up the number of EFI memory descriptor */ 71 for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) { 72 md = old; 73 start = md->phys_addr; 74 end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; 75 76 for (i = 0; i < nr_fake_mem; i++) { 77 /* modifying range */ 78 m_start = fake_mems[i].range.start; 79 m_end = fake_mems[i].range.end; 80 81 if (m_start <= start) { 82 /* split into 2 parts */ 83 if (start < m_end && m_end < end) 84 new_nr_map++; 85 } 86 if (start < m_start && m_start < end) { 87 /* split into 3 parts */ 88 if (m_end < end) 89 new_nr_map += 2; 90 /* split into 2 parts */ 91 if (end <= m_end) 92 new_nr_map++; 93 } 94 } 95 } 96 97 /* allocate memory for new EFI memmap */ 98 new_memmap_phy = memblock_alloc(memmap.desc_size * new_nr_map, 99 PAGE_SIZE); 100 if (!new_memmap_phy) 101 return; 102 103 /* create new EFI memmap */ 104 new_memmap = early_memremap(new_memmap_phy, 105 memmap.desc_size * new_nr_map); 106 if (!new_memmap) { 107 memblock_free(new_memmap_phy, memmap.desc_size * new_nr_map); 108 return; 109 } 110 111 for (old = memmap.map, new = new_memmap; 112 old < memmap.map_end; 113 old += memmap.desc_size, new += memmap.desc_size) { 114 115 /* copy original EFI memory descriptor */ 116 memcpy(new, old, memmap.desc_size); 117 md = new; 118 start = md->phys_addr; 119 end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; 120 121 for (i = 0; i < nr_fake_mem; i++) { 122 /* modifying range */ 123 m_start = fake_mems[i].range.start; 124 m_end = fake_mems[i].range.end; 125 m_attr = fake_mems[i].attribute; 126 127 if (m_start <= start && end <= m_end) 128 md->attribute |= m_attr; 129 130 if (m_start <= start && 131 (start < m_end && m_end < end)) { 132 /* first part */ 133 md->attribute |= m_attr; 134 md->num_pages = (m_end - md->phys_addr + 1) >> 135 EFI_PAGE_SHIFT; 136 /* latter part */ 137 new += memmap.desc_size; 138 memcpy(new, old, memmap.desc_size); 139 md = new; 140 md->phys_addr = m_end + 1; 141 md->num_pages = (end - md->phys_addr + 1) >> 142 EFI_PAGE_SHIFT; 143 } 144 145 if ((start < m_start && m_start < end) && m_end < end) { 146 /* first part */ 147 md->num_pages = (m_start - md->phys_addr) >> 148 EFI_PAGE_SHIFT; 149 /* middle part */ 150 new += memmap.desc_size; 151 memcpy(new, old, memmap.desc_size); 152 md = new; 153 md->attribute |= m_attr; 154 md->phys_addr = m_start; 155 md->num_pages = (m_end - m_start + 1) >> 156 EFI_PAGE_SHIFT; 157 /* last part */ 158 new += memmap.desc_size; 159 memcpy(new, old, memmap.desc_size); 160 md = new; 161 md->phys_addr = m_end + 1; 162 md->num_pages = (end - m_end) >> 163 EFI_PAGE_SHIFT; 164 } 165 166 if ((start < m_start && m_start < end) && 167 (end <= m_end)) { 168 /* first part */ 169 md->num_pages = (m_start - md->phys_addr) >> 170 EFI_PAGE_SHIFT; 171 /* latter part */ 172 new += memmap.desc_size; 173 memcpy(new, old, memmap.desc_size); 174 md = new; 175 md->phys_addr = m_start; 176 md->num_pages = (end - md->phys_addr + 1) >> 177 EFI_PAGE_SHIFT; 178 md->attribute |= m_attr; 179 } 180 } 181 } 182 183 /* swap into new EFI memmap */ 184 efi_unmap_memmap(); 185 memmap.map = new_memmap; 186 memmap.phys_map = new_memmap_phy; 187 memmap.nr_map = new_nr_map; 188 memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size; 189 set_bit(EFI_MEMMAP, &efi.flags); 190 191 /* print new EFI memmap */ 192 efi_print_memmap(); 193} 194 195static int __init setup_fake_mem(char *p) 196{ 197 u64 start = 0, mem_size = 0, attribute = 0; 198 int i; 199 200 if (!p) 201 return -EINVAL; 202 203 while (*p != '\0') { 204 mem_size = memparse(p, &p); 205 if (*p == '@') 206 start = memparse(p+1, &p); 207 else 208 break; 209 210 if (*p == ':') 211 attribute = simple_strtoull(p+1, &p, 0); 212 else 213 break; 214 215 if (nr_fake_mem >= EFI_MAX_FAKEMEM) 216 break; 217 218 fake_mems[nr_fake_mem].range.start = start; 219 fake_mems[nr_fake_mem].range.end = start + mem_size - 1; 220 fake_mems[nr_fake_mem].attribute = attribute; 221 nr_fake_mem++; 222 223 if (*p == ',') 224 p++; 225 } 226 227 sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem), 228 cmp_fake_mem, NULL); 229 230 for (i = 0; i < nr_fake_mem; i++) 231 pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", 232 fake_mems[i].attribute, fake_mems[i].range.start, 233 fake_mems[i].range.end); 234 235 return *p == '\0' ? 0 : -EINVAL; 236} 237 238early_param("efi_fake_mem", setup_fake_mem); 239