From ebb9492030235f2badcd528b6b2d829458cfa682 Mon Sep 17 00:00:00 2001 From: Alexey Sheplyakov Date: Mon, 23 May 2022 19:28:24 +0400 Subject: [PATCH 607/631] arm64-stub: fixed secondary cores boot on Baikal-M SoC Old versions of Baikal-M firmware (ARM-TF) deny execution attempts outside of the (physical) address ranges [0x80000000, 0x8FFFFFFF] and [0xA0000000, 0xBFFFFFFF] Thus PSCI calls to boot secondary cores fail unless the kernel image resides in one of these address ranges. However UEFI PE/COFF loader puts the kernel image into the forbidden range. Since the alignment is good enough EFI stub does not try to relocate the kernel. As a result secondary CPUs fail to boot. Relocation to a random address is not going to work either. Therefore automatically disable kaslr on "known bad" systems (for now only Baikal-M) and forcibly relocate the kernel to a low(er) address. This patch is necessary only for old firmware (pre SDK-M 5.1) and prevents kalsr from working on Baikal-M systems. Signed-off-by: Alexey Sheplyakov X-DONTUPSTREAM X-legacy X-feature-Baikal-M --- drivers/firmware/efi/libstub/arm64-stub.c | 62 ++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index f9de5217ea65..665a59a287dc 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "efistub.h" @@ -57,6 +58,32 @@ efi_status_t check_platform_features(void) return EFI_SUCCESS; } +static const char* machines_need_low_alloc[] = { + "baikal,baikal-m", + "baikal,bm1000", +}; + +static bool need_low_alloc(void) { + size_t i; + const void *fdt; + const char *match; + + fdt = get_efi_config_table(DEVICE_TREE_GUID); + if (!fdt) { + efi_info("failed to retrive FDT from EFI\n"); + return false; + } + + for (i = 0; i < ARRAY_SIZE(machines_need_low_alloc); i++) { + match = machines_need_low_alloc[i]; + if (fdt_node_check_compatible(fdt, 0, match) == 0) { + efi_info("machine %s: forcing kernel relocation to low address\n", match); + return true; + } + } + return false; +} + /* * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail * to provide space, and fail to zero it). Check for this condition by double @@ -93,6 +120,18 @@ static bool check_image_region(u64 base, u64 size) return ret; } +static inline efi_status_t efi_low_alloc(unsigned long size, unsigned long align, + unsigned long *addr) +{ + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. Skip the first 8 + * bytes so we start at a nice even number. + */ + return efi_low_alloc_above(size, align, addr, 0x8); +} + + efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, @@ -114,12 +153,21 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, */ u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN; + bool force_low_reloc = need_low_alloc(); + if (force_low_reloc) { + if (!efi_nokaslr) { + efi_info("booting on a broken firmware, KASLR will be disabled\n"); + efi_nokaslr = true; + } + } + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; void *p; if (efi_nokaslr) { - efi_info("KASLR disabled on kernel command line\n"); + if (!force_low_reloc) + efi_info("KASLR disabled on kernel command line\n"); } else if (efi_bs_call(handle_protocol, image_handle, &li_fixed_proto, &p) == EFI_SUCCESS) { efi_info("Image placement fixed by loader\n"); @@ -161,6 +209,15 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, status = EFI_OUT_OF_RESOURCES; } + if (force_low_reloc) { + status = efi_low_alloc(*reserve_size, + min_kimg_align, + reserve_addr); + if (status != EFI_SUCCESS) { + efi_err("Failed to relocate kernel, expect secondary CPUs boot failure\n"); + } + } + if (status != EFI_SUCCESS) { if (!check_image_region((u64)_text, kernel_memsize)) { efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n"); @@ -185,6 +242,9 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, } *image_addr = *reserve_addr; + if (efi_nokaslr) { + efi_info("relocating kernel to 0x%lx\n", *image_addr); + } memcpy((void *)*image_addr, _text, kernel_size); return EFI_SUCCESS; -- 2.35.2