1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2008 Semihalf
4 *
5 * (C) Copyright 2000-2006
6 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7 */
8
9
10#include <common.h>
11#include <bootm.h>
12#include <bootstage.h>
13#include <cpu_func.h>
14#include <env.h>
15#include <init.h>
16#include <lmb.h>
17#include <log.h>
18#include <watchdog.h>
19#include <command.h>
20#include <image.h>
21#include <malloc.h>
22#include <asm/global_data.h>
23#include <u-boot/zlib.h>
24#include <bzlib.h>
25#include <asm/byteorder.h>
26#include <asm/mp.h>
27#include <bootm.h>
28#include <vxworks.h>
29
30#if defined(CONFIG_OF_LIBFDT)
31#include <linux/libfdt.h>
32#include <fdt_support.h>
33#endif
34
35#ifdef CONFIG_SYS_INIT_RAM_LOCK
36#include <asm/cache.h>
37#endif
38
39DECLARE_GLOBAL_DATA_PTR;
40
41static ulong get_sp (void);
42extern void ft_fixup_num_cores(void *blob);
43static void set_clocks_in_mhz (struct bd_info *kbd);
44
45#ifndef CFG_SYS_LINUX_LOWMEM_MAX_SIZE
46#define CFG_SYS_LINUX_LOWMEM_MAX_SIZE	(768*1024*1024)
47#endif
48
49static void boot_jump_linux(struct bootm_headers *images)
50{
51	void	(*kernel)(struct bd_info *, ulong r4, ulong r5, ulong r6,
52			      ulong r7, ulong r8, ulong r9);
53#ifdef CONFIG_OF_LIBFDT
54	char *of_flat_tree = images->ft_addr;
55#endif
56
57	kernel = (void (*)(struct bd_info *, ulong, ulong, ulong,
58			   ulong, ulong, ulong))images->ep;
59	debug("## Transferring control to Linux (at address %08lx) ...\n",
60	      (ulong)kernel);
61
62	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
63
64#ifdef CONFIG_BOOTSTAGE_FDT
65	bootstage_fdt_add_report();
66#endif
67#ifdef CONFIG_BOOTSTAGE_REPORT
68	bootstage_report();
69#endif
70
71#if defined(CONFIG_SYS_INIT_RAM_LOCK) && !defined(CONFIG_E500)
72	unlock_ram_in_cache();
73#endif
74
75#if defined(CONFIG_OF_LIBFDT)
76	if (of_flat_tree) {	/* device tree; boot new style */
77		/*
78		 * Linux Kernel Parameters (passing device tree):
79		 *   r3: pointer to the fdt
80		 *   r4: 0
81		 *   r5: 0
82		 *   r6: epapr magic
83		 *   r7: size of IMA in bytes
84		 *   r8: 0
85		 *   r9: 0
86		 */
87		debug("   Booting using OF flat tree...\n");
88		schedule();
89		(*kernel) ((struct bd_info *)of_flat_tree, 0, 0, EPAPR_MAGIC,
90			   env_get_bootm_mapsize(), 0, 0);
91		/* does not return */
92	} else
93#endif
94	{
95		/*
96		 * Linux Kernel Parameters (passing board info data):
97		 *   r3: ptr to board info data
98		 *   r4: initrd_start or 0 if no initrd
99		 *   r5: initrd_end - unused if r4 is 0
100		 *   r6: Start of command line string
101		 *   r7: End   of command line string
102		 *   r8: 0
103		 *   r9: 0
104		 */
105		ulong cmd_start = images->cmdline_start;
106		ulong cmd_end = images->cmdline_end;
107		ulong initrd_start = images->initrd_start;
108		ulong initrd_end = images->initrd_end;
109		struct bd_info *kbd = images->kbd;
110
111		debug("   Booting using board info...\n");
112		schedule();
113		(*kernel) (kbd, initrd_start, initrd_end,
114			   cmd_start, cmd_end, 0, 0);
115		/* does not return */
116	}
117	return;
118}
119
120void arch_lmb_reserve(struct lmb *lmb)
121{
122	phys_size_t bootm_size;
123	ulong size, bootmap_base;
124
125	bootmap_base = env_get_bootm_low();
126	bootm_size = env_get_bootm_size();
127
128#ifdef DEBUG
129	if (((u64)bootmap_base + bootm_size) >
130	    (CFG_SYS_SDRAM_BASE + (u64)gd->ram_size))
131		puts("WARNING: bootm_low + bootm_size exceed total memory\n");
132	if ((bootmap_base + bootm_size) > get_effective_memsize())
133		puts("WARNING: bootm_low + bootm_size exceed eff. memory\n");
134#endif
135
136	size = min(bootm_size, get_effective_memsize());
137	size = min(size, (ulong)CFG_SYS_LINUX_LOWMEM_MAX_SIZE);
138
139	if (size < bootm_size) {
140		ulong base = bootmap_base + size;
141		printf("WARNING: adjusting available memory from 0x%lx to 0x%llx\n",
142		       size, (unsigned long long)bootm_size);
143		lmb_reserve(lmb, base, bootm_size - size);
144	}
145
146	arch_lmb_reserve_generic(lmb, get_sp(), gd->ram_top, 4096);
147
148#ifdef CONFIG_MP
149	cpu_mp_lmb_reserve(lmb);
150#endif
151
152	return;
153}
154
155static void boot_prep_linux(struct bootm_headers *images)
156{
157#ifdef CONFIG_MP
158	/*
159	 * if we are MP make sure to flush the device tree so any changes are
160	 * made visibile to all other cores.  In AMP boot scenarios the cores
161	 * might not be HW cache coherent with each other.
162	 */
163	flush_cache((unsigned long)images->ft_addr, images->ft_len);
164#endif
165}
166
167static int boot_cmdline_linux(struct bootm_headers *images)
168{
169	ulong of_size = images->ft_len;
170	struct lmb *lmb = &images->lmb;
171	ulong *cmd_start = &images->cmdline_start;
172	ulong *cmd_end = &images->cmdline_end;
173
174	int ret = 0;
175
176	if (!of_size) {
177		/* allocate space and init command line */
178		ret = boot_get_cmdline (lmb, cmd_start, cmd_end);
179		if (ret) {
180			puts("ERROR with allocation of cmdline\n");
181			return ret;
182		}
183	}
184
185	return ret;
186}
187
188static int boot_bd_t_linux(struct bootm_headers *images)
189{
190	ulong of_size = images->ft_len;
191	struct lmb *lmb = &images->lmb;
192	struct bd_info **kbd = &images->kbd;
193
194	int ret = 0;
195
196	if (!of_size) {
197		/* allocate space for kernel copy of board info */
198		ret = boot_get_kbd (lmb, kbd);
199		if (ret) {
200			puts("ERROR with allocation of kernel bd\n");
201			return ret;
202		}
203		set_clocks_in_mhz(*kbd);
204	}
205
206	return ret;
207}
208
209static int boot_body_linux(struct bootm_headers *images)
210{
211	int ret;
212
213	/* allocate space for kernel copy of board info */
214	ret = boot_bd_t_linux(images);
215	if (ret)
216		return ret;
217
218	if (IS_ENABLED(CONFIG_LMB)) {
219		ret = image_setup_linux(images);
220		if (ret)
221			return ret;
222	}
223
224	return 0;
225}
226
227int do_bootm_linux(int flag, struct bootm_info *bmi)
228{
229	struct bootm_headers *images = bmi->images;
230	int	ret;
231
232	if (flag & BOOTM_STATE_OS_CMDLINE) {
233		boot_cmdline_linux(images);
234		return 0;
235	}
236
237	if (flag & BOOTM_STATE_OS_BD_T) {
238		boot_bd_t_linux(images);
239		return 0;
240	}
241
242	if (flag & BOOTM_STATE_OS_PREP) {
243		boot_prep_linux(images);
244		return 0;
245	}
246
247	boot_prep_linux(images);
248	ret = boot_body_linux(images);
249	if (ret)
250		return ret;
251	boot_jump_linux(images);
252
253	return 0;
254}
255
256static ulong get_sp (void)
257{
258	ulong sp;
259
260	asm( "mr %0,1": "=r"(sp) : );
261	return sp;
262}
263
264static void set_clocks_in_mhz (struct bd_info *kbd)
265{
266	char	*s;
267
268	s = env_get("clocks_in_mhz");
269	if (s) {
270		/* convert all clock information to MHz */
271		kbd->bi_intfreq /= 1000000L;
272		kbd->bi_busfreq /= 1000000L;
273	}
274}
275
276#if defined(CONFIG_BOOTM_VXWORKS)
277void boot_prep_vxworks(struct bootm_headers *images)
278{
279#if defined(CONFIG_OF_LIBFDT)
280	int off;
281	u64 base, size;
282
283	if (!images->ft_addr)
284		return;
285
286	base = (u64)gd->ram_base;
287	size = (u64)gd->ram_size;
288
289	off = fdt_path_offset(images->ft_addr, "/memory");
290	if (off < 0)
291		fdt_fixup_memory(images->ft_addr, base, size);
292
293#if defined(CONFIG_MP)
294#if defined(CONFIG_MPC85xx)
295	ft_fixup_cpu(images->ft_addr, base + size);
296	ft_fixup_num_cores(images->ft_addr);
297#elif defined(CONFIG_MPC86xx)
298	off = fdt_add_mem_rsv(images->ft_addr,
299			determine_mp_bootpg(NULL), (u64)4096);
300	if (off < 0)
301		printf("## WARNING %s: %s\n", __func__, fdt_strerror(off));
302	ft_fixup_num_cores(images->ft_addr);
303#endif
304	flush_cache((unsigned long)images->ft_addr, images->ft_len);
305#endif
306#endif
307}
308
309void boot_jump_vxworks(struct bootm_headers *images)
310{
311	/* PowerPC VxWorks boot interface conforms to the ePAPR standard
312	 * general purpuse registers:
313	 *
314	 *	r3: Effective address of the device tree image
315	 *	r4: 0
316	 *	r5: 0
317	 *	r6: ePAPR magic value
318	 *	r7: shall be the size of the boot IMA in bytes
319	 *	r8: 0
320	 *	r9: 0
321	 *	TCR: WRC = 0, no watchdog timer reset will occur
322	 */
323	schedule();
324
325	((void (*)(void *, ulong, ulong, ulong,
326		ulong, ulong, ulong))images->ep)(images->ft_addr,
327		0, 0, EPAPR_MAGIC, env_get_bootm_mapsize(), 0, 0);
328}
329#endif
330