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