[U-Boot] [PATCH 1/2] ARMv8: add optional Linux kernel image header

From: Stephen Warren swarren@nvidia.com
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 swarren@nvidia.com --- This series relies (at the very least for diff context) on the previous series I sent Dec 19: ARM: tegra: don't use CONFIG_SPL_TEXT_BASE when no SPL ... ARM: tegra: use CONFIG_SYS_INIT_SP_BSS_OFFSET.
arch/arm/Kconfig | 16 ++++ .../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, 158 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 93b93c142929..69f304b6d7e6 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -32,6 +32,22 @@ 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. + 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. + +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 000000000000..3e720937f012 --- /dev/null +++ b/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h @@ -0,0 +1,86 @@ +/* + * (C) Copyright 2017 NVIDIA Corporation <www.nvidia.com> + * + * 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 catalin.marinas@arm.com + * Will Deacon will.deacon@arm.com + * + * 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 03e744e4a673..7c6807f4e1fd 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 <asm/boot0-linux-kernel-header.h> +#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 22195b8834b5..7b76e0f9f08e 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 000000000000..ca28780daac5 --- /dev/null +++ b/arch/arm/include/asm/boot0-linux-kernel-header.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2017 NVIDIA Corporation <www.nvidia.com> + * + * 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 catalin.marinas@arm.com + * Will Deacon will.deacon@arm.com + * + * 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 */

From: Stephen Warren swarren@nvidia.com
Enable CONFIG_LINUX_KERNEL_IMAGE_HEADER for all 64-bit Tegra boards. cboot (the boot SW that runs before U-Boot) will eventually use this information.
Signed-off-by: Stephen Warren swarren@nvidia.com --- arch/arm/mach-tegra/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 51d143687b06..fd0082d22a33 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -60,8 +60,14 @@ config TEGRA_ARMV7_COMMON config TEGRA_ARMV8_COMMON bool "Tegra 64-bit common options" select ARM64 + select LINUX_KERNEL_IMAGE_HEADER select TEGRA_COMMON
+if TEGRA_ARMV8_COMMON +config LNX_KRNL_IMG_TEXT_OFFSET_BASE + default 0x80000000 +endif + choice prompt "Tegra SoC select" optional

Hi Stephen,
On 2 January 2018 at 16:54, Stephen Warren swarren@wwwdotorg.org wrote:
From: Stephen Warren swarren@nvidia.com
Enable CONFIG_LINUX_KERNEL_IMAGE_HEADER for all 64-bit Tegra boards. cboot (the boot SW that runs before U-Boot) will eventually use this information.
How does U-Boot use it? Does it not come from the FIT?
Signed-off-by: Stephen Warren swarren@nvidia.com
arch/arm/mach-tegra/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 51d143687b06..fd0082d22a33 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -60,8 +60,14 @@ config TEGRA_ARMV7_COMMON config TEGRA_ARMV8_COMMON bool "Tegra 64-bit common options" select ARM64
select LINUX_KERNEL_IMAGE_HEADER select TEGRA_COMMON
+if TEGRA_ARMV8_COMMON +config LNX_KRNL_IMG_TEXT_OFFSET_BASE
default 0x80000000
+endif
choice prompt "Tegra SoC select" optional -- 2.15.1
Regards, Simon

On 01/07/2018 09:50 PM, Simon Glass wrote:
Hi Stephen,
On 2 January 2018 at 16:54, Stephen Warren swarren@wwwdotorg.org wrote:
From: Stephen Warren swarren@nvidia.com
Enable CONFIG_LINUX_KERNEL_IMAGE_HEADER for all 64-bit Tegra boards. cboot (the boot SW that runs before U-Boot) will eventually use this information.
How does U-Boot use it? Does it not come from the FIT?
U-Boot doesn't use the header itself. Rather, the header provides information (e.g. BSS usage) to whatever SW is loading U-Boot into RAM. We don't use FIT at all; I'd rather our boot flow not know about bootloader-specific data formats.

On Tue, Jan 02, 2018 at 04:54:21PM -0700, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
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 swarren@nvidia.com
This series relies (at the very least for diff context) on the previous series I sent Dec 19: ARM: tegra: don't use CONFIG_SPL_TEXT_BASE when no SPL ... ARM: tegra: use CONFIG_SYS_INIT_SP_BSS_OFFSET.
Neat. Are the Linux folks encouraging other projects to re-use this header? Is there somewhere we can point to as reference for the format for the Kconfig help as well, not just the text? Thanks!

