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