Skip to content

Commit 2053bc5

Browse files
kirylbp3tk0v
authored andcommitted
efi: Add unaccepted memory support
efi_config_parse_tables() reserves memory that holds unaccepted memory configuration table so it won't be reused by page allocator. Core-mm requires few helpers to support unaccepted memory: - accept_memory() checks the range of addresses against the bitmap and accept memory if needed. - range_contains_unaccepted_memory() checks if anything within the range requires acceptance. Architectural code has to provide efi_get_unaccepted_table() that returns pointer to the unaccepted memory configuration table. arch_accept_memory() handles arch-specific part of memory acceptance. Signed-off-by: Kirill A. Shutemov <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Ard Biesheuvel <[email protected]> Reviewed-by: Tom Lendacky <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 3fd1239 commit 2053bc5

File tree

5 files changed

+142
-0
lines changed

5 files changed

+142
-0
lines changed

arch/x86/platform/efi/efi.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ static const unsigned long * const efi_tables[] = {
9696
#ifdef CONFIG_EFI_COCO_SECRET
9797
&efi.coco_secret,
9898
#endif
99+
#ifdef CONFIG_UNACCEPTED_MEMORY
100+
&efi.unaccepted,
101+
#endif
99102
};
100103

101104
u64 efi_setup; /* efi setup_data physical address */

drivers/firmware/efi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
4141
obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
4242
obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
4343
obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
44+
obj-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o

drivers/firmware/efi/efi.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ struct efi __read_mostly efi = {
5050
#ifdef CONFIG_EFI_COCO_SECRET
5151
.coco_secret = EFI_INVALID_TABLE_ADDR,
5252
#endif
53+
#ifdef CONFIG_UNACCEPTED_MEMORY
54+
.unaccepted = EFI_INVALID_TABLE_ADDR,
55+
#endif
5356
};
5457
EXPORT_SYMBOL(efi);
5558

@@ -605,6 +608,9 @@ static const efi_config_table_type_t common_tables[] __initconst = {
605608
#ifdef CONFIG_EFI_COCO_SECRET
606609
{LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" },
607610
#endif
611+
#ifdef CONFIG_UNACCEPTED_MEMORY
612+
{LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" },
613+
#endif
608614
#ifdef CONFIG_EFI_GENERIC_STUB
609615
{LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table },
610616
#endif
@@ -759,6 +765,25 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
759765
}
760766
}
761767

768+
if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) &&
769+
efi.unaccepted != EFI_INVALID_TABLE_ADDR) {
770+
struct efi_unaccepted_memory *unaccepted;
771+
772+
unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted));
773+
if (unaccepted) {
774+
unsigned long size;
775+
776+
if (unaccepted->version == 1) {
777+
size = sizeof(*unaccepted) + unaccepted->size;
778+
memblock_reserve(efi.unaccepted, size);
779+
} else {
780+
efi.unaccepted = EFI_INVALID_TABLE_ADDR;
781+
}
782+
783+
early_memunmap(unaccepted, sizeof(*unaccepted));
784+
}
785+
}
786+
762787
return 0;
763788
}
764789

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/efi.h>
4+
#include <linux/memblock.h>
5+
#include <linux/spinlock.h>
6+
#include <asm/unaccepted_memory.h>
7+
8+
/* Protects unaccepted memory bitmap */
9+
static DEFINE_SPINLOCK(unaccepted_memory_lock);
10+
11+
/*
12+
* accept_memory() -- Consult bitmap and accept the memory if needed.
13+
*
14+
* Only memory that is explicitly marked as unaccepted in the bitmap requires
15+
* an action. All the remaining memory is implicitly accepted and doesn't need
16+
* acceptance.
17+
*
18+
* No need to accept:
19+
* - anything if the system has no unaccepted table;
20+
* - memory that is below phys_base;
21+
* - memory that is above the memory that addressable by the bitmap;
22+
*/
23+
void accept_memory(phys_addr_t start, phys_addr_t end)
24+
{
25+
struct efi_unaccepted_memory *unaccepted;
26+
unsigned long range_start, range_end;
27+
unsigned long flags;
28+
u64 unit_size;
29+
30+
unaccepted = efi_get_unaccepted_table();
31+
if (!unaccepted)
32+
return;
33+
34+
unit_size = unaccepted->unit_size;
35+
36+
/*
37+
* Only care for the part of the range that is represented
38+
* in the bitmap.
39+
*/
40+
if (start < unaccepted->phys_base)
41+
start = unaccepted->phys_base;
42+
if (end < unaccepted->phys_base)
43+
return;
44+
45+
/* Translate to offsets from the beginning of the bitmap */
46+
start -= unaccepted->phys_base;
47+
end -= unaccepted->phys_base;
48+
49+
/* Make sure not to overrun the bitmap */
50+
if (end > unaccepted->size * unit_size * BITS_PER_BYTE)
51+
end = unaccepted->size * unit_size * BITS_PER_BYTE;
52+
53+
range_start = start / unit_size;
54+
55+
spin_lock_irqsave(&unaccepted_memory_lock, flags);
56+
for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap,
57+
DIV_ROUND_UP(end, unit_size)) {
58+
unsigned long phys_start, phys_end;
59+
unsigned long len = range_end - range_start;
60+
61+
phys_start = range_start * unit_size + unaccepted->phys_base;
62+
phys_end = range_end * unit_size + unaccepted->phys_base;
63+
64+
arch_accept_memory(phys_start, phys_end);
65+
bitmap_clear(unaccepted->bitmap, range_start, len);
66+
}
67+
spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
68+
}
69+
70+
bool range_contains_unaccepted_memory(phys_addr_t start, phys_addr_t end)
71+
{
72+
struct efi_unaccepted_memory *unaccepted;
73+
unsigned long flags;
74+
bool ret = false;
75+
u64 unit_size;
76+
77+
unaccepted = efi_get_unaccepted_table();
78+
if (!unaccepted)
79+
return false;
80+
81+
unit_size = unaccepted->unit_size;
82+
83+
/*
84+
* Only care for the part of the range that is represented
85+
* in the bitmap.
86+
*/
87+
if (start < unaccepted->phys_base)
88+
start = unaccepted->phys_base;
89+
if (end < unaccepted->phys_base)
90+
return false;
91+
92+
/* Translate to offsets from the beginning of the bitmap */
93+
start -= unaccepted->phys_base;
94+
end -= unaccepted->phys_base;
95+
96+
/* Make sure not to overrun the bitmap */
97+
if (end > unaccepted->size * unit_size * BITS_PER_BYTE)
98+
end = unaccepted->size * unit_size * BITS_PER_BYTE;
99+
100+
spin_lock_irqsave(&unaccepted_memory_lock, flags);
101+
while (start < end) {
102+
if (test_bit(start / unit_size, unaccepted->bitmap)) {
103+
ret = true;
104+
break;
105+
}
106+
107+
start += unit_size;
108+
}
109+
spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
110+
111+
return ret;
112+
}

include/linux/efi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ extern struct efi {
646646
unsigned long tpm_final_log; /* TPM2 Final Events Log table */
647647
unsigned long mokvar_table; /* MOK variable config table */
648648
unsigned long coco_secret; /* Confidential computing secret table */
649+
unsigned long unaccepted; /* Unaccepted memory table */
649650

650651
efi_get_time_t *get_time;
651652
efi_set_time_t *set_time;

0 commit comments

Comments
 (0)