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