Skip to content

Commit 199c847

Browse files
djbwrafaeljw
authored andcommitted
x86/efi: Add efi_fake_mem support for EFI_MEMORY_SP
Given that EFI_MEMORY_SP is platform BIOS policy decision for marking memory ranges as "reserved for a specific purpose" there will inevitably be scenarios where the BIOS omits the attribute in situations where it is desired. Unlike other attributes if the OS wants to reserve this memory from the kernel the reservation needs to happen early in init. So early, in fact, that it needs to happen before e820__memblock_setup() which is a pre-requisite for efi_fake_memmap() that wants to allocate memory for the updated table. Introduce an x86 specific efi_fake_memmap_early() that can search for attempts to set EFI_MEMORY_SP via efi_fake_mem and update the e820 table accordingly. The KASLR code that scans the command line looking for user-directed memory reservations also needs to be updated to consider "efi_fake_mem=nn@ss:0x40000" requests. Acked-by: Ard Biesheuvel <[email protected]> Reviewed-by: Dave Hansen <[email protected]> Signed-off-by: Dan Williams <[email protected]> Acked-by: Thomas Gleixner <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 16993c0 commit 199c847

File tree

8 files changed

+147
-23
lines changed

8 files changed

+147
-23
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,15 +1196,21 @@
11961196
updating original EFI memory map.
11971197
Region of memory which aa attribute is added to is
11981198
from ss to ss+nn.
1199+
11991200
If efi_fake_mem=2G@4G:0x10000,2G@0x10a0000000:0x10000
12001201
is specified, EFI_MEMORY_MORE_RELIABLE(0x10000)
12011202
attribute is added to range 0x100000000-0x180000000 and
12021203
0x10a0000000-0x1120000000.
12031204

1205+
If efi_fake_mem=8G@9G:0x40000 is specified, the
1206+
EFI_MEMORY_SP(0x40000) attribute is added to
1207+
range 0x240000000-0x43fffffff.
1208+
12041209
Using this parameter you can do debugging of EFI memmap
1205-
related feature. For example, you can do debugging of
1210+
related features. For example, you can do debugging of
12061211
Address Range Mirroring feature even if your box
1207-
doesn't support it.
1212+
doesn't support it, or mark specific memory as
1213+
"soft reserved".
12081214

12091215
efivar_ssdt= [EFI; X86] Name of an EFI variable that contains an SSDT
12101216
that is to be dynamically loaded by Linux. If there are

arch/x86/boot/compressed/kaslr.c

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,14 @@ char *skip_spaces(const char *str)
132132
#include "../../../../lib/ctype.c"
133133
#include "../../../../lib/cmdline.c"
134134

