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, &efi_range->range);
50
51	/* allocate memory for new EFI memmap */
52	if (efi_memmap_alloc(new_nr_map, &data) != 0)
53		return;
54
55	/* create new EFI memmap */
56	new_memmap = early_memremap(data.phys_map, data.size);
57	if (!new_memmap) {
58		__efi_memmap_free(data.phys_map, data.size, data.flags);
59		return;
60	}
61
62	efi_memmap_insert(&efi.memmap, new_memmap, efi_range);
63
64	/* swap into new EFI memmap */
65	early_memunmap(new_memmap, data.size);
66
67	efi_memmap_install(&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_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(p, &p);
94		if (*p == '@')
95			start = memparse(p+1, &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(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
117	     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, E820_TYPE_RAM, 1);
193			e820__range_add(start, size, E820_TYPE_SOFT_RESERVED);
194			e820__update_table(e820_table);
195		}
196	}
197}
198