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(¶ms);
250 if (BOOTM_ENABLE_CMDLINE_TAG)
251 setup_commandline_tag(gd->bd, commandline);
252 if (BOOTM_ENABLE_REVISION_TAG)
253 setup_revision_tag(¶ms);
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(¶ms);
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