]> git.ipfire.org Git - people/ms/u-boot.git/blame - lib_ppc/ppc_linux.c
[new uImage] Cleanup image header pointer use in bootm code
[people/ms/u-boot.git] / lib_ppc / ppc_linux.c
CommitLineData
5d3cc55e
MB
1/*
2 * (C) Copyright 2008 Semihalf
3 *
4 * (C) Copyright 2000-2006
5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25
26#include <common.h>
27#include <watchdog.h>
28#include <command.h>
29#include <image.h>
30#include <malloc.h>
31#include <zlib.h>
32#include <bzlib.h>
33#include <environment.h>
34#include <asm/byteorder.h>
35
36#if defined(CONFIG_OF_LIBFDT)
37#include <fdt.h>
38#include <libfdt.h>
39#include <fdt_support.h>
d45d5a18 40#elif defined(CONFIG_OF_FLAT_TREE)
5d3cc55e
MB
41#include <ft_build.h>
42#endif
43
44#ifdef CONFIG_LOGBUFFER
45#include <logbuff.h>
46#endif
47
48#ifdef CFG_INIT_RAM_LOCK
49#include <asm/cache.h>
50#endif
51
5d3cc55e 52DECLARE_GLOBAL_DATA_PTR;
5d3cc55e 53
5d3cc55e
MB
54extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
55
56#if defined(CONFIG_CMD_BDI)
57extern int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
58#endif
59
60void __attribute__((noinline))
f13e7b2e 61do_bootm_linux(cmd_tbl_t *cmdtp, int flag,
5d3cc55e 62 int argc, char *argv[],
f13e7b2e 63 image_header_t *hdr,
5d3cc55e
MB
64 int verify)
65{
5d3cc55e 66 ulong initrd_high;
5d3cc55e 67 int initrd_copy_to_ram = 1;
f13e7b2e
MB
68 ulong initrd_start, initrd_end;
69 ulong rd_data, rd_len;
70 image_header_t *rd_hdr;
71
72 ulong cmd_start, cmd_end;
5d3cc55e 73 char *cmdline;
f13e7b2e
MB
74
75 ulong sp;
5d3cc55e
MB
76 char *s;
77 bd_t *kbd;
78 void (*kernel)(bd_t *, ulong, ulong, ulong, ulong);
f13e7b2e 79
5d3cc55e 80#if defined(CONFIG_OF_FLAT_TREE) || defined(CONFIG_OF_LIBFDT)
f13e7b2e 81 image_header_t *fdt_hdr;
5d3cc55e
MB
82 char *of_flat_tree = NULL;
83 ulong of_data = 0;
84#endif
85
86 if ((s = getenv ("initrd_high")) != NULL) {
87 /* a value of "no" or a similar string will act like 0,
88 * turning the "load high" feature off. This is intentional.
89 */
90 initrd_high = simple_strtoul(s, NULL, 16);
91 if (initrd_high == ~0)
92 initrd_copy_to_ram = 0;
93 } else { /* not set, no restrictions to load high */
94 initrd_high = ~0;
95 }
96
97#ifdef CONFIG_LOGBUFFER
98 kbd=gd->bd;
99 /* Prevent initrd from overwriting logbuffer */
100 if (initrd_high < (kbd->bi_memsize-LOGBUFF_LEN-LOGBUFF_OVERHEAD))
101 initrd_high = kbd->bi_memsize-LOGBUFF_LEN-LOGBUFF_OVERHEAD;
102 debug ("## Logbuffer at 0x%08lX ", kbd->bi_memsize-LOGBUFF_LEN);
103#endif
104
105 /*
106 * Booting a (Linux) kernel image
107 *
108 * Allocate space for command line and board info - the
109 * address should be as high as possible within the reach of
110 * the kernel (see CFG_BOOTMAPSZ settings), but in unused
111 * memory, which means far enough below the current stack
112 * pointer.
113 */
114
115 asm( "mr %0,1": "=r"(sp) : );
116
117 debug ("## Current stack ends at 0x%08lX ", sp);
118
119 sp -= 2048; /* just to be sure */
120 if (sp > CFG_BOOTMAPSZ)
121 sp = CFG_BOOTMAPSZ;
122 sp &= ~0xF;
123
124 debug ("=> set upper limit to 0x%08lX\n", sp);
125
126 cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF);
127 kbd = (bd_t *)(((ulong)cmdline - sizeof(bd_t)) & ~0xF);
128
129 if ((s = getenv("bootargs")) == NULL)
130 s = "";
131
132 strcpy (cmdline, s);
133
134 cmd_start = (ulong)&cmdline[0];
135 cmd_end = cmd_start + strlen(cmdline);
136
137 *kbd = *(gd->bd);
138
139#ifdef DEBUG
140 printf ("## cmdline at 0x%08lX ... 0x%08lX\n", cmd_start, cmd_end);
141
142#if defined(CONFIG_CMD_BDI)
143 do_bdinfo (NULL, 0, 0, NULL);
144#endif
145#endif
146
147 if ((s = getenv ("clocks_in_mhz")) != NULL) {
148 /* convert all clock information to MHz */
149 kbd->bi_intfreq /= 1000000L;
150 kbd->bi_busfreq /= 1000000L;
151#if defined(CONFIG_MPC8220)
152 kbd->bi_inpfreq /= 1000000L;
153 kbd->bi_pcifreq /= 1000000L;
154 kbd->bi_pevfreq /= 1000000L;
155 kbd->bi_flbfreq /= 1000000L;
156 kbd->bi_vcofreq /= 1000000L;
157#endif
158#if defined(CONFIG_CPM2)
159 kbd->bi_cpmfreq /= 1000000L;
160 kbd->bi_brgfreq /= 1000000L;
161 kbd->bi_sccfreq /= 1000000L;
162 kbd->bi_vco /= 1000000L;
163#endif
164#if defined(CONFIG_MPC5xxx)
165 kbd->bi_ipbfreq /= 1000000L;
166 kbd->bi_pcifreq /= 1000000L;
167#endif /* CONFIG_MPC5xxx */
168 }
169
170 kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))image_get_ep (hdr);
171
172 /*
173 * Check if there is an initrd image
174 */
175
176#if defined(CONFIG_OF_FLAT_TREE) || defined(CONFIG_OF_LIBFDT)
177 /* Look for a '-' which indicates to ignore the ramdisk argument */
178 if (argc >= 3 && strcmp(argv[2], "-") == 0) {
179 debug ("Skipping initrd\n");
f13e7b2e 180 rd_len = rd_data = 0;
5d3cc55e
MB
181 }
182 else
183#endif
184 if (argc >= 3) {
185 debug ("Not skipping initrd\n");
186 show_boot_progress (9);
187
f13e7b2e
MB
188 rd_hdr = (image_header_t *)simple_strtoul (argv[2], NULL, 16);
189 printf ("## Loading RAMDisk Image at %08lx ...\n", (ulong)rd_hdr);
5d3cc55e 190
f13e7b2e 191 if (!image_check_magic (rd_hdr)) {
5d3cc55e
MB
192 puts ("Bad Magic Number\n");
193 show_boot_progress (-10);
194 do_reset (cmdtp, flag, argc, argv);
195 }
196
f13e7b2e 197 if (!image_check_hcrc (rd_hdr)) {
5d3cc55e
MB
198 puts ("Bad Header Checksum\n");
199 show_boot_progress (-11);
200 do_reset (cmdtp, flag, argc, argv);
201 }
202 show_boot_progress (10);
203
f13e7b2e 204 print_image_hdr (rd_hdr);
5d3cc55e
MB
205
206 if (verify) {
207 puts (" Verifying Checksum ... ");
208
f13e7b2e 209 if (!image_check_dcrc_wd (rd_hdr, CHUNKSZ)) {
5d3cc55e
MB
210 puts ("Bad Data CRC\n");
211 show_boot_progress (-12);
212 do_reset (cmdtp, flag, argc, argv);
213 }
214 puts ("OK\n");
215 }
216
217 show_boot_progress (11);
218
f13e7b2e
MB
219 if (!image_check_os (rd_hdr, IH_OS_LINUX) ||
220 !image_check_arch (rd_hdr, IH_ARCH_PPC) ||
221 !image_check_type (rd_hdr, IH_TYPE_RAMDISK)) {
5d3cc55e
MB
222 puts ("No Linux PPC Ramdisk Image\n");
223 show_boot_progress (-13);
224 do_reset (cmdtp, flag, argc, argv);
225 }
226
f13e7b2e
MB
227 rd_data = image_get_data (rd_hdr);
228 rd_len = image_get_data_size (rd_hdr);
5d3cc55e
MB
229
230 /*
231 * Now check if we have a multifile image
232 */
f13e7b2e
MB
233 } else if (image_check_type (hdr, IH_TYPE_MULTI)) {
234 /*
235 * Get second entry data start address and len
236 */
237 image_multi_getimg (hdr, 1, &rd_data, &rd_len);
5d3cc55e 238 show_boot_progress (13);
5d3cc55e
MB
239 } else {
240 /*
f13e7b2e 241 * No initrd image
5d3cc55e
MB
242 */
243 show_boot_progress (14);
244
f13e7b2e 245 rd_len = rd_data = 0;
5d3cc55e
MB
246 }
247
248#if defined(CONFIG_OF_FLAT_TREE) || defined(CONFIG_OF_LIBFDT)
249 if(argc > 3) {
250 of_flat_tree = (char *) simple_strtoul(argv[3], NULL, 16);
f13e7b2e 251 fdt_hdr = (image_header_t *)of_flat_tree;
5d3cc55e 252#if defined(CONFIG_OF_FLAT_TREE)
958fc48a 253 if (*((ulong *)(of_flat_tree)) == OF_DT_HEADER) {
d45d5a18 254#elif defined(CONFIG_OF_LIBFDT)
958fc48a 255 if (fdt_check_header (of_flat_tree) == 0) {
5d3cc55e
MB
256#endif
257#ifndef CFG_NO_FLASH
258 if (addr2info((ulong)of_flat_tree) != NULL)
259 of_data = (ulong)of_flat_tree;
260#endif
f13e7b2e
MB
261 } else if (image_check_magic (fdt_hdr)) {
262 printf ("## Flat Device Tree at %08lX\n", fdt_hdr);
263 print_image_hdr (fdt_hdr);
5d3cc55e 264
f13e7b2e
MB
265 if ((image_get_load (fdt_hdr) < image_get_image_end (fdt_hdr)) &&
266 ((image_get_load (fdt_hdr) + image_get_data_size (fdt_hdr)) > (unsigned long)fdt_hdr)) {
5d3cc55e
MB
267 puts ("ERROR: fdt overwritten - "
268 "must RESET the board to recover.\n");
269 do_reset (cmdtp, flag, argc, argv);
270 }
271
272 puts (" Verifying Checksum ... ");
f13e7b2e 273 if (!image_check_hcrc (fdt_hdr)) {
5d3cc55e
MB
274 puts ("ERROR: fdt header checksum invalid - "
275 "must RESET the board to recover.\n");
276 do_reset (cmdtp, flag, argc, argv);
277 }
278
f13e7b2e 279 if (!image_check_dcrc (fdt_hdr)) {
5d3cc55e
MB
280 puts ("ERROR: fdt checksum invalid - "
281 "must RESET the board to recover.\n");
282 do_reset (cmdtp, flag, argc, argv);
283 }
284 puts ("OK\n");
285
f13e7b2e 286 if (!image_check_type (fdt_hdr, IH_TYPE_FLATDT)) {
5d3cc55e
MB
287 puts ("ERROR: uImage is not a fdt - "
288 "must RESET the board to recover.\n");
289 do_reset (cmdtp, flag, argc, argv);
290 }
f13e7b2e 291 if (image_get_comp (fdt_hdr) != IH_COMP_NONE) {
5d3cc55e
MB
292 puts ("ERROR: uImage is compressed - "
293 "must RESET the board to recover.\n");
294 do_reset (cmdtp, flag, argc, argv);
295 }
296#if defined(CONFIG_OF_FLAT_TREE)
297 if (*((ulong *)(of_flat_tree + image_get_header_size ())) != OF_DT_HEADER) {
d45d5a18 298#elif defined(CONFIG_OF_LIBFDT)
5d3cc55e
MB
299 if (fdt_check_header (of_flat_tree + image_get_header_size ()) != 0) {
300#endif
301 puts ("ERROR: uImage data is not a fdt - "
302 "must RESET the board to recover.\n");
303 do_reset (cmdtp, flag, argc, argv);
304 }
305
f13e7b2e 306 memmove ((void *)image_get_load (fdt_hdr),
5d3cc55e 307 (void *)(of_flat_tree + image_get_header_size ()),
f13e7b2e 308 image_get_data_size (fdt_hdr));
5d3cc55e 309
f13e7b2e 310 of_flat_tree = (char *)image_get_load (fdt_hdr);
5d3cc55e
MB
311 } else {
312 puts ("Did not find a flat Flat Device Tree.\n"
313 "Must RESET the board to recover.\n");
314 do_reset (cmdtp, flag, argc, argv);
315 }
316 printf (" Booting using the fdt at 0x%x\n",
317 of_flat_tree);
f13e7b2e
MB
318 } else if (image_check_type (hdr, IH_TYPE_MULTI)) {
319 ulong fdt_data, fdt_len;
5d3cc55e 320
f13e7b2e
MB
321 image_multi_getimg (hdr, 2, &fdt_data, &fdt_len);
322 if (fdt_len) {
323
324 of_flat_tree = (char *)fdt_data;
5d3cc55e
MB
325
326#ifndef CFG_NO_FLASH
f13e7b2e
MB
327 /* move the blob if it is in flash (set of_data to !null) */
328 if (addr2info ((ulong)of_flat_tree) != NULL)
329 of_data = (ulong)of_flat_tree;
5d3cc55e
MB
330#endif
331
5d3cc55e 332#if defined(CONFIG_OF_FLAT_TREE)
f13e7b2e 333 if (*((ulong *)(of_flat_tree)) != OF_DT_HEADER) {
d45d5a18 334#elif defined(CONFIG_OF_LIBFDT)
f13e7b2e 335 if (fdt_check_header (of_flat_tree) != 0) {
5d3cc55e 336#endif
f13e7b2e
MB
337 puts ("ERROR: image is not a fdt - "
338 "must RESET the board to recover.\n");
339 do_reset (cmdtp, flag, argc, argv);
340 }
5d3cc55e
MB
341
342#if defined(CONFIG_OF_FLAT_TREE)
f13e7b2e 343 if (((struct boot_param_header *)of_flat_tree)->totalsize != fdt_len) {
d45d5a18 344#elif defined(CONFIG_OF_LIBFDT)
f13e7b2e 345 if (be32_to_cpu (fdt_totalsize (of_flat_tree)) != fdt_len) {
5d3cc55e 346#endif
f13e7b2e
MB
347 puts ("ERROR: fdt size != image size - "
348 "must RESET the board to recover.\n");
349 do_reset (cmdtp, flag, argc, argv);
350 }
5d3cc55e
MB
351 }
352 }
353#endif
f13e7b2e 354 if (!rd_data) {
5d3cc55e
MB
355 debug ("No initrd\n");
356 }
357
f13e7b2e 358 if (rd_data) {
5d3cc55e 359 if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */
f13e7b2e
MB
360 initrd_start = rd_data;
361 initrd_end = initrd_start + rd_len;
5d3cc55e 362 } else {
f13e7b2e 363 initrd_start = (ulong)kbd - rd_len;
5d3cc55e
MB
364 initrd_start &= ~(4096 - 1); /* align on page */
365
366 if (initrd_high) {
367 ulong nsp;
368
369 /*
370 * the inital ramdisk does not need to be within
371 * CFG_BOOTMAPSZ as it is not accessed until after
372 * the mm system is initialised.
373 *
374 * do the stack bottom calculation again and see if
375 * the initrd will fit just below the monitor stack
376 * bottom without overwriting the area allocated
377 * above for command line args and board info.
378 */
379 asm( "mr %0,1": "=r"(nsp) : );
380 nsp -= 2048; /* just to be sure */
381 nsp &= ~0xF;
382 if (nsp > initrd_high) /* limit as specified */
383 nsp = initrd_high;
f13e7b2e 384 nsp -= rd_len;
5d3cc55e
MB
385 nsp &= ~(4096 - 1); /* align on page */
386 if (nsp >= sp)
387 initrd_start = nsp;
388 }
389
390 show_boot_progress (12);
391
392 debug ("## initrd at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
f13e7b2e 393 rd_data, rd_data + rd_len - 1, rd_len, rd_len);
5d3cc55e 394
f13e7b2e 395 initrd_end = initrd_start + rd_len;
5d3cc55e
MB
396 printf (" Loading Ramdisk to %08lx, end %08lx ... ",
397 initrd_start, initrd_end);
af13cdbc
MB
398
399 memmove_wd((void *)initrd_start,
f13e7b2e 400 (void *)rd_data, rd_len, CHUNKSZ);
af13cdbc 401
5d3cc55e
MB
402 puts ("OK\n");
403 }
404 } else {
405 initrd_start = 0;
406 initrd_end = 0;
407 }
408
409#if defined(CONFIG_OF_LIBFDT)
410
411#ifdef CFG_BOOTMAPSZ
412 /*
413 * The blob must be within CFG_BOOTMAPSZ,
414 * so we flag it to be copied if it is not.
415 */
416 if (of_flat_tree >= (char *)CFG_BOOTMAPSZ)
417 of_data = (ulong)of_flat_tree;
418#endif
419
420 /* move of_flat_tree if needed */
421 if (of_data) {
422 int err;
423 ulong of_start, of_len;
424
425 of_len = be32_to_cpu(fdt_totalsize(of_data));
426
427 /* position on a 4K boundary before the kbd */
428 of_start = (ulong)kbd - of_len;
429 of_start &= ~(4096 - 1); /* align on page */
430 debug ("## device tree at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
431 of_data, of_data + of_len - 1, of_len, of_len);
432
433 of_flat_tree = (char *)of_start;
434 printf (" Loading Device Tree to %08lx, end %08lx ... ",
435 of_start, of_start + of_len - 1);
436 err = fdt_open_into((void *)of_data, (void *)of_start, of_len);
437 if (err != 0) {
438 puts ("ERROR: fdt move failed - "
439 "must RESET the board to recover.\n");
440 do_reset (cmdtp, flag, argc, argv);
441 }
442 puts ("OK\n");
443 }
444 /*
445 * Add the chosen node if it doesn't exist, add the env and bd_t
446 * if the user wants it (the logic is in the subroutines).
447 */
448 if (of_flat_tree) {
449 if (fdt_chosen(of_flat_tree, initrd_start, initrd_end, 0) < 0) {
450 puts ("ERROR: /chosen node create failed - "
451 "must RESET the board to recover.\n");
452 do_reset (cmdtp, flag, argc, argv);
453 }
454#ifdef CONFIG_OF_HAS_UBOOT_ENV
455 if (fdt_env(of_flat_tree) < 0) {
456 puts ("ERROR: /u-boot-env node create failed - "
457 "must RESET the board to recover.\n");
458 do_reset (cmdtp, flag, argc, argv);
459 }
460#endif
461#ifdef CONFIG_OF_HAS_BD_T
462 if (fdt_bd_t(of_flat_tree) < 0) {
463 puts ("ERROR: /bd_t node create failed - "
464 "must RESET the board to recover.\n");
465 do_reset (cmdtp, flag, argc, argv);
466 }
467#endif
468#ifdef CONFIG_OF_BOARD_SETUP
469 /* Call the board-specific fixup routine */
470 ft_board_setup(of_flat_tree, gd->bd);
471#endif
472 }
d45d5a18
MB
473
474#elif defined(CONFIG_OF_FLAT_TREE)
475
5d3cc55e
MB
476#ifdef CFG_BOOTMAPSZ
477 /*
478 * The blob must be within CFG_BOOTMAPSZ,
479 * so we flag it to be copied if it is not.
480 */
481 if (of_flat_tree >= (char *)CFG_BOOTMAPSZ)
482 of_data = (ulong)of_flat_tree;
483#endif
484
485 /* move of_flat_tree if needed */
486 if (of_data) {
487 ulong of_start, of_len;
488 of_len = ((struct boot_param_header *)of_data)->totalsize;
489
490 /* provide extra 8k pad */
491 of_start = (ulong)kbd - of_len - 8192;
492 of_start &= ~(4096 - 1); /* align on page */
493 debug ("## device tree at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
494 of_data, of_data + of_len - 1, of_len, of_len);
495
496 of_flat_tree = (char *)of_start;
497 printf (" Loading Device Tree to %08lx, end %08lx ... ",
498 of_start, of_start + of_len - 1);
499 memmove ((void *)of_start, (void *)of_data, of_len);
500 puts ("OK\n");
501 }
502 /*
503 * Create the /chosen node and modify the blob with board specific
504 * values as needed.
505 */
506 ft_setup(of_flat_tree, kbd, initrd_start, initrd_end);
507 /* ft_dump_blob(of_flat_tree); */
d45d5a18
MB
508
509#endif /* #if defined(CONFIG_OF_LIBFDT) #elif defined(CONFIG_OF_FLAT_TREE) */
510
5d3cc55e
MB
511 debug ("## Transferring control to Linux (at address %08lx) ...\n",
512 (ulong)kernel);
513
514 show_boot_progress (15);
515
516#if defined(CFG_INIT_RAM_LOCK) && !defined(CONFIG_E500)
517 unlock_ram_in_cache();
518#endif
519
520#if defined(CONFIG_OF_FLAT_TREE) || defined(CONFIG_OF_LIBFDT)
521 if (of_flat_tree) { /* device tree; boot new style */
522 /*
523 * Linux Kernel Parameters (passing device tree):
524 * r3: pointer to the fdt, followed by the board info data
525 * r4: physical pointer to the kernel itself
526 * r5: NULL
527 * r6: NULL
528 * r7: NULL
529 */
530 (*kernel) ((bd_t *)of_flat_tree, (ulong)kernel, 0, 0, 0);
531 /* does not return */
532 }
533#endif
534 /*
535 * Linux Kernel Parameters (passing board info data):
536 * r3: ptr to board info data
537 * r4: initrd_start or 0 if no initrd
538 * r5: initrd_end - unused if r4 is 0
539 * r6: Start of command line string
540 * r7: End of command line string
541 */
542 (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end);
543 /* does not return */
544}