diff options
author | Dzmitry Sankouski <dsankouski@gmail.com> | 2022-02-22 21:49:52 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-04-04 14:53:26 -0400 |
commit | 12a3e1ada06c702c403cf1eebb063ef7a34688cd (patch) | |
tree | d58972054d39b90b0b0a92d5c9b093d0547264a6 | |
parent | 5c9b420ada3b4f2a08da4789571f0ea373881565 (diff) | |
download | u-boot-12a3e1ada06c702c403cf1eebb063ef7a34688cd.tar.gz u-boot-12a3e1ada06c702c403cf1eebb063ef7a34688cd.tar.bz2 u-boot-12a3e1ada06c702c403cf1eebb063ef7a34688cd.zip |
arm: init: save previous bootloader data
When u-boot is used as a chain-loaded bootloader (replacing OS kernel),
previous bootloader leaves data in RAM, that can be reused.
For example, on recent arm linux system, when chainloading u-boot,
there are initramfs and fdt in RAM prepared for OS booting. Initramfs
may be modified to store u-boot's payload, thus providing the ability to
use chainloaded u-boot to boot OS without any storage support.
Two config options added:
- SAVE_PREV_BL_INITRAMFS_START_ADDR
saves initramfs start address to 'prevbl_initrd_start_addr' environment
variable
- SAVE_PREV_BL_FDT_ADDR
saves fdt address to 'prevbl_fdt_addr' environment variable
Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com>
Cc: Tom Rini <trini@konsulko.com>
-rw-r--r-- | arch/arm/lib/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/lib/save_prev_bl_data.c | 91 | ||||
-rw-r--r-- | boot/Kconfig | 24 | ||||
-rw-r--r-- | common/board_r.c | 5 | ||||
-rw-r--r-- | include/init.h | 13 |
5 files changed, 138 insertions, 0 deletions
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 594fc1228a..c603fe61bc 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -48,6 +48,11 @@ obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMCPY) += memcpy.o endif obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o +ifneq ($(filter y,$(CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR) $(CONFIG_SAVE_PREV_BL_FDT_ADDR)),) +obj-y += save_prev_bl_data.o +endif + +# obj-$(CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR) += save_prev_bl_data.o obj-y += bdinfo.o obj-y += sections.o CFLAGS_REMOVE_sections.o := $(LTO_CFLAGS) diff --git a/arch/arm/lib/save_prev_bl_data.c b/arch/arm/lib/save_prev_bl_data.c new file mode 100644 index 0000000000..f4ee86a89c --- /dev/null +++ b/arch/arm/lib/save_prev_bl_data.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * save_prev_bl_data - saving previous bootloader data + * to environment variables. + * + * Copyright (c) 2022 Dzmitry Sankouski (dsankouski@gmail.com) + */ +#include <init.h> +#include <env.h> +#include <fdtdec.h> +#include <fdt_support.h> +#include <fdt.h> +#include <common.h> +#include <linux/errno.h> +#include <asm/system.h> +#include <asm/armv8/mmu.h> + +static ulong reg0 __section(".data"); + +/** + * Save x0 register value, assuming previous bootloader set it to + * point on loaded fdt or (for older linux kernels)atags. + */ +void save_boot_params(ulong r0) +{ + reg0 = r0; + save_boot_params_ret(); +} + +bool is_addr_accessible(phys_addr_t addr) +{ + struct mm_region *mem = mem_map; + phys_addr_t bank_start; + phys_addr_t bank_end; + + while (mem->size) { + bank_start = mem->phys; + bank_end = bank_start + mem->size; + debug("check if block %pap - %pap includes %pap\n", &bank_start, &bank_end, &addr); + if (addr > bank_start && addr < bank_end) + return true; + mem++; + } + + return false; +} + +int save_prev_bl_data(void) +{ + struct fdt_header *fdt_blob; + int node; + u64 initrd_start_prop; + + if (!is_addr_accessible((phys_addr_t)reg0)) + return -ENODATA; + + fdt_blob = (struct fdt_header *)reg0; + if (!fdt_valid(&fdt_blob)) { + pr_warn("%s: address 0x%lx is not a valid fdt\n", __func__, reg0); + return -ENODATA; + } + + if (CONFIG_IS_ENABLED(SAVE_PREV_BL_FDT_ADDR)) + env_set_addr("prevbl_fdt_addr", (void *)reg0); + if (!CONFIG_IS_ENABLED(SAVE_PREV_BL_INITRAMFS_START_ADDR)) + return 0; + + node = fdt_path_offset(fdt_blob, "/chosen"); + if (!node) { + pr_warn("%s: chosen node not found in device tree at addr: 0x%lx\n", + __func__, reg0); + return -ENODATA; + } + /* + * linux,initrd-start property might be either 64 or 32 bit, + * depending on primary bootloader implementation. + */ + initrd_start_prop = fdtdec_get_uint64(fdt_blob, node, "linux,initrd-start", 0); + if (!initrd_start_prop) { + debug("%s: attempt to get uint64 linux,initrd-start property failed, trying uint\n", + __func__); + initrd_start_prop = fdtdec_get_uint(fdt_blob, node, "linux,initrd-start", 0); + if (!initrd_start_prop) { + debug("%s: attempt to get uint failed, too\n", __func__); + return -ENODATA; + } + } + env_set_addr("prevbl_initrd_start_addr", (void *)initrd_start_prop); + + return 0; +} diff --git a/boot/Kconfig b/boot/Kconfig index 394b26f246..ec5b956490 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -1192,4 +1192,28 @@ config DEFAULT_FDT_FILE help This option is used to set the default fdt file to boot OS. +config SAVE_PREV_BL_FDT_ADDR + depends on ARM + bool "Saves fdt address, passed by the previous bootloader, to env var" + help + When u-boot is used as a chain-loaded bootloader (replacing OS kernel), + enable this option to save fdt address, passed by the + previous bootloader for future use. + Address is saved to `prevbl_fdt_addr` environment variable. + + If no fdt was provided by previous bootloader, no env variables + will be created. + +config SAVE_PREV_BL_INITRAMFS_START_ADDR + depends on ARM + bool "Saves initramfs address, passed by the previous bootloader, to env var" + help + When u-boot is used as a chain-loaded bootloader(replacing OS kernel), + enable this option to save initramfs address, passed by the + previous bootloader for future use. + Address is saved to `prevbl_initrd_start_addr` environment variable. + + If no initramfs was provided by previous bootloader, no env variables + will be created. + endmenu # Booting diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..8dc87ed2be 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -445,6 +445,11 @@ static int initr_env(void) env_set_hex("fdtcontroladdr", (unsigned long)map_to_sysmem(gd->fdt_blob)); + #if (CONFIG_IS_ENABLED(SAVE_PREV_BL_INITRAMFS_START_ADDR) || \ + CONFIG_IS_ENABLED(SAVE_PREV_BL_FDT_ADDR)) + save_prev_bl_data(); + #endif + /* Initialize from environment */ image_load_addr = env_get_ulong("loadaddr", 16, image_load_addr); diff --git a/include/init.h b/include/init.h index 74496500d2..7b8f62c121 100644 --- a/include/init.h +++ b/include/init.h @@ -155,6 +155,19 @@ int arch_setup_bdinfo(void); */ int setup_bdinfo(void); +#if defined(CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR) || \ +defined(CONFIG_SAVE_PREV_BL_FDT_ADDR) +/** + * save_prev_bl_data - Save prev bl data in env vars. + * + * When u-boot is chain-loaded, save previous bootloader data, + * like initramfs address to environment variables. + * + * Return: 0 if ok; -ENODATA on error + */ +int save_prev_bl_data(void); +#endif + /** * cpu_secondary_init_r() - CPU-specific secondary initialization * |