1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 Fraunhofer AISEC,
4  * Lukas Auer <lukas.auer@aisec.fraunhofer.de>
5  *
6  * Based on common/spl/spl_atf.c
7  */
8 #include <cpu_func.h>
9 #include <errno.h>
10 #include <hang.h>
11 #include <image.h>
12 #include <spl.h>
13 #include <asm/global_data.h>
14 #include <asm/smp.h>
15 #include <opensbi.h>
16 #include <linux/libfdt.h>
17 #include <linux/printk.h>
18 #include <mapmem.h>
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 struct fw_dynamic_info opensbi_info;
23 
spl_opensbi_find_os_node(void * blob,int * uboot_node,int os_type)24 static int spl_opensbi_find_os_node(void *blob, int *uboot_node, int os_type)
25 {
26 	int fit_images_node, node;
27 	const char *fit_os;
28 
29 	fit_images_node = fdt_path_offset(blob, "/fit-images");
30 	if (fit_images_node < 0)
31 		return -ENODEV;
32 
33 	fdt_for_each_subnode(node, blob, fit_images_node) {
34 		fit_os = fdt_getprop(blob, node, FIT_OS_PROP, NULL);
35 		if (!fit_os)
36 			continue;
37 
38 		if (genimg_get_os_id(fit_os) == os_type) {
39 			*uboot_node = node;
40 			return 0;
41 		}
42 	}
43 
44 	return -ENODEV;
45 }
46 
spl_invoke_opensbi(struct spl_image_info * spl_image)47 void __noreturn spl_invoke_opensbi(struct spl_image_info *spl_image)
48 {
49 	int ret, os_node;
50 	ulong os_entry;
51 	int os_type;
52 	typedef void __noreturn (*opensbi_entry_t)(ulong hartid, ulong dtb, ulong info);
53 	opensbi_entry_t opensbi_entry;
54 
55 	if (!spl_image->fdt_addr) {
56 		pr_err("No device tree specified in SPL image\n");
57 		hang();
58 	}
59 
60 	if (!IS_ALIGNED((uintptr_t)spl_image->fdt_addr, 8)) {
61 		pr_err("SPL image loaded an improperly-aligned device tree\n");
62 		hang();
63 	}
64 
65 	/*
66 	 * Originally, u-boot-spl will place DTB directly after the kernel,
67 	 * but the size of the kernel did not include the BSS section, which
68 	 * means u-boot-spl will place the DTB in the kernel BSS section
69 	 * causing the DTB to be cleared by kernel BSS initializtion.
70 	 * Moving DTB in front of the kernel can avoid the error.
71 	 */
72 #if CONFIG_IS_ENABLED(LOAD_FIT_OPENSBI_OS_BOOT) && \
73     CONFIG_VAL(PAYLOAD_ARGS_ADDR)
74 	memcpy((void *)CONFIG_SPL_PAYLOAD_ARGS_ADDR, spl_image->fdt_addr,
75 	       fdt_totalsize(spl_image->fdt_addr));
76 	spl_image->fdt_addr = map_sysmem(CONFIG_SPL_PAYLOAD_ARGS_ADDR, 0);
77 #endif
78 
79 	/*
80 	 * Find next os image in /fit-images
81 	 * The next os image default is u-boot proper, once enable
82 	 * OpenSBI OS boot mode, the OS image should be linux.
83 	 */
84 	if (CONFIG_IS_ENABLED(LOAD_FIT_OPENSBI_OS_BOOT))
85 		os_type = IH_OS_LINUX;
86 	else
87 		os_type = IH_OS_U_BOOT;
88 
89 	ret = spl_opensbi_find_os_node(spl_image->fdt_addr, &os_node, os_type);
90 	if (ret) {
91 		pr_err("Can't find %s node for opensbi, %d\n",
92 		       genimg_get_os_name(os_type), ret);
93 		hang();
94 	}
95 
96 	/* Get U-Boot entry point */
97 	ret = fit_image_get_entry(spl_image->fdt_addr, os_node, &os_entry);
98 	if (ret)
99 		ret = fit_image_get_load(spl_image->fdt_addr, os_node, &os_entry);
100 
101 	/* Prepare opensbi_info object */
102 	opensbi_info.magic = FW_DYNAMIC_INFO_MAGIC_VALUE;
103 	opensbi_info.version = FW_DYNAMIC_INFO_VERSION;
104 	opensbi_info.next_addr = os_entry;
105 	opensbi_info.next_mode = FW_DYNAMIC_INFO_NEXT_MODE_S;
106 	opensbi_info.options = CONFIG_SPL_OPENSBI_SCRATCH_OPTIONS;
107 	opensbi_info.boot_hart = gd->arch.boot_hart;
108 
109 	opensbi_entry = (opensbi_entry_t)spl_image->entry_point;
110 	invalidate_icache_all();
111 
112 #ifdef CONFIG_SPL_SMP
113 	/*
114 	 * Start OpenSBI on all secondary harts and wait for acknowledgment.
115 	 *
116 	 * OpenSBI first relocates itself to its link address. This is done by
117 	 * the main hart. To make sure no hart is still running U-Boot SPL
118 	 * during relocation, we wait for all secondary harts to acknowledge
119 	 * the call-function request before entering OpenSBI on the main hart.
120 	 * Otherwise, code corruption can occur if the link address ranges of
121 	 * U-Boot SPL and OpenSBI overlap.
122 	 */
123 	ret = smp_call_function((ulong)spl_image->entry_point,
124 				(ulong)spl_image->fdt_addr,
125 				(ulong)&opensbi_info, 1);
126 	if (ret)
127 		hang();
128 #endif
129 	opensbi_entry(gd->arch.boot_hart, (ulong)spl_image->fdt_addr,
130 		      (ulong)&opensbi_info);
131 }
132