Skip to content

Commit 3fd1239

Browse files
kirylbp3tk0v
authored andcommitted
x86/boot/compressed: Handle unaccepted memory
The firmware will pre-accept the memory used to run the stub. But, the stub is responsible for accepting the memory into which it decompresses the main kernel. Accept memory just before decompression starts. The stub is also responsible for choosing a physical address in which to place the decompressed kernel image. The KASLR mechanism will randomize this physical address. Since the accepted memory region is relatively small, KASLR would be quite ineffective if it only used the pre-accepted area (EFI_CONVENTIONAL_MEMORY). Ensure that KASLR randomizes among the entire physical address space by also including EFI_UNACCEPTED_MEMORY. Signed-off-by: Kirill A. Shutemov <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Liam Merwick <[email protected]> Reviewed-by: Tom Lendacky <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 745e3ed commit 3fd1239

File tree

5 files changed

+95
-12
lines changed

5 files changed

+95
-12
lines changed

arch/x86/boot/compressed/efi.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ typedef guid_t efi_guid_t __aligned(__alignof__(u32));
1616
#define ACPI_TABLE_GUID EFI_GUID(0xeb9d2d30, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
1717
#define ACPI_20_TABLE_GUID EFI_GUID(0x8868e871, 0xe4f1, 0x11d3, 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81)
1818
#define EFI_CC_BLOB_GUID EFI_GUID(0x067b1f5f, 0xcf26, 0x44c5, 0x85, 0x54, 0x93, 0xd7, 0x77, 0x91, 0x2d, 0x42)
19+
#define LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID EFI_GUID(0xd5d1de3c, 0x105c, 0x44f9, 0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31)
1920

2021
#define EFI32_LOADER_SIGNATURE "EL32"
2122
#define EFI64_LOADER_SIGNATURE "EL64"
@@ -32,6 +33,7 @@ typedef struct {
3233
} efi_table_hdr_t;
3334

3435
#define EFI_CONVENTIONAL_MEMORY 7
36+
#define EFI_UNACCEPTED_MEMORY 15
3537

3638
#define EFI_MEMORY_MORE_RELIABLE \
3739
((u64)0x0000000000010000ULL) /* higher reliability */
@@ -104,6 +106,14 @@ struct efi_setup_data {
104106
u64 reserved[8];
105107
};
106108

109+
struct efi_unaccepted_memory {
110+
u32 version;
111+
u32 unit_size;
112+
u64 phys_base;
113+
u64 size;
114+
unsigned long bitmap[];
115+
};
116+
107117
static inline int efi_guidcmp (efi_guid_t left, efi_guid_t right)
108118
{
109119
return memcmp(&left, &right, sizeof (efi_guid_t));

arch/x86/boot/compressed/kaslr.c

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,33 @@ static bool process_mem_region(struct mem_vector *region,
672672
}
673673

674674
#ifdef CONFIG_EFI
675+
676+
/*
677+
* Only EFI_CONVENTIONAL_MEMORY and EFI_UNACCEPTED_MEMORY (if supported) are
678+
* guaranteed to be free.
679+
*
680+
* Pick free memory more conservatively than the EFI spec allows: according to
681+
* the spec, EFI_BOOT_SERVICES_{CODE|DATA} are also free memory and thus
682+
* available to place the kernel image into, but in practice there's firmware
683+
* where using that memory leads to crashes. Buggy vendor EFI code registers
684+
* for an event that triggers on SetVirtualAddressMap(). The handler assumes
685+
* that EFI_BOOT_SERVICES_DATA memory has not been touched by loader yet, which
686+
* is probably true for Windows.
687+
*
688+
* Preserve EFI_BOOT_SERVICES_* regions until after SetVirtualAddressMap().
689+
*/
690+
static inline bool memory_type_is_free(efi_memory_desc_t *md)
691+
{
692+
if (md->type == EFI_CONVENTIONAL_MEMORY)
693+
return true;
694+
695+
if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) &&
696+
md->type == EFI_UNACCEPTED_MEMORY)
697+
return true;
698+
699+
return false;
700+
}
701+
675702
/*
676703
* Returns true if we processed the EFI memmap, which we prefer over the E820
677704
* table if it is available.
@@ -716,18 +743,7 @@ process_efi_entries(unsigned long minimum, unsigned long image_size)
716743
for (i = 0; i < nr_desc; i++) {
717744
md = efi_early_memdesc_ptr(pmap, e->efi_memdesc_size, i);
718745

719-
/*
720-
* Here we are more conservative in picking free memory than
721-
* the EFI spec allows:
722-
*
723-
* According to the spec, EFI_BOOT_SERVICES_{CODE|DATA} are also
724-
* free memory and thus available to place the kernel image into,
725-
* but in practice there's firmware where using that memory leads
726-
* to crashes.
727-
*
728-
* Only EFI_CONVENTIONAL_MEMORY is guaranteed to be free.
729-
*/
730-
if (md->type != EFI_CONVENTIONAL_MEMORY)
746+
if (!memory_type_is_free(md))
731747
continue;
732748

