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