135+
enum parse_mode {
136+
PARSE_MEMMAP,
137+
PARSE_EFI,
138+
};
139+
135140
static int
136-
parse_memmap(char *p, unsigned long long *start, unsigned long long *size)
141+
parse_memmap(char *p, unsigned long long *start, unsigned long long *size,
142+
enum parse_mode mode)
137143
{
138144
char *oldp;
139145

@@ -156,8 +162,29 @@ parse_memmap(char *p, unsigned long long *start, unsigned long long *size)
156162
*start = memparse(p + 1, &p);
157163
return 0;
158164
case '@':
159-
/* memmap=nn@ss specifies usable region, should be skipped */
160-
*size = 0;
165+
if (mode == PARSE_MEMMAP) {
166+
/*
167+
* memmap=nn@ss specifies usable region, should
168+
* be skipped
169+
*/
170+
*size = 0;
171+
} else {
172+
unsigned long long flags;
173+
174+
/*
175+
* efi_fake_mem=nn@ss:attr the attr specifies
176+
* flags that might imply a soft-reservation.
177+
*/
178+
*start = memparse(p + 1, &p);
179+
if (p && *p == ':') {
180+
p++;
181+
if (kstrtoull(p, 0, &flags) < 0)
182+
*size = 0;
183+
else if (flags & EFI_MEMORY_SP)
184+
return 0;
185+
}
186+
*size = 0;
187+
}
161188
/* Fall through */
162189
default:
163190
/*
@@ -172,7 +199,7 @@ parse_memmap(char *p, unsigned long long *start, unsigned long long *size)
172199
return -EINVAL;
173200
}
174201

175-
static void mem_avoid_memmap(char *str)
202+
static void mem_avoid_memmap(enum parse_mode mode, char *str)
176203
{
177204
static int i;
178205

@@ -187,7 +214,7 @@ static void mem_avoid_memmap(char *str)
187214
if (k)
188215
*k++ = 0;
189216

190-
rc = parse_memmap(str, &start, &size);
217+
rc = parse_memmap(str, &start, &size, mode);
191218
if (rc < 0)
192219
break;
193220
str = k;
@@ -238,7 +265,6 @@ static void parse_gb_huge_pages(char *param, char *val)
238265
}
239266
}
240267

241-
242268
static void handle_mem_options(void)
243269
{
244270
char *args = (char *)get_cmd_line_ptr();
@@ -271,7 +297,7 @@ static void handle_mem_options(void)
271297
}
272298

273299
if (!strcmp(param, "memmap")) {
274-
mem_avoid_memmap(val);
300+
mem_avoid_memmap(PARSE_MEMMAP, val);
275301
} else if (strstr(param, "hugepages")) {
276302
parse_gb_huge_pages(param, val);
277303
} else if (!strcmp(param, "mem")) {
@@ -284,6 +310,8 @@ static void handle_mem_options(void)
284310
goto out;
285311

286312
mem_limit = mem_size;
313+
} else if (!strcmp(param, "efi_fake_mem")) {
314+
mem_avoid_memmap(PARSE_EFI, val);
287315
}
288316
}
289317

arch/x86/include/asm/efi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,12 @@ static inline void efi_reserve_boot_services(void)
263263
}
264264
#endif /* CONFIG_EFI */
265265

266+
#ifdef CONFIG_EFI_FAKE_MEMMAP
267+
extern void __init efi_fake_memmap_early(void);
268+
#else
269+
static inline void efi_fake_memmap_early(void)
270+
{
271+
}
272+
#endif
273+
266274
#endif /* _ASM_X86_EFI_H */

arch/x86/platform/efi/efi.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ int __init efi_memblock_x86_reserve_range(void)
262262
if (add_efi_memmap || do_efi_soft_reserve())
263263
do_add_efi_memmap();
264264

265+
efi_fake_memmap_early();
266+
265267
WARN(efi.memmap.desc_version != 1,
266268
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
267269
efi.memmap.desc_version);

drivers/firmware/efi/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ obj-$(CONFIG_UEFI_CPER) += cper.o
2020
obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
2121
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
2222
obj-$(CONFIG_EFI_STUB) += libstub/
23-
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
23+
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_map.o
2424
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
2525
obj-$(CONFIG_EFI_TEST) += test/
2626
obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
2727
obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o
2828
obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o
2929

30+
fake_map-y += fake_mem.o
31+
fake_map-$(CONFIG_X86) += x86_fake_mem.o
32+
3033
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
3134
obj-$(CONFIG_ARM) += $(arm-obj-y)
3235
obj-$(CONFIG_ARM64) += $(arm-obj-y)

drivers/firmware/efi/fake_mem.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@
1717
#include <linux/memblock.h>
1818
#include <linux/types.h>
1919
#include <linux/sort.h>
20-
#include <asm/efi.h>
20+
#include "fake_mem.h"
2121

22-
#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
23-
24-
static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM];
25-
static int nr_fake_mem;
22+
struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM];
23+
int nr_fake_mem;
2624

2725
static int __init cmp_fake_mem(const void *x1, const void *x2)
2826
{
@@ -50,7 +48,7 @@ void __init efi_fake_memmap(void)
5048
/* count up the number of EFI memory descriptor */
5149
for (i = 0; i < nr_fake_mem; i++) {
5250
for_each_efi_memory_desc(md) {
53-
struct range *r = &fake_mems[i].range;
51+
struct range *r = &efi_fake_mems[i].range;
5452

5553
new_nr_map += efi_memmap_split_count(md, r);
5654
}
@@ -70,7 +68,7 @@ void __init efi_fake_memmap(void)
7068
}
7169

7270
for (i = 0; i < nr_fake_mem; i++)
73-
efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]);
71+
efi_memmap_insert(&efi.memmap, new_memmap, &efi_fake_mems[i]);
7472

