From 8163faf952c467b01758b8a9a0dc8538e487aa9a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 3 Jan 2018 14:31:51 -0700 Subject: [PATCH] ARMv8: add optional Linux kernel image header Allow placing a Linux kernel image header at the start of the U-Boot binary. This is useful since the image header reports the amount of memory (BSS and similar) that U-Boot needs to use, but that isn't part of the binary size. This can be used by the code that loads U-Boot into memory to determine where to load U-Boot, based on other users of memory. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren --- arch/arm/Kconfig | 17 +++++ .../arm/cpu/armv8/linux-kernel-image-header-vars.h | 86 ++++++++++++++++++++++ arch/arm/cpu/armv8/start.S | 4 +- arch/arm/cpu/armv8/u-boot.lds | 4 + arch/arm/include/asm/boot0-linux-kernel-header.h | 49 ++++++++++++ 5 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/armv8/linux-kernel-image-header-vars.h create mode 100644 arch/arm/include/asm/boot0-linux-kernel-header.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c15acdf..001ece3 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -32,6 +32,23 @@ config SYS_INIT_SP_BSS_OFFSET calculate the stack pointer. This offset should be large enough so that the early malloc region, global data (gd), and early stack usage do not overlap any appended DTB. + +config LINUX_KERNEL_IMAGE_HEADER + bool + help + Place a Linux kernel image header at the start of the U-Boot binary. + The format of the header is described in the Linux kernel source at + Documentation/arm64/booting.txt. This feature is useful since the + image header reports the amount of memory (BSS and similar) that + U-Boot needs to use, but which isn't part of the binary. + +if LINUX_KERNEL_IMAGE_HEADER +config LNX_KRNL_IMG_TEXT_OFFSET_BASE + hex + help + The value subtracted from CONFIG_SYS_TEXT_BASE to calculate the + TEXT_OFFSET value written in to the Linux kernel image header. +endif endif config STATIC_RELA diff --git a/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h b/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h new file mode 100644 index 0000000..3e72093 --- /dev/null +++ b/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h @@ -0,0 +1,86 @@ +/* + * (C) Copyright 2017 NVIDIA Corporation + * + * Derived from Linux kernel v4.14 files: + * + * arch/arm64/include/asm/assembler.h: + * Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S + * Copyright (C) 1996-2000 Russell King + * Copyright (C) 2012 ARM Ltd. + * + * arch/arm64/kernel/head.S: + * Based on arch/arm/kernel/head.S + * Copyright (C) 1994-2002 Russell King + * Copyright (C) 2003-2012 ARM Ltd. + * Authors: Catalin Marinas + * Will Deacon + * + * arch/arm64/kernel/image.h: + * Copyright (C) 2014 ARM Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * There aren't any ELF relocations we can use to endian-swap values known only + * at link time (e.g. the subtraction of two symbol addresses), so we must get + * the linker to endian-swap certain values before emitting them. + * + * Note that, in order for this to work when building the ELF64 PIE executable + * (for KASLR), these values should not be referenced via R_AARCH64_ABS64 + * relocations, since these are fixed up at runtime rather than at build time + * when PIE is in effect. So we need to split them up in 32-bit high and low + * words. + */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define DATA_LE32(data) \ + ((((data) & 0x000000ff) << 24) | \ + (((data) & 0x0000ff00) << 8) | \ + (((data) & 0x00ff0000) >> 8) | \ + (((data) & 0xff000000) >> 24)) +#else +#define DATA_LE32(data) ((data) & 0xffffffff) +#endif + +#define DEFINE_IMAGE_LE64(sym, data) \ + sym##_lo32 = DATA_LE32((data) & 0xffffffff); \ + sym##_hi32 = DATA_LE32((data) >> 32) + +#define __MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define __CODE_DATA_SIZE (__bss_start - _start) +#define __BSS_SIZE (__bss_end - __bss_start) +#ifdef CONFIG_SYS_INIT_SP_BSS_OFFSET +#define __MAX_EXTRA_RAM_USAGE __MAX(__BSS_SIZE, CONFIG_SYS_INIT_SP_BSS_OFFSET) +#else +#define __MAX_EXTRA_RAM_USAGE __BSS_SIZE +#endif +#define __MEM_USAGE (__CODE_DATA_SIZE + __MAX_EXTRA_RAM_USAGE) + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define __HEAD_FLAG_BE 1 +#else +#define __HEAD_FLAG_BE 0 +#endif + +#define __HEAD_FLAG_PAGE_SIZE 1 /* 4K hard-coded */ + +#define __HEAD_FLAG_PHYS_BASE 1 + +#define __HEAD_FLAGS ((__HEAD_FLAG_BE << 0) | \ + (__HEAD_FLAG_PAGE_SIZE << 1) | \ + (__HEAD_FLAG_PHYS_BASE << 3)) + +#define TEXT_OFFSET (CONFIG_SYS_TEXT_BASE - \ + CONFIG_LNX_KRNL_IMG_TEXT_OFFSET_BASE) + +/* + * These will output as part of the Image header, which should be little-endian + * regardless of the endianness of the kernel. While constant values could be + * endian swapped in head.S, all are done here for consistency. + */ +#define HEAD_SYMBOLS \ + DEFINE_IMAGE_LE64(_kernel_size_le, __MEM_USAGE); \ + DEFINE_IMAGE_LE64(_kernel_offset_le, TEXT_OFFSET); \ + DEFINE_IMAGE_LE64(_kernel_flags_le, __HEAD_FLAGS); + + HEAD_SYMBOLS diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S index f385ed4..7a98a1c 100644 --- a/arch/arm/cpu/armv8/start.S +++ b/arch/arm/cpu/armv8/start.S @@ -19,7 +19,9 @@ .globl _start _start: -#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK +#if defined(LINUX_KERNEL_IMAGE_HEADER) +#include +#elif defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) /* * Various SoCs need something special and SoC-specific up front in * order to boot, allow them to set that in their boot0.h file and then diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 22195b8..7b76e0f 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -159,4 +159,8 @@ SECTIONS /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } + +#ifdef CONFIG_LINUX_KERNEL_IMAGE_HEADER +#include "linux-kernel-image-header-vars.h" +#endif } diff --git a/arch/arm/include/asm/boot0-linux-kernel-header.h b/arch/arm/include/asm/boot0-linux-kernel-header.h new file mode 100644 index 0000000..ca28780 --- /dev/null +++ b/arch/arm/include/asm/boot0-linux-kernel-header.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2017 NVIDIA Corporation + * + * Derived from Linux kernel v4.14 files: + * + * arch/arm64/include/asm/assembler.h: + * Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S + * Copyright (C) 1996-2000 Russell King + * Copyright (C) 2012 ARM Ltd. + * + * arch/arm64/kernel/head.S: + * Based on arch/arm/kernel/head.S + * Copyright (C) 1994-2002 Russell King + * Copyright (C) 2003-2012 ARM Ltd. + * Authors: Catalin Marinas + * Will Deacon + * + * arch/arm64/kernel/image.h: + * Copyright (C) 2014 ARM Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ + + /* + * Emit a 64-bit absolute little endian symbol reference in a way that + * ensures that it will be resolved at build time, even when building a + * PIE binary. This requires cooperation from the linker script, which + * must emit the lo32/hi32 halves individually. + */ + .macro le64sym, sym + .long \sym\()_lo32 + .long \sym\()_hi32 + .endm + +.globl _start +_start: + /* + * DO NOT MODIFY. Image header expected by Linux boot-loaders. + */ + b reset /* branch to kernel start, magic */ + .long 0 /* reserved */ + le64sym _kernel_offset_le /* Image load offset from start of RAM, little-endian */ + le64sym _kernel_size_le /* Effective size of kernel image, little-endian */ + le64sym _kernel_flags_le /* Informative flags, little-endian */ + .quad 0 /* reserved */ + .quad 0 /* reserved */ + .quad 0 /* reserved */ + .ascii "ARM\x64" /* Magic number */ + .long 0 /* reserved */