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