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>
This commit is contained in:
Dzmitry Sankouski 2022-02-22 21:49:52 +03:00 committed by Tom Rini
parent 5c9b420ada
commit 12a3e1ada0
5 changed files with 138 additions and 0 deletions

View file

@ -48,6 +48,11 @@ obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMCPY) += memcpy.o
endif endif
obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o 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 += bdinfo.o
obj-y += sections.o obj-y += sections.o
CFLAGS_REMOVE_sections.o := $(LTO_CFLAGS) CFLAGS_REMOVE_sections.o := $(LTO_CFLAGS)

View file

@ -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;
}

View file

@ -1192,4 +1192,28 @@ config DEFAULT_FDT_FILE
help help
This option is used to set the default fdt file to boot OS. 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 endmenu # Booting

View file

@ -445,6 +445,11 @@ static int initr_env(void)
env_set_hex("fdtcontroladdr", env_set_hex("fdtcontroladdr",
(unsigned long)map_to_sysmem(gd->fdt_blob)); (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 */ /* Initialize from environment */
image_load_addr = env_get_ulong("loadaddr", 16, image_load_addr); image_load_addr = env_get_ulong("loadaddr", 16, image_load_addr);

View file

@ -155,6 +155,19 @@ int arch_setup_bdinfo(void);
*/ */
int 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 * cpu_secondary_init_r() - CPU-specific secondary initialization
* *