1// SPDX-License-Identifier: GPL-2.0
2/*
3 * fake_mem.c
4 *
5 * Copyright (C) 2015 FUJITSU LIMITED
6 * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
7 *
8 * This code introduces new boot option named "efi_fake_mem"
9 * By specifying this parameter, you can add arbitrary attribute to
10 * specific memory range by updating original (firmware provided) EFI
11 * memmap.
12 */
13
14#include <linux/kernel.h>
15#include <linux/efi.h>
16#include <linux/init.h>
17#include <linux/memblock.h>
18#include <linux/types.h>
19#include <linux/sort.h>
20#include <asm/e820/api.h>
21#include <asm/efi.h>
22
23#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
24
25static struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM];
26static int nr_fake_mem;
27
28static int __init cmp_fake_mem(const void *x1, const void *x2)
29{
30 const struct efi_mem_range *m1 = x1;
31 const struct efi_mem_range *m2 = x2;
32
33 if (m1->range.start < m2->range.start)
34 return -1;
35 if (m1->range.start > m2->range.start)
36 return 1;
37 return 0;
38}
39
40static void __init efi_fake_range(struct efi_mem_range *efi_range)
41{
42 struct efi_memory_map_data data = { 0 };
43 int new_nr_map = efi.memmap.nr_map;
44 efi_memory_desc_t *md;
45 void *new_memmap;
46
47 /* count up the number of EFI memory descriptor */
48 for_each_efi_memory_desc(md)
49 new_nr_map += efi_memmap_split_count(md, range: &efi_range->range);
50
51 /* allocate memory for new EFI memmap */
52 if (efi_memmap_alloc(num_entries: new_nr_map, data: &data) != 0)
53 return;
54
55 /* create new EFI memmap */
56 new_memmap = early_memremap(phys_addr: data.phys_map, size: data.size);
57 if (!new_memmap) {
58 __efi_memmap_free(phys: data.phys_map, size: data.size, flags: data.flags);
59 return;
60 }
61
62 efi_memmap_insert(old_memmap: &efi.memmap, buf: new_memmap, mem: efi_range);
63
64 /* swap into new EFI memmap */
65 early_memunmap(addr: new_memmap, size: data.size);
66
67 efi_memmap_install(data: &data);
68}
69
70void __init efi_fake_memmap(void)
71{
72 int i;
73
74 if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
75 return;
76
77 for (i = 0; i < nr_fake_mem; i++)
78 efi_fake_range(efi_range: &efi_fake_mems[i]);
79
80 /* print new EFI memmap */
81 efi_print_memmap();
82}
83
84static int __init setup_fake_mem(char *p)
85{
86 u64 start = 0, mem_size = 0, attribute = 0;
87 int i;
88
89 if (!p)
90 return -EINVAL;
91
92 while (*p != '\0') {
93 mem_size = memparse(ptr: p, retptr: &p);
94 if (*p == '@')
95 start = memparse(ptr: p+1, retptr: &p);
96 else
97 break;
98
99 if (*p == ':')
100 attribute = simple_strtoull(p+1, &p, 0);
101 else
102 break;
103
104 if (nr_fake_mem >= EFI_MAX_FAKEMEM)
105 break;
106
107 efi_fake_mems[nr_fake_mem].range.start = start;
108 efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
109 efi_fake_mems[nr_fake_mem].attribute = attribute;
110 nr_fake_mem++;
111
112 if (*p == ',')
113 p++;
114 }
115
116 sort(base: efi_fake_mems, num: nr_fake_mem, size: sizeof(struct efi_mem_range),
117 cmp_func: cmp_fake_mem, NULL);
118
119 for (i = 0; i < nr_fake_mem; i++)
120 pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
121 efi_fake_mems[i].attribute, efi_fake_mems[i].range.start,
122 efi_fake_mems[i].range.end);
123
124 return *p == '\0' ? 0 : -EINVAL;
125}
126
127early_param("efi_fake_mem", setup_fake_mem);
128
129void __init efi_fake_memmap_early(void)
130{
131 int i;
132
133 /*
134 * The late efi_fake_mem() call can handle all requests if
135 * EFI_MEMORY_SP support is disabled.
136 */
137 if (!efi_soft_reserve_enabled())
138 return;
139
140 if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
141 return;
142
143 /*
144 * Given that efi_fake_memmap() needs to perform memblock
145 * allocations it needs to run after e820__memblock_setup().
146 * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given
147 * address range that potentially needs to mark the memory as
148 * reserved prior to e820__memblock_setup(). Update e820
149 * directly if EFI_MEMORY_SP is specified for an
150 * EFI_CONVENTIONAL_MEMORY descriptor.
151 */
152 for (i = 0; i < nr_fake_mem; i++) {
153 struct efi_mem_range *mem = &efi_fake_mems[i];
154 efi_memory_desc_t *md;
155 u64 m_start, m_end;
156
157 if ((mem->attribute & EFI_MEMORY_SP) == 0)
158 continue;
159
160 m_start = mem->range.start;
161 m_end = mem->range.end;
162 for_each_efi_memory_desc(md) {
163 u64 start, end, size;
164
165 if (md->type != EFI_CONVENTIONAL_MEMORY)
166 continue;
167
168 start = md->phys_addr;
169 end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
170
171 if (m_start <= end && m_end >= start)
172 /* fake range overlaps descriptor */;
173 else
174 continue;
175
176 /*
177 * Trim the boundary of the e820 update to the
178 * descriptor in case the fake range overlaps
179 * !EFI_CONVENTIONAL_MEMORY
180 */
181 start = max(start, m_start);
182 end = min(end, m_end);
183 size = end - start + 1;
184
185 if (end <= start)
186 continue;
187
188 /*
189 * Ensure each efi_fake_mem instance results in
190 * a unique e820 resource
191 */
192 e820__range_remove(start, size, old_type: E820_TYPE_RAM, check_type: 1);
193 e820__range_add(start, size, type: E820_TYPE_SOFT_RESERVED);
194 e820__update_table(table: e820_table);
195 }
196 }
197}
198

source code of linux/arch/x86/platform/efi/fake_mem.c