7573
/* swap into new EFI memmap */
7674
early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);
@@ -104,22 +102,22 @@ static int __init setup_fake_mem(char *p)
104102
if (nr_fake_mem >= EFI_MAX_FAKEMEM)
105103
break;
106104

107-
fake_mems[nr_fake_mem].range.start = start;
108-
fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
109-
fake_mems[nr_fake_mem].attribute = attribute;
105+
efi_fake_mems[nr_fake_mem].range.start = start;
106+
efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
107+
efi_fake_mems[nr_fake_mem].attribute = attribute;
110108
nr_fake_mem++;
111109

112110
if (*p == ',')
113111
p++;
114112
}
115113

116-
sort(fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
114+
sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
117115
cmp_fake_mem, NULL);
118116

119117
for (i = 0; i < nr_fake_mem; i++)
120118
pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
121-
fake_mems[i].attribute, fake_mems[i].range.start,
122-
fake_mems[i].range.end);
119+
efi_fake_mems[i].attribute, efi_fake_mems[i].range.start,
120+
efi_fake_mems[i].range.end);
123121

124122
return *p == '\0' ? 0 : -EINVAL;
125123
}

drivers/firmware/efi/fake_mem.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __EFI_FAKE_MEM_H__
3+
#define __EFI_FAKE_MEM_H__
4+
#include <asm/efi.h>
5+
6+
#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
7+
8+
extern struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM];
9+
extern int nr_fake_mem;
10+
#endif /* __EFI_FAKE_MEM_H__ */

drivers/firmware/efi/x86_fake_mem.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright(c) 2019 Intel Corporation. All rights reserved. */
3+
#include <linux/efi.h>
4+
#include <asm/e820/api.h>
5+
#include "fake_mem.h"
6+
7+
void __init efi_fake_memmap_early(void)
8+
{
9+
int i;
10+
11+
/*
12+
* The late efi_fake_mem() call can handle all requests if
13+
* EFI_MEMORY_SP support is disabled.
14+
*/
15+
if (!efi_soft_reserve_enabled())
16+
return;
17+
18+
if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
19+
return;
20+
21+
/*
22+
* Given that efi_fake_memmap() needs to perform memblock
23+
* allocations it needs to run after e820__memblock_setup().
24+
* However, if efi_fake_mem specifies EFI_MEMORY_SP for a given
25+
* address range that potentially needs to mark the memory as
26+
* reserved prior to e820__memblock_setup(). Update e820
27+
* directly if EFI_MEMORY_SP is specified for an
28+
* EFI_CONVENTIONAL_MEMORY descriptor.
29+
*/
30+
for (i = 0; i < nr_fake_mem; i++) {
31+
struct efi_mem_range *mem = &efi_fake_mems[i];
32+
efi_memory_desc_t *md;
33+
u64 m_start, m_end;
34+
35+
if ((mem->attribute & EFI_MEMORY_SP) == 0)
36+
continue;
37+
38+
m_start = mem->range.start;
39+
m_end = mem->range.end;
40+
for_each_efi_memory_desc(md) {
41+
u64 start, end;
42+
43+
if (md->type != EFI_CONVENTIONAL_MEMORY)
44+
continue;
45+
46+
start = md->phys_addr;
47+
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
48+
49+
if (m_start <= end && m_end >= start)
50+
/* fake range overlaps descriptor */;
51+
else
52+
continue;
53+
54+
/*
55+
* Trim the boundary of the e820 update to the
56+
* descriptor in case the fake range overlaps
57+
* !EFI_CONVENTIONAL_MEMORY
58+
*/
59+
start = max(start, m_start);
60+
end = min(end, m_end);
61+
62+
if (end <= start)
63+
continue;
64+
e820__range_update(start, end - start + 1, E820_TYPE_RAM,
65+
E820_TYPE_SOFT_RESERVED);
66+
e820__update_table(e820_table);
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)