1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * save_prev_bl_data - saving previous bootloader data 4 * to environment variables. 5 * 6 * Copyright (c) 2022 Dzmitry Sankouski (dsankouski@gmail.com) 7 */ 8#include <init.h> 9#include <env.h> 10#include <fdtdec.h> 11#include <fdt_support.h> 12#include <fdt.h> 13#include <common.h> 14#include <linux/errno.h> 15#include <asm/system.h> 16#include <asm/armv8/mmu.h> 17 18static ulong reg0 __section(".data"); 19 20/** 21 * Save x0 register value, assuming previous bootloader set it to 22 * point on loaded fdt or (for older linux kernels)atags. 23 */ 24void save_boot_params(ulong r0) 25{ 26 reg0 = r0; 27 save_boot_params_ret(); 28} 29 30bool is_addr_accessible(phys_addr_t addr) 31{ 32 struct mm_region *mem = mem_map; 33 phys_addr_t bank_start; 34 phys_addr_t bank_end; 35 36 while (mem->size) { 37 bank_start = mem->phys; 38 bank_end = bank_start + mem->size; 39 debug("check if block %pap - %pap includes %pap\n", &bank_start, &bank_end, &addr); 40 if (addr > bank_start && addr < bank_end) 41 return true; 42 mem++; 43 } 44 45 return false; 46} 47 48phys_addr_t get_prev_bl_fdt_addr(void) 49{ 50 return reg0; 51} 52 53int save_prev_bl_data(void) 54{ 55 struct fdt_header *fdt_blob; 56 int node; 57 u64 initrd_start_prop; 58 59 if (!is_addr_accessible((phys_addr_t)reg0)) 60 return -ENODATA; 61 62 fdt_blob = (struct fdt_header *)reg0; 63 if (!fdt_valid(&fdt_blob)) { 64 pr_warn("%s: address 0x%lx is not a valid fdt\n", __func__, reg0); 65 return -ENODATA; 66 } 67 68 if (IS_ENABLED(CONFIG_SAVE_PREV_BL_FDT_ADDR)) 69 env_set_addr("prevbl_fdt_addr", (void *)reg0); 70 if (!IS_ENABLED(CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR)) 71 return 0; 72 73 node = fdt_path_offset(fdt_blob, "/chosen"); 74 if (!node) { 75 pr_warn("%s: chosen node not found in device tree at addr: 0x%lx\n", 76 __func__, reg0); 77 return -ENODATA; 78 } 79 /* 80 * linux,initrd-start property might be either 64 or 32 bit, 81 * depending on primary bootloader implementation. 82 */ 83 initrd_start_prop = fdtdec_get_uint64(fdt_blob, node, "linux,initrd-start", 0); 84 if (!initrd_start_prop) { 85 debug("%s: attempt to get uint64 linux,initrd-start property failed, trying uint\n", 86 __func__); 87 initrd_start_prop = fdtdec_get_uint(fdt_blob, node, "linux,initrd-start", 0); 88 if (!initrd_start_prop) { 89 debug("%s: attempt to get uint failed, too\n", __func__); 90 return -ENODATA; 91 } 92 } 93 env_set_addr("prevbl_initrd_start_addr", (void *)initrd_start_prop); 94 95 return 0; 96} 97