From 12a3e1ada06c702c403cf1eebb063ef7a34688cd Mon Sep 17 00:00:00 2001 From: Dzmitry Sankouski Date: Tue, 22 Feb 2022 21:49:52 +0300 Subject: [PATCH] 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 Cc: Tom Rini --- arch/arm/lib/Makefile | 5 ++ arch/arm/lib/save_prev_bl_data.c | 91 ++++++++++++++++++++++++++++++++ boot/Kconfig | 24 +++++++++ common/board_r.c | 5 ++ include/init.h | 13 +++++ 5 files changed, 138 insertions(+) create mode 100644 arch/arm/lib/save_prev_bl_data.c 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 * -- 2.39.5