733749
if (efi_soft_reserve_enabled() &&

arch/x86/boot/compressed/mem.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,50 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22

33
#include "error.h"
4+
#include "misc.h"
45

56
void arch_accept_memory(phys_addr_t start, phys_addr_t end)
67
{
78
/* Platform-specific memory-acceptance call goes here */
89
error("Cannot accept memory");
910
}
11+
12+
bool init_unaccepted_memory(void)
13+
{
14+
guid_t guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID;
15+
struct efi_unaccepted_memory *table;
16+
unsigned long cfg_table_pa;
17+
unsigned int cfg_table_len;
18+
enum efi_type et;
19+
int ret;
20+
21+
et = efi_get_type(boot_params);
22+
if (et == EFI_TYPE_NONE)
23+
return false;
24+
25+
ret = efi_get_conf_table(boot_params, &cfg_table_pa, &cfg_table_len);
26+
if (ret) {
27+
warn("EFI config table not found.");
28+
return false;
29+
}
30+
31+
table = (void *)efi_find_vendor_table(boot_params, cfg_table_pa,
32+
cfg_table_len, guid);
33+
if (!table)
34+
return false;
35+
36+
if (table->version != 1)
37+
error("Unknown version of unaccepted memory table\n");
38+
39+
/*
40+
* In many cases unaccepted_table is already set by EFI stub, but it
41+
* has to be initialized again to cover cases when the table is not
42+
* allocated by EFI stub or EFI stub copied the kernel image with
43+
* efi_relocate_kernel() before the variable is set.
44+
*
45+
* It must be initialized before the first usage of accept_memory().
46+
*/
47+
unaccepted_table = table;
48+
49+
return true;
50+
}

arch/x86/boot/compressed/misc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,12 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
455455
#endif
456456

457457
debug_putstr("\nDecompressing Linux... ");
458+
459+
if (init_unaccepted_memory()) {
460+
debug_putstr("Accepting memory... ");
461+
accept_memory(__pa(output), __pa(output) + needed_size);
462+
}
463+
458464
__decompress(input_data, input_len, NULL, NULL, output, output_len,
459465
NULL, error);
460466
entry_offset = parse_elf(output);

arch/x86/boot/compressed/misc.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,14 @@ static inline unsigned long efi_find_vendor_table(struct boot_params *bp,
247247
}
248248
#endif /* CONFIG_EFI */
249249

250+
#ifdef CONFIG_UNACCEPTED_MEMORY
251+
bool init_unaccepted_memory(void);
252+
#else
253+
static inline bool init_unaccepted_memory(void) { return false; }
254+
#endif
255+
256+
/* Defined in EFI stub */
257+
extern struct efi_unaccepted_memory *unaccepted_table;
258+
void accept_memory(phys_addr_t start, phys_addr_t end);
259+
250260
#endif /* BOOT_COMPRESSED_MISC_H */

0 commit comments

Comments
 (0)