On 01/03/2018 06:45 AM, Tom Rini wrote:
On Tue, Jan 02, 2018 at 04:54:21PM -0700, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
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 swarren@nvidia.com
This series relies (at the very least for diff context) on the previous series I sent Dec 19: ARM: tegra: don't use CONFIG_SPL_TEXT_BASE when no SPL ... ARM: tegra: use CONFIG_SYS_INIT_SP_BSS_OFFSET.
Neat. Are the Linux folks encouraging other projects to re-use this header?
Not that I'm aware of, but mainly because I haven't talked to upstream kernel people about this at all. I implemented this because in recent versions of Linux4Tegra, for all 64-bit SoCs, our main CPU EL2/EL1 boot path is either:
cboot -> Linux kernel cboot -> U-Boot -> Linux kernel
cboot is an NVIDIA binary bootloader. cboot wants to know about BSS/... usage of the binary it loads, which can be determined from the Linux kernel image header, but we don't want cboot to take different paths when booting Linux or U-Boot (or indeed anything at all), so the simplest thing was to make U-Boot look as identical to a Linux kernel as possible.
Is there somewhere we can point to as reference for the format for the Kconfig help as well, not just the text? Thanks!
The Linux kernel file Documentation/arm64/booting.txt does describe the format. Should I respin and add that filename to the Kconfig text?

On Wed, Jan 03, 2018 at 10:42:56AM -0700, Stephen Warren wrote:
On 01/03/2018 06:45 AM, Tom Rini wrote:
On Tue, Jan 02, 2018 at 04:54:21PM -0700, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
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 swarren@nvidia.com
This series relies (at the very least for diff context) on the previous series I sent Dec 19: ARM: tegra: don't use CONFIG_SPL_TEXT_BASE when no SPL ... ARM: tegra: use CONFIG_SYS_INIT_SP_BSS_OFFSET.
Neat. Are the Linux folks encouraging other projects to re-use this header?
Not that I'm aware of, but mainly because I haven't talked to upstream kernel people about this at all. I implemented this because in recent versions of Linux4Tegra, for all 64-bit SoCs, our main CPU EL2/EL1 boot path is either:
cboot -> Linux kernel cboot -> U-Boot -> Linux kernel
cboot is an NVIDIA binary bootloader. cboot wants to know about BSS/... usage of the binary it loads, which can be determined from the Linux kernel image header, but we don't want cboot to take different paths when booting Linux or U-Boot (or indeed anything at all), so the simplest thing was to make U-Boot look as identical to a Linux kernel as possible.
Is there somewhere we can point to as reference for the format for the Kconfig help as well, not just the text? Thanks!
The Linux kernel file Documentation/arm64/booting.txt does describe the format. Should I respin and add that filename to the Kconfig text?
Yes, and I feel like it can't hurt to let the arm64 maintainers know too as frankly it's a good idea and more projects should make use of it, so maybe it should be a bit more formally documented (in one of those fancy new rst files?) or something.

On 01/03/2018 10:56 AM, Tom Rini wrote:
On Wed, Jan 03, 2018 at 10:42:56AM -0700, Stephen Warren wrote:
On 01/03/2018 06:45 AM, Tom Rini wrote:
On Tue, Jan 02, 2018 at 04:54:21PM -0700, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
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 swarren@nvidia.com
This series relies (at the very least for diff context) on the previous series I sent Dec 19: ARM: tegra: don't use CONFIG_SPL_TEXT_BASE when no SPL ... ARM: tegra: use CONFIG_SYS_INIT_SP_BSS_OFFSET.
Neat. Are the Linux folks encouraging other projects to re-use this header?
Not that I'm aware of, but mainly because I haven't talked to upstream kernel people about this at all. I implemented this because in recent versions of Linux4Tegra, for all 64-bit SoCs, our main CPU EL2/EL1 boot path is either:
cboot -> Linux kernel cboot -> U-Boot -> Linux kernel
cboot is an NVIDIA binary bootloader. cboot wants to know about BSS/... usage of the binary it loads, which can be determined from the Linux kernel image header, but we don't want cboot to take different paths when booting Linux or U-Boot (or indeed anything at all), so the simplest thing was to make U-Boot look as identical to a Linux kernel as possible.
Is there somewhere we can point to as reference for the format for the Kconfig help as well, not just the text? Thanks!
The Linux kernel file Documentation/arm64/booting.txt does describe the format. Should I respin and add that filename to the Kconfig text?
Yes, and I feel like it can't hurt to let the arm64 maintainers know too as frankly it's a good idea and more projects should make use of it, so maybe it should be a bit more formally documented (in one of those fancy new rst files?) or something.
I talked to Catalin Marinas (one of the arch/arm64 maintainers) on IRC and he said he didn't see any issue with this approach.
participants (3)
-
Simon Glass
-
Stephen Warren
-
Tom Rini