From 868a4a3d3aa09f2090b50be4ad18d3f623b60acd Mon Sep 17 00:00:00 2001 From: Alexey Sheplyakov Date: Tue, 10 Nov 2020 19:05:39 +0400 Subject: [PATCH 623/634] 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. X-DONTUPSTREAM X-legacy --- 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 9cc556013..5486a223a 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" @@ -34,6 +35,31 @@ efi_status_t check_platform_features(void) return EFI_SUCCESS; } +static const char* machines_need_low_alloc[] = { + "baikal,baikal-m", +}; + +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 @@ -79,6 +105,19 @@ 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, @@ -99,6 +138,14 @@ 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)) { if (!efi_nokaslr) { status = efi_get_random_bytes(sizeof(phys_seed), @@ -112,7 +159,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_nokaslr = true; } } else { - efi_info("KASLR disabled on kernel command line\n"); + if (!force_low_reloc) + efi_info("KASLR disabled on kernel command line\n"); } } @@ -140,6 +188,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"); @@ -164,6 +221,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.33.2