]>
Commit | Line | Data |
---|---|---|
0a672d49 SS |
1 | /* Copyright (C) 2011 |
2 | * Corscience GmbH & Co. KG - Simon Schwarz <[email protected]> | |
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 <[email protected]> | |
9 | * | |
10 | * Copyright (C) 2001 Erik Mouw ([email protected]) | |
11 | * | |
1a459660 | 12 | * SPDX-License-Identifier: GPL-2.0+ |
c609719b WD |
13 | */ |
14 | ||
15 | #include <common.h> | |
16 | #include <command.h> | |
c609719b | 17 | #include <image.h> |
a31e091a | 18 | #include <u-boot/zlib.h> |
c609719b | 19 | #include <asm/byteorder.h> |
2d1916e4 JR |
20 | #include <libfdt.h> |
21 | #include <fdt_support.h> | |
0a672d49 | 22 | #include <asm/bootm.h> |
89e6f138 | 23 | #include <linux/compiler.h> |
c609719b | 24 | |
d4296887 | 25 | #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) |
bb975455 AP |
26 | #include <asm/armv7.h> |
27 | #endif | |
28 | ||
d87080b7 WD |
29 | DECLARE_GLOBAL_DATA_PTR; |
30 | ||
c609719b | 31 | static struct tag *params; |
2d1916e4 | 32 | |
0a672d49 SS |
33 | static ulong get_sp(void) |
34 | { | |
35 | ulong ret; | |
36 | ||
37 | asm("mov %0, sp" : "=r"(ret) : ); | |
38 | return ret; | |
39 | } | |
40 | ||
2d1916e4 JR |
41 | void arch_lmb_reserve(struct lmb *lmb) |
42 | { | |
43 | ulong sp; | |
44 | ||
45 | /* | |
46 | * Booting a (Linux) kernel image | |
47 | * | |
48 | * Allocate space for command line and board info - the | |
49 | * address should be as high as possible within the reach of | |
50 | * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused | |
51 | * memory, which means far enough below the current stack | |
52 | * pointer. | |
53 | */ | |
54 | sp = get_sp(); | |
55 | debug("## Current stack ends at 0x%08lx ", sp); | |
56 | ||
fcfa696b RH |
57 | /* adjust sp by 4K to be safe */ |
58 | sp -= 4096; | |
2d1916e4 JR |
59 | lmb_reserve(lmb, sp, |
60 | gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); | |
61 | } | |
62 | ||
bce1b92a SG |
63 | /** |
64 | * announce_and_cleanup() - Print message and prepare for kernel boot | |
65 | * | |
66 | * @fake: non-zero to do everything except actually boot | |
67 | */ | |
68 | static void announce_and_cleanup(int fake) | |
2d1916e4 | 69 | { |
bce1b92a SG |
70 | printf("\nStarting kernel ...%s\n\n", fake ? |
71 | "(fake run for tracing)" : ""); | |
0a672d49 | 72 | bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); |
94fd1316 | 73 | #ifdef CONFIG_BOOTSTAGE_FDT |
bce1b92a SG |
74 | if (flag == BOOTM_STATE_OS_FAKE_GO) |
75 | bootstage_fdt_add_report(); | |
94fd1316 | 76 | #endif |
0a672d49 SS |
77 | #ifdef CONFIG_BOOTSTAGE_REPORT |
78 | bootstage_report(); | |
79 | #endif | |
1055171e | 80 | |
0a672d49 SS |
81 | #ifdef CONFIG_USB_DEVICE |
82 | udc_disconnect(); | |
2d1916e4 | 83 | #endif |
0a672d49 SS |
84 | cleanup_before_linux(); |
85 | } | |
c609719b | 86 | |
5779d8d9 | 87 | static void setup_start_tag (bd_t *bd) |
c609719b | 88 | { |
0a672d49 | 89 | params = (struct tag *)bd->bi_boot_params; |
c609719b | 90 | |
5779d8d9 WD |
91 | params->hdr.tag = ATAG_CORE; |
92 | params->hdr.size = tag_size (tag_core); | |
c609719b | 93 | |
5779d8d9 WD |
94 | params->u.core.flags = 0; |
95 | params->u.core.pagesize = 0; | |
96 | params->u.core.rootdev = 0; | |
c609719b | 97 | |
5779d8d9 | 98 | params = tag_next (params); |
c609719b | 99 | } |
c609719b | 100 | |
0a672d49 | 101 | static void setup_memory_tags(bd_t *bd) |
c609719b | 102 | { |
5779d8d9 | 103 | int i; |
c609719b | 104 | |
5779d8d9 WD |
105 | for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { |
106 | params->hdr.tag = ATAG_MEM; | |
107 | params->hdr.size = tag_size (tag_mem32); | |
c609719b | 108 | |
5779d8d9 WD |
109 | params->u.mem.start = bd->bi_dram[i].start; |
110 | params->u.mem.size = bd->bi_dram[i].size; | |
c609719b | 111 | |
5779d8d9 WD |
112 | params = tag_next (params); |
113 | } | |
c609719b | 114 | } |
c609719b | 115 | |
0a672d49 | 116 | static void setup_commandline_tag(bd_t *bd, char *commandline) |
c609719b | 117 | { |
5779d8d9 | 118 | char *p; |
c609719b | 119 | |
109c0e3a WD |
120 | if (!commandline) |
121 | return; | |
122 | ||
5779d8d9 WD |
123 | /* eat leading white space */ |
124 | for (p = commandline; *p == ' '; p++); | |
c609719b | 125 | |
5779d8d9 WD |
126 | /* skip non-existent command lines so the kernel will still |
127 | * use its default command line. | |
128 | */ | |
129 | if (*p == '\0') | |
130 | return; | |
c609719b | 131 | |
5779d8d9 WD |
132 | params->hdr.tag = ATAG_CMDLINE; |
133 | params->hdr.size = | |
134 | (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; | |
c609719b | 135 | |
5779d8d9 | 136 | strcpy (params->u.cmdline.cmdline, p); |
c609719b | 137 | |
5779d8d9 | 138 | params = tag_next (params); |
c609719b | 139 | } |
c609719b | 140 | |
0a672d49 | 141 | static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end) |
c609719b | 142 | { |
5779d8d9 WD |
143 | /* an ATAG_INITRD node tells the kernel where the compressed |
144 | * ramdisk can be found. ATAG_RDIMG is a better name, actually. | |
145 | */ | |
498b8db7 | 146 | params->hdr.tag = ATAG_INITRD2; |
5779d8d9 | 147 | params->hdr.size = tag_size (tag_initrd); |
c609719b | 148 | |
5779d8d9 WD |
149 | params->u.initrd.start = initrd_start; |
150 | params->u.initrd.size = initrd_end - initrd_start; | |
c609719b | 151 | |
5779d8d9 | 152 | params = tag_next (params); |
c609719b WD |
153 | } |
154 | ||
c19d13b0 | 155 | static void setup_serial_tag(struct tag **tmp) |
3a574cbe WD |
156 | { |
157 | struct tag *params = *tmp; | |
158 | struct tag_serialnr serialnr; | |
3a574cbe WD |
159 | |
160 | get_board_serial(&serialnr); | |
161 | params->hdr.tag = ATAG_SERIAL; | |
162 | params->hdr.size = tag_size (tag_serialnr); | |
163 | params->u.serialnr.low = serialnr.low; | |
164 | params->u.serialnr.high= serialnr.high; | |
165 | params = tag_next (params); | |
166 | *tmp = params; | |
167 | } | |
3a574cbe | 168 | |
c19d13b0 | 169 | static void setup_revision_tag(struct tag **in_params) |
289f932c WD |
170 | { |
171 | u32 rev = 0; | |
289f932c WD |
172 | |
173 | rev = get_board_rev(); | |
289f932c WD |
174 | params->hdr.tag = ATAG_REVISION; |
175 | params->hdr.size = tag_size (tag_revision); | |
176 | params->u.revision.rev = rev; | |
177 | params = tag_next (params); | |
178 | } | |
289f932c | 179 | |
0a672d49 | 180 | static void setup_end_tag(bd_t *bd) |
c609719b | 181 | { |
5779d8d9 WD |
182 | params->hdr.tag = ATAG_NONE; |
183 | params->hdr.size = 0; | |
c609719b WD |
184 | } |
185 | ||
89e6f138 T |
186 | __weak void setup_board_tags(struct tag **in_params) {} |
187 | ||
bb975455 AP |
188 | static void do_nonsec_virt_switch(void) |
189 | { | |
190 | #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) | |
191 | if (armv7_switch_nonsec() == 0) | |
d4296887 AP |
192 | #ifdef CONFIG_ARMV7_VIRT |
193 | if (armv7_switch_hyp() == 0) | |
194 | debug("entered HYP mode\n"); | |
195 | #else | |
bb975455 AP |
196 | debug("entered non-secure state\n"); |
197 | #endif | |
d4296887 | 198 | #endif |
0ae76531 DF |
199 | |
200 | #ifdef CONFIG_ARM64 | |
201 | smp_kick_all_cpus(); | |
202 | armv8_switch_to_el2(); | |
203 | #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 | |
204 | armv8_switch_to_el1(); | |
205 | #endif | |
206 | #endif | |
bb975455 AP |
207 | } |
208 | ||
0a672d49 SS |
209 | /* Subcommand: PREP */ |
210 | static void boot_prep_linux(bootm_headers_t *images) | |
211 | { | |
0a672d49 | 212 | char *commandline = getenv("bootargs"); |
0a672d49 | 213 | |
c19d13b0 | 214 | if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { |
0a672d49 | 215 | #ifdef CONFIG_OF_LIBFDT |
0a672d49 | 216 | debug("using: FDT\n"); |
6caa1956 | 217 | if (image_setup_linux(images)) { |
0a672d49 SS |
218 | printf("FDT creation failed! hanging..."); |
219 | hang(); | |
220 | } | |
0a672d49 | 221 | #endif |
c19d13b0 | 222 | } else if (BOOTM_ENABLE_TAGS) { |
0a672d49 SS |
223 | debug("using: ATAGS\n"); |
224 | setup_start_tag(gd->bd); | |
c19d13b0 SG |
225 | if (BOOTM_ENABLE_SERIAL_TAG) |
226 | setup_serial_tag(¶ms); | |
227 | if (BOOTM_ENABLE_CMDLINE_TAG) | |
228 | setup_commandline_tag(gd->bd, commandline); | |
229 | if (BOOTM_ENABLE_REVISION_TAG) | |
230 | setup_revision_tag(¶ms); | |
231 | if (BOOTM_ENABLE_MEMORY_TAGS) | |
232 | setup_memory_tags(gd->bd); | |
233 | if (BOOTM_ENABLE_INITRD_TAG) { | |
234 | if (images->rd_start && images->rd_end) { | |
235 | setup_initrd_tag(gd->bd, images->rd_start, | |
236 | images->rd_end); | |
237 | } | |
238 | } | |
89e6f138 | 239 | setup_board_tags(¶ms); |
0a672d49 | 240 | setup_end_tag(gd->bd); |
c19d13b0 | 241 | } else { |
0a672d49 SS |
242 | printf("FDT and ATAGS support not compiled in - hanging\n"); |
243 | hang(); | |
0a672d49 | 244 | } |
bb975455 | 245 | do_nonsec_virt_switch(); |
0a672d49 SS |
246 | } |
247 | ||
248 | /* Subcommand: GO */ | |
bce1b92a | 249 | static void boot_jump_linux(bootm_headers_t *images, int flag) |
0a672d49 | 250 | { |
0ae76531 DF |
251 | #ifdef CONFIG_ARM64 |
252 | void (*kernel_entry)(void *fdt_addr); | |
253 | int fake = (flag & BOOTM_STATE_OS_FAKE_GO); | |
254 | ||
255 | kernel_entry = (void (*)(void *fdt_addr))images->ep; | |
256 | ||
257 | debug("## Transferring control to Linux (at address %lx)...\n", | |
258 | (ulong) kernel_entry); | |
259 | bootstage_mark(BOOTSTAGE_ID_RUN_OS); | |
260 | ||
261 | announce_and_cleanup(fake); | |
262 | ||
263 | if (!fake) | |
264 | kernel_entry(images->ft_addr); | |
265 | #else | |
0a672d49 SS |
266 | unsigned long machid = gd->bd->bi_arch_number; |
267 | char *s; | |
268 | void (*kernel_entry)(int zero, int arch, uint params); | |
17239976 | 269 | unsigned long r2; |
bce1b92a | 270 | int fake = (flag & BOOTM_STATE_OS_FAKE_GO); |
0a672d49 SS |
271 | |
272 | kernel_entry = (void (*)(int, int, uint))images->ep; | |
273 | ||
274 | s = getenv("machid"); | |
275 | if (s) { | |
276 | strict_strtoul(s, 16, &machid); | |
277 | printf("Using machid 0x%lx from environment\n", machid); | |
278 | } | |
279 | ||
280 | debug("## Transferring control to Linux (at address %08lx)" \ | |
281 | "...\n", (ulong) kernel_entry); | |
282 | bootstage_mark(BOOTSTAGE_ID_RUN_OS); | |
bce1b92a | 283 | announce_and_cleanup(fake); |
17239976 | 284 | |
c19d13b0 | 285 | if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) |
17239976 SW |
286 | r2 = (unsigned long)images->ft_addr; |
287 | else | |
17239976 SW |
288 | r2 = gd->bd->bi_boot_params; |
289 | ||
bce1b92a SG |
290 | if (!fake) |
291 | kernel_entry(0, machid, r2); | |
0ae76531 | 292 | #endif |
0a672d49 SS |
293 | } |
294 | ||
295 | /* Main Entry point for arm bootm implementation | |
296 | * | |
297 | * Modeled after the powerpc implementation | |
298 | * DIFFERENCE: Instead of calling prep and go at the end | |
299 | * they are called if subcommand is equal 0. | |
300 | */ | |
301 | int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) | |
302 | { | |
303 | /* No need for those on ARM */ | |
304 | if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) | |
305 | return -1; | |
306 | ||
307 | if (flag & BOOTM_STATE_OS_PREP) { | |
308 | boot_prep_linux(images); | |
309 | return 0; | |
310 | } | |
311 | ||
bce1b92a SG |
312 | if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { |
313 | boot_jump_linux(images, flag); | |
0a672d49 SS |
314 | return 0; |
315 | } | |
316 | ||
317 | boot_prep_linux(images); | |
bce1b92a | 318 | boot_jump_linux(images, flag); |
0a672d49 | 319 | return 0; |
2d1916e4 | 320 | } |
44f074c7 MV |
321 | |
322 | #ifdef CONFIG_CMD_BOOTZ | |
323 | ||
324 | struct zimage_header { | |
325 | uint32_t code[9]; | |
326 | uint32_t zi_magic; | |
327 | uint32_t zi_start; | |
328 | uint32_t zi_end; | |
329 | }; | |
330 | ||
331 | #define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818 | |
332 | ||
a5266d6b | 333 | int bootz_setup(ulong image, ulong *start, ulong *end) |
44f074c7 | 334 | { |
a5266d6b | 335 | struct zimage_header *zi; |
44f074c7 | 336 | |
a5266d6b | 337 | zi = (struct zimage_header *)map_sysmem(image, 0); |
44f074c7 MV |
338 | if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) { |
339 | puts("Bad Linux ARM zImage magic!\n"); | |
340 | return 1; | |
341 | } | |
342 | ||
a5266d6b SG |
343 | *start = zi->zi_start; |
344 | *end = zi->zi_end; | |
44f074c7 | 345 | |
a5266d6b SG |
346 | printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n", image, *start, |
347 | *end); | |
44f074c7 MV |
348 | |
349 | return 0; | |
350 | } | |
a5266d6b | 351 | |
44f074c7 | 352 | #endif /* CONFIG_CMD_BOOTZ */ |
871a57bb MY |
353 | |
354 | #if defined(CONFIG_BOOTM_VXWORKS) | |
355 | void boot_prep_vxworks(bootm_headers_t *images) | |
356 | { | |
357 | #if defined(CONFIG_OF_LIBFDT) | |
358 | int off; | |
359 | ||
360 | if (images->ft_addr) { | |
361 | off = fdt_path_offset(images->ft_addr, "/memory"); | |
362 | if (off < 0) { | |
363 | if (arch_fixup_memory_node(images->ft_addr)) | |
364 | puts("## WARNING: fixup memory failed!\n"); | |
365 | } | |
366 | } | |
367 | #endif | |
368 | cleanup_before_linux(); | |
369 | } | |
370 | void boot_jump_vxworks(bootm_headers_t *images) | |
371 | { | |
372 | /* ARM VxWorks requires device tree physical address to be passed */ | |
373 | ((void (*)(void *))images->ep)(images->ft_addr); | |
374 | } | |
375 | #endif |