mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 00:11:51 +01:00 
			
		
		
		
	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>
		
			
				
	
	
		
			92 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			92 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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;
 | |
| }
 |