[PATCH] Reserve EBDA region

From: Gan Shun Lim
Date: Thu Dec 19 2019 - 19:57:22 EST


When we boot LinuxBoot-style (as a UEFI binary), sometimes UEFI does
not correctly mark the EBDA region as reserved. We reserve it in all
e820 maps to ensure both the Linuxboot kernel and the future kexec-ed
kernel do not corrupt it. This patch also adds a LinuxBoot config that
selects the RESERVE_EBDA automatically.

Signed-off-by: Gan Shun Lim <ganshun@xxxxxxxxxx>
---
arch/x86/Kconfig | 23 ++++++++++++++++++++
arch/x86/include/asm/e820/api.h | 1 +
arch/x86/kernel/e820.c | 38 +++++++++++++++++++++++++++++++++
arch/x86/kernel/setup.c | 4 ++++
4 files changed, 66 insertions(+)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5e89499536606..bf3bd0381df70 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2467,6 +2467,29 @@ config MODIFY_LDT_SYSCALL

source "kernel/livepatch/Kconfig"

+config LINUXBOOT
+ bool "LinuxBoot"
+ select KEXEC_CORE
+ select KEXEC
+ select KEXEC_FILE
+ select EFI
+ select EFI_STUB
+ select RESERVE_EBDA
+ help
+ Include code to handle booting directly out of firmware.
+ On EFI systems, this means booting with the EFI boot stub
+ as an EFI application, embedded in the firmware image. LinuxBoot
+ should then kexec the actual runtime operating system, and is not
+ meant to keep running.
+
+config RESERVE_EBDA
+ bool "Reserve the EBDA on boot"
+ help
+ The Extended BIOS Data Area can be left unreserved when booting
+ LinuxBoot as an EFI application. This unconditionally marks the
+ area as reserved for both current and kexec-ed kernels so that
+ it's not accidently corrupted on kexec.
+
endmenu

config ARCH_HAS_ADD_PAGES
diff --git a/arch/x86/include/asm/e820/api.h b/arch/x86/include/asm/e820/api.h
index e8f58ddd06d97..57c6bc2f78fda 100644
--- a/arch/x86/include/asm/e820/api.h
+++ b/arch/x86/include/asm/e820/api.h
@@ -29,6 +29,7 @@ extern u64 e820__memblock_alloc_reserved(u64 size, u64 align);
extern void e820__memblock_setup(void);

extern void e820__reserve_setup_data(void);
+extern void e820__reserve_ebda(void);
extern void e820__finish_early_params(void);
extern void e820__reserve_resources(void);
extern void e820__reserve_resources_late(void);
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index c5399e80c59c5..8401d3fd9b945 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -18,6 +18,7 @@
#include <linux/memory_hotplug.h>

#include <asm/e820/api.h>
+#include <asm/bios_ebda.h>
#include <asm/setup.h>

/*
@@ -523,6 +524,14 @@ static u64 __init e820__range_update_kexec(u64 start, u64 size, enum e820_type o
return __e820__range_update(e820_table_kexec, start, size, old_type, new_type);
}

+static u64 __init e820__range_update_firmware(u64 start, u64 size,
+ enum e820_type old_type,
+ enum e820_type new_type)
+{
+ return __e820__range_update(e820_table_firmware, start, size,
+ old_type, new_type);
+}
+
/* Remove a range of memory from the E820 table: */
u64 __init e820__range_remove(u64 start, u64 size, enum e820_type old_type, bool check_type)
{
@@ -1022,6 +1031,35 @@ void __init e820__reserve_setup_data(void)
e820__print_table("reserve setup_data");
}

+void __init e820__reserve_ebda(void)
+{
+ unsigned long address;
+ unsigned long start_page, new_length;
+ size_t length;
+
+ address = get_bios_ebda();
+ if (!address) {
+ pr_info("BIOS EBDA non-existent.\n");
+ return;
+ }
+ length = get_bios_ebda_length();
+ start_page = ALIGN_DOWN(address, PAGE_SIZE);
+ new_length = PAGE_ALIGN(address + length) - start_page;
+ pr_info("BIOS EBDA structure found at phys 0x%lx, length 0x%x\n",
+ address, (unsigned int)length);
+ pr_info("Reserving EBDA Page aligned starting at 0x%lx, length 0x%lx\n",
+ start_page, new_length);
+ // Reserve page aligned because memremap doesn't allow us to access
+ // mixed pages.
+ e820__range_update(start_page, new_length, E820_TYPE_RAM,
+ E820_TYPE_RESERVED);
+ // reserve only the actual ebda region to pass on.
+ e820__range_update_kexec(address, length, E820_TYPE_RAM,
+ E820_TYPE_RESERVED);
+ e820__range_update_firmware(address, length, E820_TYPE_RAM,
+ E820_TYPE_RESERVED);
+}
+
/*
* Called after parse_early_param(), after early parameters (such as mem=)
* have been processed, in which case we already have an E820 table filled in
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index cedfe2077a69b..06226d8222d13 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -1036,6 +1036,10 @@ void __init setup_arch(char **cmdline_p)
setup_clear_cpu_cap(X86_FEATURE_APIC);
}

+#ifdef CONFIG_LINUXBOOT_RESERVE_EBDA
+ /* update the e820_saved too */
+ e820__reserve_ebda();
+#endif
e820__reserve_setup_data();
e820__finish_early_params();

--
2.24.1.735.g03f4e72817-goog