xref: /rk3399_rockchip-uboot/arch/arm/lib/bootm.c (revision fbc03ba7beeb1620e5ccb689f2f51a2e08f1c180)
1 /* Copyright (C) 2011
2  * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
3  *  - Added prep subcommand support
4  *  - Reorganized source - modeled after powerpc version
5  *
6  * (C) Copyright 2002
7  * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
8  * Marius Groeger <mgroeger@sysgo.de>
9  *
10  * Copyright (C) 2001  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
11  *
12  * SPDX-License-Identifier:	GPL-2.0+
13  */
14 
15 #include <common.h>
16 #include <command.h>
17 #include <amp.h>
18 #include <dm.h>
19 #include <dm/root.h>
20 #include <image.h>
21 #include <u-boot/zlib.h>
22 #include <asm/byteorder.h>
23 #include <linux/libfdt.h>
24 #include <mapmem.h>
25 #include <fdt_support.h>
26 #include <asm/bootm.h>
27 #include <asm/secure.h>
28 #include <linux/compiler.h>
29 #include <bootm.h>
30 #include <vxworks.h>
31 
32 #ifdef CONFIG_ARMV7_NONSEC
33 #include <asm/armv7.h>
34 #endif
35 #include <asm/setup.h>
36 #include <asm/arch/rockchip_smccc.h>
37 
38 DECLARE_GLOBAL_DATA_PTR;
39 
40 static struct tag *params;
41 
42 static ulong get_sp(void)
43 {
44 	ulong ret;
45 
46 	asm("mov %0, sp" : "=r"(ret) : );
47 	return ret;
48 }
49 
50 void arch_lmb_reserve(struct lmb *lmb)
51 {
52 	ulong sp;
53 
54 	/*
55 	 * Booting a (Linux) kernel image
56 	 *
57 	 * Allocate space for command line and board info - the
58 	 * address should be as high as possible within the reach of
59 	 * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
60 	 * memory, which means far enough below the current stack
61 	 * pointer.
62 	 */
63 	sp = get_sp();
64 	debug("## Current stack ends at 0x%08lx ", sp);
65 
66 	/* adjust sp by 4K to be safe */
67 	sp -= 4096;
68 	lmb_reserve(lmb, sp,
69 		    gd->ram_top - sp);
70 }
71 
72 __weak void board_quiesce_devices(void *images)
73 {
74 }
75 
76 /**
77  * announce_and_cleanup() - Print message and prepare for kernel boot
78  *
79  * @fake: non-zero to do everything except actually boot
80  */
81 static void announce_and_cleanup(bootm_headers_t *images, int fake)
82 {
83 	ulong us, tt_us;
84 
85 	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
86 #ifdef CONFIG_BOOTSTAGE_FDT
87 	bootstage_fdt_add_report();
88 #endif
89 #ifdef CONFIG_BOOTSTAGE_REPORT
90 	bootstage_report();
91 #endif
92 
93 #ifdef CONFIG_USB_DEVICE
94 	udc_disconnect();
95 #endif
96 
97 	board_quiesce_devices(images);
98 
99 	/* Flush all console data */
100 	flushc();
101 
102 	/*
103 	 * Call remove function of all devices with a removal flag set.
104 	 * This may be useful for last-stage operations, like cancelling
105 	 * of DMA operation or releasing device internal buffers.
106 	 */
107 	dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
108 
109 	cleanup_before_linux();
110 
111 	us = (get_ticks() - gd->sys_start_tick) / (COUNTER_FREQUENCY / 1000000);
112 	tt_us = get_ticks() / (COUNTER_FREQUENCY / 1000000);
113 	printf("Total: %ld.%ld/%ld.%ld ms\n", us / 1000, us % 1000, tt_us / 1000, tt_us % 1000);
114 
115 	printf("\nStarting kernel ...%s\n\n", fake ?
116 		"(fake run for tracing)" : "");
117 }
118 
119 static void setup_start_tag (bd_t *bd)
120 {
121 	params = (struct tag *)bd->bi_boot_params;
122 
123 	params->hdr.tag = ATAG_CORE;
124 	params->hdr.size = tag_size (tag_core);
125 
126 	params->u.core.flags = 0;
127 	params->u.core.pagesize = 0;
128 	params->u.core.rootdev = 0;
129 
130 	params = tag_next (params);
131 }
132 
133 static void setup_memory_tags(bd_t *bd)
134 {
135 	int i;
136 
137 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
138 		params->hdr.tag = ATAG_MEM;
139 		params->hdr.size = tag_size (tag_mem32);
140 
141 		params->u.mem.start = bd->bi_dram[i].start;
142 		params->u.mem.size = bd->bi_dram[i].size;
143 
144 		params = tag_next (params);
145 	}
146 }
147 
148 static void setup_commandline_tag(bd_t *bd, char *commandline)
149 {
150 	char *p;
151 
152 	if (!commandline)
153 		return;
154 
155 	/* eat leading white space */
156 	for (p = commandline; *p == ' '; p++);
157 
158 	/* skip non-existent command lines so the kernel will still
159 	 * use its default command line.
160 	 */
161 	if (*p == '\0')
162 		return;
163 
164 	params->hdr.tag = ATAG_CMDLINE;
165 	params->hdr.size =
166 		(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
167 
168 	strcpy (params->u.cmdline.cmdline, p);
169 
170 	params = tag_next (params);
171 }
172 
173 static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end)
174 {
175 	/* an ATAG_INITRD node tells the kernel where the compressed
176 	 * ramdisk can be found. ATAG_RDIMG is a better name, actually.
177 	 */
178 	params->hdr.tag = ATAG_INITRD2;
179 	params->hdr.size = tag_size (tag_initrd);
180 
181 	params->u.initrd.start = initrd_start;
182 	params->u.initrd.size = initrd_end - initrd_start;
183 
184 	params = tag_next (params);
185 }
186 
187 static void setup_serial_tag(struct tag **tmp)
188 {
189 	struct tag *params = *tmp;
190 	struct tag_serialnr serialnr;
191 
192 	get_board_serial(&serialnr);
193 	params->hdr.tag = ATAG_SERIAL;
194 	params->hdr.size = tag_size (tag_serialnr);
195 	params->u.serialnr.low = serialnr.low;
196 	params->u.serialnr.high= serialnr.high;
197 	params = tag_next (params);
198 	*tmp = params;
199 }
200 
201 static void setup_revision_tag(struct tag **in_params)
202 {
203 	u32 rev = 0;
204 
205 	rev = get_board_rev();
206 	params->hdr.tag = ATAG_REVISION;
207 	params->hdr.size = tag_size (tag_revision);
208 	params->u.revision.rev = rev;
209 	params = tag_next (params);
210 }
211 
212 static void setup_end_tag(bd_t *bd)
213 {
214 	params->hdr.tag = ATAG_NONE;
215 	params->hdr.size = 0;
216 }
217 
218 __weak void setup_board_tags(struct tag **in_params) {}
219 
220 #ifdef CONFIG_ARM64
221 static void do_nonsec_virt_switch(void)
222 {
223 	smp_kick_all_cpus();
224 	dcache_disable();	/* flush cache before swtiching to EL2 */
225 }
226 #endif
227 
228 /* Subcommand: PREP */
229 static void boot_prep_linux(bootm_headers_t *images)
230 {
231 	char *commandline = env_get("bootargs");
232 
233 	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
234 #ifdef CONFIG_OF_LIBFDT
235 		debug("using: FDT\n");
236 		if (image_setup_linux(images)) {
237 			printf("FDT creation failed! hanging...");
238 			hang();
239 		}
240 #endif
241 	} else if (BOOTM_ENABLE_TAGS) {
242 		debug("using: ATAGS\n");
243 		setup_start_tag(gd->bd);
244 		if (BOOTM_ENABLE_SERIAL_TAG)
245 			setup_serial_tag(&params);
246 		if (BOOTM_ENABLE_CMDLINE_TAG)
247 			setup_commandline_tag(gd->bd, commandline);
248 		if (BOOTM_ENABLE_REVISION_TAG)
249 			setup_revision_tag(&params);
250 		if (BOOTM_ENABLE_MEMORY_TAGS)
251 			setup_memory_tags(gd->bd);
252 		if (BOOTM_ENABLE_INITRD_TAG) {
253 			/*
254 			 * In boot_ramdisk_high(), it may relocate ramdisk to
255 			 * a specified location. And set images->initrd_start &
256 			 * images->initrd_end to relocated ramdisk's start/end
257 			 * addresses. So use them instead of images->rd_start &
258 			 * images->rd_end when possible.
259 			 */
260 			if (images->initrd_start && images->initrd_end) {
261 				setup_initrd_tag(gd->bd, images->initrd_start,
262 						 images->initrd_end);
263 			} else if (images->rd_start && images->rd_end) {
264 				setup_initrd_tag(gd->bd, images->rd_start,
265 						 images->rd_end);
266 			}
267 		}
268 		setup_board_tags(&params);
269 		setup_end_tag(gd->bd);
270 	} else {
271 		printf("FDT and ATAGS support not compiled in - hanging\n");
272 		hang();
273 	}
274 }
275 
276 __weak bool armv7_boot_nonsec_default(void)
277 {
278 #ifdef CONFIG_ARMV7_BOOT_SEC_DEFAULT
279 	return false;
280 #else
281 	return true;
282 #endif
283 }
284 
285 #ifdef CONFIG_ARMV7_NONSEC
286 bool armv7_boot_nonsec(void)
287 {
288 	char *s = env_get("bootm_boot_mode");
289 	bool nonsec = armv7_boot_nonsec_default();
290 
291 	if (s && !strcmp(s, "sec"))
292 		nonsec = false;
293 
294 	if (s && !strcmp(s, "nonsec"))
295 		nonsec = true;
296 
297 	return nonsec;
298 }
299 #endif
300 
301 #ifdef CONFIG_ARM64
302 __weak void update_os_arch_secondary_cores(uint8_t os_arch)
303 {
304 }
305 
306 #ifdef CONFIG_ARMV8_SWITCH_TO_EL1
307 static void switch_to_el1(void)
308 {
309 	if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
310 	    (images.os.arch == IH_ARCH_ARM))
311 		armv8_switch_to_el1(0, (u64)gd->bd->bi_arch_number,
312 				    (u64)images.ft_addr, 0,
313 				    (u64)images.ep,
314 				    ES_TO_AARCH32);
315 	else
316 		armv8_switch_to_el1((u64)images.ft_addr, 0, 0, 0,
317 				    images.ep,
318 				    ES_TO_AARCH64);
319 }
320 #endif
321 #endif
322 
323 #ifdef CONFIG_ARM64_SWITCH_TO_AARCH32
324 static int arm64_switch_aarch32(bootm_headers_t *images)
325 {
326 	void *fdt = images->ft_addr;
327 	ulong mpidr;
328 	int ret, es_flag;
329 	int nodeoff, tmp;
330 	int num = -1;
331 
332 	/* arm aarch32 SVC */
333 	images->os.arch = IH_ARCH_ARM;
334 	es_flag = PE_STATE(0, 0, 0, 0);
335 
336 	nodeoff = fdt_path_offset(fdt, "/cpus");
337 	if (nodeoff < 0) {
338 		printf("couldn't find /cpus\n");
339 		return nodeoff;
340 	}
341 
342 	/* config all nonboot cpu state */
343 	for (tmp = fdt_first_subnode(fdt, nodeoff);
344 	     tmp >= 0;
345 	     tmp = fdt_next_subnode(fdt, tmp)) {
346 		const struct fdt_property *prop;
347 		int len;
348 
349 		prop = fdt_get_property(fdt, tmp, "device_type", &len);
350 		if (!prop)
351 			continue;
352 		if (len < 4)
353 			continue;
354 		if (strcmp(prop->data, "cpu"))
355 			continue;
356 		/* skip boot(first) cpu */
357 		num++;
358 		if (num == 0)
359 			continue;
360 
361 		mpidr = (ulong)fdtdec_get_addr_size_auto_parent(fdt,
362 					nodeoff, tmp, "reg", 0, NULL, false);
363 		ret = sip_smc_amp_cfg(AMP_PE_STATE, mpidr, es_flag, 0);
364 		if (ret) {
365 			printf("CPU@%lx init AArch32 failed: %d\n", mpidr, ret);
366 			continue;
367 		}
368 	}
369 
370 	return es_flag;
371 }
372 #endif
373 
374 /* Subcommand: GO */
375 static void boot_jump_linux(bootm_headers_t *images, int flag)
376 {
377 #ifdef CONFIG_ARM64
378 	void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
379 			void *res2);
380 	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
381 	int es_flag = 0;
382 
383 #if defined(CONFIG_AMP)
384 	es_flag = arm64_switch_amp_pe(images);
385 #elif defined(CONFIG_ARM64_SWITCH_TO_AARCH32)
386 	es_flag = arm64_switch_aarch32(images);
387 #endif
388 	kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
389 				void *res2))images->ep;
390 
391 	debug("## Transferring control to Linux (at address %lx)...\n",
392 		(ulong) kernel_entry);
393 	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
394 
395 	announce_and_cleanup(images, fake);
396 
397 	if (!fake) {
398 #ifdef CONFIG_ARMV8_PSCI
399 		armv8_setup_psci();
400 #endif
401 		do_nonsec_virt_switch();
402 
403 		update_os_arch_secondary_cores(images->os.arch);
404 
405 #ifdef CONFIG_ARMV8_SWITCH_TO_EL1
406 		armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
407 				    (u64)switch_to_el1, ES_TO_AARCH64);
408 #else
409 		if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
410 		    (images->os.arch == IH_ARCH_ARM))
411 			armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,
412 					    (u64)images->ft_addr, es_flag,
413 					    (u64)images->ep,
414 					    ES_TO_AARCH32);
415 		else
416 			armv8_switch_to_el2((u64)images->ft_addr, 0, 0, es_flag,
417 					    images->ep,
418 					    ES_TO_AARCH64);
419 #endif
420 	}
421 #else
422 	unsigned long machid = gd->bd->bi_arch_number;
423 	char *s;
424 	void (*kernel_entry)(int zero, int arch, uint params);
425 	unsigned long r2;
426 	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
427 
428 	kernel_entry = (void (*)(int, int, uint))images->ep;
429 #ifdef CONFIG_CPU_V7M
430 	ulong addr = (ulong)kernel_entry | 1;
431 	kernel_entry = (void *)addr;
432 #endif
433 	s = env_get("machid");
434 	if (s) {
435 		if (strict_strtoul(s, 16, &machid) < 0) {
436 			debug("strict_strtoul failed!\n");
437 			return;
438 		}
439 		printf("Using machid 0x%lx from environment\n", machid);
440 	}
441 
442 	debug("## Transferring control to Linux (at address %08lx)" \
443 		"...\n", (ulong) kernel_entry);
444 	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
445 	announce_and_cleanup(images, fake);
446 
447 	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
448 		r2 = (unsigned long)images->ft_addr;
449 	else
450 		r2 = gd->bd->bi_boot_params;
451 
452 	if (!fake) {
453 #ifdef CONFIG_ARMV7_NONSEC
454 		if (armv7_boot_nonsec()) {
455 			armv7_init_nonsec();
456 			secure_ram_addr(_do_nonsec_entry)(kernel_entry,
457 							  0, machid, r2);
458 		} else
459 #endif
460 			kernel_entry(0, machid, r2);
461 	}
462 #endif
463 }
464 
465 /* Main Entry point for arm bootm implementation
466  *
467  * Modeled after the powerpc implementation
468  * DIFFERENCE: Instead of calling prep and go at the end
469  * they are called if subcommand is equal 0.
470  */
471 int do_bootm_linux(int flag, int argc, char * const argv[],
472 		   bootm_headers_t *images)
473 {
474 	/* No need for those on ARM */
475 	if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
476 		return -1;
477 
478 	if (flag & BOOTM_STATE_OS_PREP) {
479 		boot_prep_linux(images);
480 		return 0;
481 	}
482 
483 	if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
484 		boot_jump_linux(images, flag);
485 		return 0;
486 	}
487 
488 	boot_prep_linux(images);
489 	boot_jump_linux(images, flag);
490 	return 0;
491 }
492 
493 #if defined(CONFIG_BOOTM_VXWORKS)
494 void boot_prep_vxworks(bootm_headers_t *images)
495 {
496 #if defined(CONFIG_OF_LIBFDT)
497 	int off;
498 
499 	if (images->ft_addr) {
500 		off = fdt_path_offset(images->ft_addr, "/memory");
501 		if (off < 0) {
502 			if (arch_fixup_fdt(images->ft_addr))
503 				puts("## WARNING: fixup memory failed!\n");
504 		}
505 	}
506 #endif
507 	cleanup_before_linux();
508 }
509 void boot_jump_vxworks(bootm_headers_t *images)
510 {
511 	/* ARM VxWorks requires device tree physical address to be passed */
512 	((void (*)(void *))images->ep)(images->ft_addr);
513 }
514 #endif
515