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