--- /dev/null
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Boot support
+ */
+#include <common.h>
+#include <watchdog.h>
+#include <command.h>
+#include <image.h>
+#include <malloc.h>
+#include <zlib.h>
+#include <bzlib.h>
+#include <environment.h>
+#include <asm/byteorder.h>
+
+ /*cmd_boot.c*/
+ extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+
+#if (CONFIG_COMMANDS & CFG_CMD_DATE) || defined(CONFIG_TIMESTAMP)
+#include <rtc.h>
+#endif
+
+#ifdef CFG_HUSH_PARSER
+#include <hush.h>
+#endif
+
+#ifdef CONFIG_SHOW_BOOT_PROGRESS
+# include <status_led.h>
+# define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg)
+#else
+# define SHOW_BOOT_PROGRESS(arg)
+#endif
+
+#ifdef CFG_INIT_RAM_LOCK
+#include <asm/cache.h>
+#endif
+
+#ifdef CONFIG_LOGBUFFER
+#include <logbuff.h>
+#endif
+
+#ifdef CONFIG_HAS_DATAFLASH
+#include <dataflash.h>
+#endif
+
+/*
+ * Some systems (for example LWMON) have very short watchdog periods;
+ * we must make sure to split long operations like memmove() or
+ * crc32() into reasonable chunks.
+ */
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+# define CHUNKSZ (64 * 1024)
+#endif
+
+int gunzip (void *, int, unsigned char *, unsigned long *);
+
+static void *zalloc(void *, unsigned, unsigned);
+static void zfree(void *, void *, unsigned);
+
+#if (CONFIG_COMMANDS & CFG_CMD_IMI)
+static int image_info (unsigned long addr);
+#endif
+
+#if (CONFIG_COMMANDS & CFG_CMD_IMLS)
+#include <flash.h>
+extern flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */
+static int do_imls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+#endif
+
+static void print_type (image_header_t *hdr);
+
+#ifdef __I386__
+image_header_t *fake_header(image_header_t *hdr, void *ptr, int size);
+#endif
+
+/*
+ * Continue booting an OS image; caller already has:
+ * - copied image header to global variable `header'
+ * - checked header magic number, checksums (both header & image),
+ * - verified image architecture (PPC) and type (KERNEL or MULTI),
+ * - loaded (first part of) image to header load address,
+ * - disabled interrupts.
+ */
+typedef void boot_os_Fcn (cmd_tbl_t *cmdtp, int flag,
+ int argc, char *argv[],
+ ulong addr, /* of image to boot */
+ ulong *len_ptr, /* multi-file image length table */
+ int verify); /* getenv("verify")[0] != 'n' */
+
+#ifdef DEBUG
+extern int do_bdinfo ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+#endif
+
+#ifdef CONFIG_PPC
+static boot_os_Fcn do_bootm_linux;
+#else
+extern boot_os_Fcn do_bootm_linux;
+#endif
+#ifdef CONFIG_SILENT_CONSOLE
+static void fixup_silent_linux (void);
+#endif
+static boot_os_Fcn do_bootm_netbsd;
+static boot_os_Fcn do_bootm_rtems;
+#if (CONFIG_COMMANDS & CFG_CMD_ELF)
+static boot_os_Fcn do_bootm_vxworks;
+static boot_os_Fcn do_bootm_qnxelf;
+int do_bootvx ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[] );
+int do_bootelf (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[] );
+#endif /* CFG_CMD_ELF */
+#if defined(CONFIG_ARTOS) && defined(CONFIG_PPC)
+static boot_os_Fcn do_bootm_artos;
+#endif
+#ifdef CONFIG_LYNXKDI
+static boot_os_Fcn do_bootm_lynxkdi;
+extern void lynxkdi_boot( image_header_t * );
+#endif
+
+image_header_t header;
+
+ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */
+
+int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ ulong iflag;
+ ulong addr;
+ ulong data, len, checksum;
+ ulong *len_ptr;
+ uint unc_len = 0x400000;
+ int i, verify;
+ char *name, *s;
+ int (*appl)(int, char *[]);
+ image_header_t *hdr = &header;
+
+ s = getenv ("verify");
+ verify = (s && (*s == 'n')) ? 0 : 1;
+
+ if (argc < 2) {
+ addr = load_addr;
+ } else {
+ addr = simple_strtoul(argv[1], NULL, 16);
+ }
+
+ SHOW_BOOT_PROGRESS (1);
+ printf ("## Booting image at %08lx ...\n", addr);
+
+ /* Copy header so we can blank CRC field for re-calculation */
+#ifdef CONFIG_HAS_DATAFLASH
+ if (addr_dataflash(addr)){
+ read_dataflash(addr, sizeof(image_header_t), (char *)&header);
+ } else
+#endif
+ memmove (&header, (char *)addr, sizeof(image_header_t));
+
+ if (ntohl(hdr->ih_magic) != IH_MAGIC) {
+#ifdef __I386__ /* correct image format not implemented yet - fake it */
+ if (fake_header(hdr, (void*)addr, -1) != NULL) {
+ /* to compensate for the addition below */
+ addr -= sizeof(image_header_t);
+ /* turnof verify,
+ * fake_header() does not fake the data crc
+ */
+ verify = 0;
+ } else
+#endif /* __I386__ */
+ {
+ puts ("Bad Magic Number\n");
+ SHOW_BOOT_PROGRESS (-1);
+ return 1;
+ }
+ }
+ SHOW_BOOT_PROGRESS (2);
+
+ data = (ulong)&header;
+ len = sizeof(image_header_t);
+
+ checksum = ntohl(hdr->ih_hcrc);
+ hdr->ih_hcrc = 0;
+
+ if (crc32 (0, (char *)data, len) != checksum) {
+ puts ("Bad Header Checksum\n");
+ SHOW_BOOT_PROGRESS (-2);
+ return 1;
+ }
+ SHOW_BOOT_PROGRESS (3);
+
+#ifdef CONFIG_HAS_DATAFLASH
+ if (addr_dataflash(addr)){
+ len = ntohl(hdr->ih_size) + sizeof(image_header_t);
+ read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
+ addr = CFG_LOAD_ADDR;
+ }
+#endif
+
+
+ /* for multi-file images we need the data part, too */
+ print_image_hdr ((image_header_t *)addr);
+
+ data = addr + sizeof(image_header_t);
+ len = ntohl(hdr->ih_size);
+
+ if (verify) {
+ puts (" Verifying Checksum ... ");
+ if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
+ printf ("Bad Data CRC\n");
+ SHOW_BOOT_PROGRESS (-3);
+ return 1;
+ }
+ puts ("OK\n");
+ }
+ SHOW_BOOT_PROGRESS (4);
+
+ len_ptr = (ulong *)data;
+
+#if defined(__PPC__)
+ if (hdr->ih_arch != IH_CPU_PPC)
+#elif defined(__ARM__)
+ if (hdr->ih_arch != IH_CPU_ARM)
+#elif defined(__I386__)
+ if (hdr->ih_arch != IH_CPU_I386)
+#elif defined(__mips__)
+ if (hdr->ih_arch != IH_CPU_MIPS)
+#elif defined(__nios__)
+ if (hdr->ih_arch != IH_CPU_NIOS)
+#elif defined(__M68K__)
+ if (hdr->ih_arch != IH_CPU_M68K)
+#elif defined(__microblaze__)
+ if (hdr->ih_arch != IH_CPU_MICROBLAZE)
+#elif defined(__nios2__)
+ if (hdr->ih_arch != IH_CPU_NIOS2)
+#else
+# error Unknown CPU type
+#endif
+ {
+ printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
+ SHOW_BOOT_PROGRESS (-4);
+ return 1;
+ }
+ SHOW_BOOT_PROGRESS (5);
+
+ switch (hdr->ih_type) {
+ case IH_TYPE_STANDALONE:
+ name = "Standalone Application";
+ /* A second argument overwrites the load address */
+ if (argc > 2) {
+ hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
+ }
+ break;
+ case IH_TYPE_KERNEL:
+ name = "Kernel Image";
+ break;
+ case IH_TYPE_MULTI:
+ name = "Multi-File Image";
+ len = ntohl(len_ptr[0]);
+ /* OS kernel is always the first image */
+ data += 8; /* kernel_len + terminator */
+ for (i=1; len_ptr[i]; ++i)
+ data += 4;
+ break;
+ default: printf ("Wrong Image Type for %s command\n", cmdtp->name);
+ SHOW_BOOT_PROGRESS (-5);
+ return 1;
+ }
+ SHOW_BOOT_PROGRESS (6);
+
+ /*
+ * We have reached the point of no return: we are going to
+ * overwrite all exception vector code, so we cannot easily
+ * recover from any failures any more...
+ */
+
+ iflag = disable_interrupts();
+
+#ifdef CONFIG_AMIGAONEG3SE
+ /*
+ * We've possible left the caches enabled during
+ * bios emulation, so turn them off again
+ */
+ icache_disable();
+ invalidate_l1_instruction_cache();
+ flush_data_cache();
+ dcache_disable();
+#endif
+
+ switch (hdr->ih_comp) {
+ case IH_COMP_NONE:
+ if(ntohl(hdr->ih_load) == addr) {
+ printf (" XIP %s ... ", name);
+ } else {
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ size_t l = len;
+ void *to = (void *)ntohl(hdr->ih_load);
+ void *from = (void *)data;
+
+ printf (" Loading %s ... ", name);
+
+ while (l > 0) {
+ size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
+ WATCHDOG_RESET();
+ memmove (to, from, tail);
+ to += tail;
+ from += tail;
+ l -= tail;
+ }
+#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
+ memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
+#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+ }
+ break;
+ case IH_COMP_GZIP:
+ printf (" Uncompressing %s ... ", name);
+ if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
+ (uchar *)data, &len) != 0) {
+ puts ("GUNZIP ERROR - must RESET board to recover\n");
+ SHOW_BOOT_PROGRESS (-6);
+ do_reset (cmdtp, flag, argc, argv);
+ }
+ break;
+#ifdef CONFIG_BZIP2
+ case IH_COMP_BZIP2:
+ printf (" Uncompressing %s ... ", name);
+ /*
+ * If we've got less than 4 MB of malloc() space,
+ * use slower decompression algorithm which requires
+ * at most 2300 KB of memory.
+ */
+ i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
+ &unc_len, (char *)data, len,
+ CFG_MALLOC_LEN < (4096 * 1024), 0);
+ if (i != BZ_OK) {
+ printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
+ SHOW_BOOT_PROGRESS (-6);
+ udelay(100000);
+ do_reset (cmdtp, flag, argc, argv);
+ }
+ break;
+#endif /* CONFIG_BZIP2 */
+ default:
+ if (iflag)
+ enable_interrupts();
+ printf ("Unimplemented compression type %d\n", hdr->ih_comp);
+ SHOW_BOOT_PROGRESS (-7);
+ return 1;
+ }
+ puts ("OK\n");
+ SHOW_BOOT_PROGRESS (7);
+
+ switch (hdr->ih_type) {
+ case IH_TYPE_STANDALONE:
+ if (iflag)
+ enable_interrupts();
+
+ /* load (and uncompress), but don't start if "autostart"
+ * is set to "no"
+ */
+ if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
+ char buf[32];
+ sprintf(buf, "%lX", len);
+ setenv("filesize", buf);
+ return 0;
+ }
+ appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
+ (*appl)(argc-1, &argv[1]);
+ return 0;
+ case IH_TYPE_KERNEL:
+ case IH_TYPE_MULTI:
+ /* handled below */
+ break;
+ default:
+ if (iflag)
+ enable_interrupts();
+ printf ("Can't boot image type %d\n", hdr->ih_type);
+ SHOW_BOOT_PROGRESS (-8);
+ return 1;
+ }
+ SHOW_BOOT_PROGRESS (8);
+
+ switch (hdr->ih_os) {
+ default: /* handled by (original) Linux case */
+ case IH_OS_LINUX:
+#ifdef CONFIG_SILENT_CONSOLE
+ fixup_silent_linux();
+#endif
+ do_bootm_linux (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+ case IH_OS_NETBSD:
+ do_bootm_netbsd (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+
+#ifdef CONFIG_LYNXKDI
+ case IH_OS_LYNXOS:
+ do_bootm_lynxkdi (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+#endif
+
+ case IH_OS_RTEMS:
+ do_bootm_rtems (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+
+#if (CONFIG_COMMANDS & CFG_CMD_ELF)
+ case IH_OS_VXWORKS:
+ do_bootm_vxworks (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+ case IH_OS_QNX:
+ do_bootm_qnxelf (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+#endif /* CFG_CMD_ELF */
+#ifdef CONFIG_ARTOS
+ case IH_OS_ARTOS:
+ do_bootm_artos (cmdtp, flag, argc, argv,
+ addr, len_ptr, verify);
+ break;
+#endif
+ }
+
+ SHOW_BOOT_PROGRESS (-9);
+#ifdef DEBUG
+ puts ("\n## Control returned to monitor - resetting...\n");
+ do_reset (cmdtp, flag, argc, argv);
+#endif
+ return 1;
+}
+
+U_BOOT_CMD(
+ bootm, CFG_MAXARGS, 1, do_bootm,
+ "bootm - boot application image from memory\n",
+ "[addr [arg ...]]\n - boot application image stored in memory\n"
+ "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
+ "\t'arg' can be the address of an initrd image\n"
+);
+
+#ifdef CONFIG_SILENT_CONSOLE
+static void
+fixup_silent_linux ()
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ char buf[256], *start, *end;
+ char *cmdline = getenv ("bootargs");
+
+ /* Only fix cmdline when requested */
+ if (!(gd->flags & GD_FLG_SILENT))
+ return;
+
+ debug ("before silent fix-up: %s\n", cmdline);
+ if (cmdline) {
+ if ((start = strstr (cmdline, "console=")) != NULL) {
+ end = strchr (start, ' ');
+ strncpy (buf, cmdline, (start - cmdline + 8));
+ if (end)
+ strcpy (buf + (start - cmdline + 8), end);
+ else
+ buf[start - cmdline + 8] = '\0';
+ } else {
+ strcpy (buf, cmdline);
+ strcat (buf, " console=");
+ }
+ } else {
+ strcpy (buf, "console=");
+ }
+
+ setenv ("bootargs", buf);
+ debug ("after silent fix-up: %s\n", buf);
+}
+#endif /* CONFIG_SILENT_CONSOLE */
+
+#ifdef CONFIG_PPC
+static void
+do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
+ int argc, char *argv[],
+ ulong addr,
+ ulong *len_ptr,
+ int verify)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ ulong sp;
+ ulong len, checksum;
+ ulong initrd_start, initrd_end;
+ ulong cmd_start, cmd_end;
+ ulong initrd_high;
+ ulong data;
+ int initrd_copy_to_ram = 1;
+ char *cmdline;
+ char *s;
+ bd_t *kbd;
+ void (*kernel)(bd_t *, ulong, ulong, ulong, ulong);
+ image_header_t *hdr = &header;
+
+ if ((s = getenv ("initrd_high")) != NULL) {
+ /* a value of "no" or a similar string will act like 0,
+ * turning the "load high" feature off. This is intentional.
+ */
+ initrd_high = simple_strtoul(s, NULL, 16);
+ if (initrd_high == ~0)
+ initrd_copy_to_ram = 0;
+ } else { /* not set, no restrictions to load high */
+ initrd_high = ~0;
+ }
+
+#ifdef CONFIG_LOGBUFFER
+ kbd=gd->bd;
+ /* Prevent initrd from overwriting logbuffer */
+ if (initrd_high < (kbd->bi_memsize-LOGBUFF_LEN-LOGBUFF_OVERHEAD))
+ initrd_high = kbd->bi_memsize-LOGBUFF_LEN-LOGBUFF_OVERHEAD;
+ debug ("## Logbuffer at 0x%08lX ", kbd->bi_memsize-LOGBUFF_LEN);
+#endif
+
+ /*
+ * Booting a (Linux) kernel image
+ *
+ * Allocate space for command line and board info - the
+ * address should be as high as possible within the reach of
+ * the kernel (see CFG_BOOTMAPSZ settings), but in unused
+ * memory, which means far enough below the current stack
+ * pointer.
+ */
+
+ asm( "mr %0,1": "=r"(sp) : );
+
+ debug ("## Current stack ends at 0x%08lX ", sp);
+
+ sp -= 2048; /* just to be sure */
+ if (sp > CFG_BOOTMAPSZ)
+ sp = CFG_BOOTMAPSZ;
+ sp &= ~0xF;
+
+ debug ("=> set upper limit to 0x%08lX\n", sp);
+
+ cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF);
+ kbd = (bd_t *)(((ulong)cmdline - sizeof(bd_t)) & ~0xF);
+
+ if ((s = getenv("bootargs")) == NULL)
+ s = "";
+
+ strcpy (cmdline, s);
+
+ cmd_start = (ulong)&cmdline[0];
+ cmd_end = cmd_start + strlen(cmdline);
+
+ *kbd = *(gd->bd);
+
+#ifdef DEBUG
+ printf ("## cmdline at 0x%08lX ... 0x%08lX\n", cmd_start, cmd_end);
+
+ do_bdinfo (NULL, 0, 0, NULL);
+#endif
+
+ if ((s = getenv ("clocks_in_mhz")) != NULL) {
+ /* convert all clock information to MHz */
+ kbd->bi_intfreq /= 1000000L;
+ kbd->bi_busfreq /= 1000000L;
+#if defined(CONFIG_MPC8220)
+ kbd->bi_inpfreq /= 1000000L;
+ kbd->bi_pcifreq /= 1000000L;
+ kbd->bi_pevfreq /= 1000000L;
+ kbd->bi_flbfreq /= 1000000L;
+ kbd->bi_vcofreq /= 1000000L;
+#endif
+#if defined(CONFIG_CPM2)
+ kbd->bi_cpmfreq /= 1000000L;
+ kbd->bi_brgfreq /= 1000000L;
+ kbd->bi_sccfreq /= 1000000L;
+ kbd->bi_vco /= 1000000L;
+#endif
+#if defined(CONFIG_MPC5xxx)
+ kbd->bi_ipbfreq /= 1000000L;
+ kbd->bi_pcifreq /= 1000000L;
+#endif /* CONFIG_MPC5xxx */
+ }
+
+ kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
+
+ /*
+ * Check if there is an initrd image
+ */
+ if (argc >= 3) {
+ SHOW_BOOT_PROGRESS (9);
+
+ addr = simple_strtoul(argv[2], NULL, 16);
+
+ printf ("## Loading RAMDisk Image at %08lx ...\n", addr);
+
+ /* Copy header so we can blank CRC field for re-calculation */
+ memmove (&header, (char *)addr, sizeof(image_header_t));
+
+ if (hdr->ih_magic != IH_MAGIC) {
+ puts ("Bad Magic Number\n");
+ SHOW_BOOT_PROGRESS (-10);
+ do_reset (cmdtp, flag, argc, argv);
+ }
+
+ data = (ulong)&header;
+ len = sizeof(image_header_t);
+
+ checksum = hdr->ih_hcrc;
+ hdr->ih_hcrc = 0;
+
+ if (crc32 (0, (char *)data, len) != checksum) {
+ puts ("Bad Header Checksum\n");
+ SHOW_BOOT_PROGRESS (-11);
+ do_reset (cmdtp, flag, argc, argv);
+ }
+
+ SHOW_BOOT_PROGRESS (10);
+
+ print_image_hdr (hdr);
+
+ data = addr + sizeof(image_header_t);
+ len = hdr->ih_size;
+
+ if (verify) {
+ ulong csum = 0;
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ ulong cdata = data, edata = cdata + len;
+#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+
+ puts (" Verifying Checksum ... ");
+
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+
+ while (cdata < edata) {
+ ulong chunk = edata - cdata;
+
+ if (chunk > CHUNKSZ)
+ chunk = CHUNKSZ;
+ csum = crc32 (csum, (char *)cdata, chunk);
+ cdata += chunk;
+
+ WATCHDOG_RESET();
+ }
+#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
+ csum = crc32 (0, (char *)data, len);
+#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+
+ if (csum != hdr->ih_dcrc) {
+ puts ("Bad Data CRC\n");
+ SHOW_BOOT_PROGRESS (-12);
+ do_reset (cmdtp, flag, argc, argv);
+ }
+ puts ("OK\n");
+ }
+
+ SHOW_BOOT_PROGRESS (11);
+
+ if ((hdr->ih_os != IH_OS_LINUX) ||
+ (hdr->ih_arch != IH_CPU_PPC) ||
+ (hdr->ih_type != IH_TYPE_RAMDISK) ) {
+ puts ("No Linux PPC Ramdisk Image\n");
+ SHOW_BOOT_PROGRESS (-13);
+ do_reset (cmdtp, flag, argc, argv);
+ }
+
+ /*
+ * Now check if we have a multifile image
+ */
+ } else if ((hdr->ih_type==IH_TYPE_MULTI) && (len_ptr[1])) {
+ u_long tail = ntohl(len_ptr[0]) % 4;
+ int i;
+
+ SHOW_BOOT_PROGRESS (13);
+
+ /* skip kernel length and terminator */
+ data = (ulong)(&len_ptr[2]);
+ /* skip any additional image length fields */
+ for (i=1; len_ptr[i]; ++i)
+ data += 4;
+ /* add kernel length, and align */
+ data += ntohl(len_ptr[0]);
+ if (tail) {
+ data += 4 - tail;
+ }
+
+ len = ntohl(len_ptr[1]);
+
+ } else {
+ /*
+ * no initrd image
+ */
+ SHOW_BOOT_PROGRESS (14);
+
+ len = data = 0;
+ }
+
+ if (!data) {
+ debug ("No initrd\n");
+ }
+
+ if (data) {
+ if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */
+ initrd_start = data;
+ initrd_end = initrd_start + len;
+ } else {
+ initrd_start = (ulong)kbd - len;
+ initrd_start &= ~(4096 - 1); /* align on page */
+
+ if (initrd_high) {
+ ulong nsp;
+
+ /*
+ * the inital ramdisk does not need to be within
+ * CFG_BOOTMAPSZ as it is not accessed until after
+ * the mm system is initialised.
+ *
+ * do the stack bottom calculation again and see if
+ * the initrd will fit just below the monitor stack
+ * bottom without overwriting the area allocated
+ * above for command line args and board info.
+ */
+ asm( "mr %0,1": "=r"(nsp) : );
+ nsp -= 2048; /* just to be sure */
+ nsp &= ~0xF;
+ if (nsp > initrd_high) /* limit as specified */
+ nsp = initrd_high;
+ nsp -= len;
+ nsp &= ~(4096 - 1); /* align on page */
+ if (nsp >= sp)
+ initrd_start = nsp;
+ }
+
+ SHOW_BOOT_PROGRESS (12);
+
+ debug ("## initrd at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
+ data, data + len - 1, len, len);
+
+ initrd_end = initrd_start + len;
+ printf (" Loading Ramdisk to %08lx, end %08lx ... ",
+ initrd_start, initrd_end);
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ {
+ size_t l = len;
+ void *to = (void *)initrd_start;
+ void *from = (void *)data;
+
+ while (l > 0) {
+ size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
+ WATCHDOG_RESET();
+ memmove (to, from, tail);
+ to += tail;
+ from += tail;
+ l -= tail;
+ }
+ }
+#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
+ memmove ((void *)initrd_start, (void *)data, len);
+#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+ puts ("OK\n");
+ }
+ } else {
+ initrd_start = 0;
+ initrd_end = 0;
+ }
+
+
+ debug ("## Transferring control to Linux (at address %08lx) ...\n",
+ (ulong)kernel);
+
+ SHOW_BOOT_PROGRESS (15);
+
+#if defined(CFG_INIT_RAM_LOCK) && !defined(CONFIG_E500)
+ unlock_ram_in_cache();
+#endif
+ /*
+ * Linux Kernel Parameters:
+ * r3: ptr to board info data
+ * r4: initrd_start or 0 if no initrd
+ * r5: initrd_end - unused if r4 is 0
+ * r6: Start of command line string
+ * r7: End of command line string
+ */
+ (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end);
+}
+#endif /* CONFIG_PPC */
+
+static void
+do_bootm_netbsd (cmd_tbl_t *cmdtp, int flag,
+ int argc, char *argv[],
+ ulong addr,
+ ulong *len_ptr,
+ int verify)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ image_header_t *hdr = &header;
+
+ void (*loader)(bd_t *, image_header_t *, char *, char *);
+ image_header_t *img_addr;
+ char *consdev;
+ char *cmdline;
+
+
+ /*
+ * Booting a (NetBSD) kernel image
+ *
+ * This process is pretty similar to a standalone application:
+ * The (first part of an multi-) image must be a stage-2 loader,
+ * which in turn is responsible for loading & invoking the actual
+ * kernel. The only differences are the parameters being passed:
+ * besides the board info strucure, the loader expects a command
+ * line, the name of the console device, and (optionally) the
+ * address of the original image header.
+ */
+
+ img_addr = 0;
+ if ((hdr->ih_type==IH_TYPE_MULTI) && (len_ptr[1]))
+ img_addr = (image_header_t *) addr;
+
+
+ consdev = "";
+#if defined (CONFIG_8xx_CONS_SMC1)
+ consdev = "smc1";
+#elif defined (CONFIG_8xx_CONS_SMC2)
+ consdev = "smc2";
+#elif defined (CONFIG_8xx_CONS_SCC2)
+ consdev = "scc2";
+#elif defined (CONFIG_8xx_CONS_SCC3)
+ consdev = "scc3";
+#endif
+
+ if (argc > 2) {
+ ulong len;
+ int i;
+
+ for (i=2, len=0 ; i<argc ; i+=1)
+ len += strlen (argv[i]) + 1;
+ cmdline = malloc (len);
+
+ for (i=2, len=0 ; i<argc ; i+=1) {
+ if (i > 2)
+ cmdline[len++] = ' ';
+ strcpy (&cmdline[len], argv[i]);
+ len += strlen (argv[i]);
+ }
+ } else if ((cmdline = getenv("bootargs")) == NULL) {
+ cmdline = "";
+ }
+
+ loader = (void (*)(bd_t *, image_header_t *, char *, char *)) hdr->ih_ep;
+
+ printf ("## Transferring control to NetBSD stage-2 loader (at address %08lx) ...\n",
+ (ulong)loader);
+
+ SHOW_BOOT_PROGRESS (15);
+
+ /*
+ * NetBSD Stage-2 Loader Parameters:
+ * r3: ptr to board info data
+ * r4: image address
+ * r5: console device
+ * r6: boot args string
+ */
+ (*loader) (gd->bd, img_addr, consdev, cmdline);
+}
+
+#if defined(CONFIG_ARTOS) && defined(CONFIG_PPC)
+
+/* Function that returns a character from the environment */
+extern uchar (*env_get_char)(int);
+
+static void
+do_bootm_artos (cmd_tbl_t *cmdtp, int flag,
+ int argc, char *argv[],
+ ulong addr,
+ ulong *len_ptr,
+ int verify)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ ulong top;
+ char *s, *cmdline;
+ char **fwenv, **ss;
+ int i, j, nxt, len, envno, envsz;
+ bd_t *kbd;
+ void (*entry)(bd_t *bd, char *cmdline, char **fwenv, ulong top);
+ image_header_t *hdr = &header;
+
+ /*
+ * Booting an ARTOS kernel image + application
+ */
+
+ /* this used to be the top of memory, but was wrong... */
+#ifdef CONFIG_PPC
+ /* get stack pointer */
+ asm volatile ("mr %0,1" : "=r"(top) );
+#endif
+ debug ("## Current stack ends at 0x%08lX ", top);
+
+ top -= 2048; /* just to be sure */
+ if (top > CFG_BOOTMAPSZ)
+ top = CFG_BOOTMAPSZ;
+ top &= ~0xF;
+
+ debug ("=> set upper limit to 0x%08lX\n", top);
+
+ /* first check the artos specific boot args, then the linux args*/
+ if ((s = getenv("abootargs")) == NULL && (s = getenv("bootargs")) == NULL)
+ s = "";
+
+ /* get length of cmdline, and place it */
+ len = strlen(s);
+ top = (top - (len + 1)) & ~0xF;
+ cmdline = (char *)top;
+ debug ("## cmdline at 0x%08lX ", top);
+ strcpy(cmdline, s);
+
+ /* copy bdinfo */
+ top = (top - sizeof(bd_t)) & ~0xF;
+ debug ("## bd at 0x%08lX ", top);
+ kbd = (bd_t *)top;
+ memcpy(kbd, gd->bd, sizeof(bd_t));
+
+ /* first find number of env entries, and their size */
+ envno = 0;
+ envsz = 0;
+ for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
+ for (nxt = i; env_get_char(nxt) != '\0'; ++nxt)
+ ;
+ envno++;
+ envsz += (nxt - i) + 1; /* plus trailing zero */
+ }
+ envno++; /* plus the terminating zero */
+ debug ("## %u envvars total size %u ", envno, envsz);
+
+ top = (top - sizeof(char **)*envno) & ~0xF;
+ fwenv = (char **)top;
+ debug ("## fwenv at 0x%08lX ", top);
+
+ top = (top - envsz) & ~0xF;
+ s = (char *)top;
+ ss = fwenv;
+
+ /* now copy them */
+ for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
+ for (nxt = i; env_get_char(nxt) != '\0'; ++nxt)
+ ;
+ *ss++ = s;
+ for (j = i; j < nxt; ++j)
+ *s++ = env_get_char(j);
+ *s++ = '\0';
+ }
+ *ss++ = NULL; /* terminate */
+
+ entry = (void (*)(bd_t *, char *, char **, ulong))ntohl(hdr->ih_ep);
+ (*entry)(kbd, cmdline, fwenv, top);
+}
+#endif
+
+
+#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
+int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int rcode = 0;
+#ifndef CFG_HUSH_PARSER
+ if (run_command (getenv ("bootcmd"), flag) < 0) rcode = 1;
+#else
+ if (parse_string_outer(getenv("bootcmd"),
+ FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0 ) rcode = 1;
+#endif
+ return rcode;
+}
+
+U_BOOT_CMD(
+ boot, 1, 1, do_bootd,
+ "boot - boot default, i.e., run 'bootcmd'\n",
+ NULL
+);
+
+/* keep old command name "bootd" for backward compatibility */
+U_BOOT_CMD(
+ bootd, 1, 1, do_bootd,
+ "bootd - boot default, i.e., run 'bootcmd'\n",
+ NULL
+);
+
+#endif
+
+#if (CONFIG_COMMANDS & CFG_CMD_IMI)
+int do_iminfo ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int arg;
+ ulong addr;
+ int rcode=0;
+
+ if (argc < 2) {
+ return image_info (load_addr);
+ }
+
+ for (arg=1; arg <argc; ++arg) {
+ addr = simple_strtoul(argv[arg], NULL, 16);
+ if (image_info (addr) != 0) rcode = 1;
+ }
+ return rcode;
+}
+
+static int image_info (ulong addr)
+{
+ ulong data, len, checksum;
+ image_header_t *hdr = &header;
+
+ printf ("\n## Checking Image at %08lx ...\n", addr);
+
+ /* Copy header so we can blank CRC field for re-calculation */
+ memmove (&header, (char *)addr, sizeof(image_header_t));
+
+ if (ntohl(hdr->ih_magic) != IH_MAGIC) {
+ puts (" Bad Magic Number\n");
+ return 1;
+ }
+
+ data = (ulong)&header;
+ len = sizeof(image_header_t);
+
+ checksum = ntohl(hdr->ih_hcrc);
+ hdr->ih_hcrc = 0;
+
+ if (crc32 (0, (char *)data, len) != checksum) {
+ puts (" Bad Header Checksum\n");
+ return 1;
+ }
+
+ /* for multi-file images we need the data part, too */
+ print_image_hdr ((image_header_t *)addr);
+
+ data = addr + sizeof(image_header_t);
+ len = ntohl(hdr->ih_size);
+
+ puts (" Verifying Checksum ... ");
+ if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
+ puts (" Bad Data CRC\n");
+ return 1;
+ }
+ puts ("OK\n");
+ return 0;
+}
+
+U_BOOT_CMD(
+ iminfo, CFG_MAXARGS, 1, do_iminfo,
+ "iminfo - print header information for application image\n",
+ "addr [addr ...]\n"
+ " - print header information for application image starting at\n"
+ " address 'addr' in memory; this includes verification of the\n"
+ " image contents (magic number, header and payload checksums)\n"
+);
+
+#endif /* CFG_CMD_IMI */
+
+#if (CONFIG_COMMANDS & CFG_CMD_IMLS)
+/*-----------------------------------------------------------------------
+ * List all images found in flash.
+ */
+int do_imls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ flash_info_t *info;
+ int i, j;
+ image_header_t *hdr;
+ ulong data, len, checksum;
+
+ for (i=0, info=&flash_info[0]; i<CFG_MAX_FLASH_BANKS; ++i, ++info) {
+ if (info->flash_id == FLASH_UNKNOWN)
+ goto next_bank;
+ for (j=0; j<CFG_MAX_FLASH_SECT; ++j) {
+
+ if (!(hdr=(image_header_t *)info->start[j]) ||
+ (ntohl(hdr->ih_magic) != IH_MAGIC))
+ goto next_sector;
+
+ /* Copy header so we can blank CRC field for re-calculation */
+ memmove (&header, (char *)hdr, sizeof(image_header_t));
+
+ checksum = ntohl(header.ih_hcrc);
+ header.ih_hcrc = 0;
+
+ if (crc32 (0, (char *)&header, sizeof(image_header_t))
+ != checksum)
+ goto next_sector;
+
+ printf ("Image at %08lX:\n", (ulong)hdr);
+ print_image_hdr( hdr );
+
+ data = (ulong)hdr + sizeof(image_header_t);
+ len = ntohl(hdr->ih_size);
+
+ puts (" Verifying Checksum ... ");
+ if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
+ puts (" Bad Data CRC\n");
+ }
+ puts ("OK\n");
+next_sector: ;
+ }
+next_bank: ;
+ }
+
+ return (0);
+}
+
+U_BOOT_CMD(
+ imls, 1, 1, do_imls,
+ "imls - list all images found in flash\n",
+ "\n"
+ " - Prints information about all images found at sector\n"
+ " boundaries in flash.\n"
+);
+#endif /* CFG_CMD_IMLS */
+
+void
+print_image_hdr (image_header_t *hdr)
+{
+#if (CONFIG_COMMANDS & CFG_CMD_DATE) || defined(CONFIG_TIMESTAMP)
+ time_t timestamp = (time_t)ntohl(hdr->ih_time);
+ struct rtc_time tm;
+#endif
+
+ printf (" Image Name: %.*s\n", IH_NMLEN, hdr->ih_name);
+#if (CONFIG_COMMANDS & CFG_CMD_DATE) || defined(CONFIG_TIMESTAMP)
+ to_tm (timestamp, &tm);
+ printf (" Created: %4d-%02d-%02d %2d:%02d:%02d UTC\n",
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+#endif /* CFG_CMD_DATE, CONFIG_TIMESTAMP */
+ puts (" Image Type: "); print_type(hdr);
+ printf ("\n Data Size: %d Bytes = ", ntohl(hdr->ih_size));
+ print_size (ntohl(hdr->ih_size), "\n");
+ printf (" Load Address: %08x\n"
+ " Entry Point: %08x\n",
+ ntohl(hdr->ih_load), ntohl(hdr->ih_ep));
+
+ if (hdr->ih_type == IH_TYPE_MULTI) {
+ int i;
+ ulong len;
+ ulong *len_ptr = (ulong *)((ulong)hdr + sizeof(image_header_t));
+
+ puts (" Contents:\n");
+ for (i=0; (len = ntohl(*len_ptr)); ++i, ++len_ptr) {
+ printf (" Image %d: %8ld Bytes = ", i, len);
+ print_size (len, "\n");
+ }
+ }
+}
+
+
+static void
+print_type (image_header_t *hdr)
+{
+ char *os, *arch, *type, *comp;
+
+ switch (hdr->ih_os) {
+ case IH_OS_INVALID: os = "Invalid OS"; break;
+ case IH_OS_NETBSD: os = "NetBSD"; break;
+ case IH_OS_LINUX: os = "Linux"; break;
+ case IH_OS_VXWORKS: os = "VxWorks"; break;
+ case IH_OS_QNX: os = "QNX"; break;
+ case IH_OS_U_BOOT: os = "U-Boot"; break;
+ case IH_OS_RTEMS: os = "RTEMS"; break;
+#ifdef CONFIG_ARTOS
+ case IH_OS_ARTOS: os = "ARTOS"; break;
+#endif
+#ifdef CONFIG_LYNXKDI
+ case IH_OS_LYNXOS: os = "LynxOS"; break;
+#endif
+ default: os = "Unknown OS"; break;
+ }
+
+ switch (hdr->ih_arch) {
+ case IH_CPU_INVALID: arch = "Invalid CPU"; break;
+ case IH_CPU_ALPHA: arch = "Alpha"; break;
+ case IH_CPU_ARM: arch = "ARM"; break;
+ case IH_CPU_I386: arch = "Intel x86"; break;
+ case IH_CPU_IA64: arch = "IA64"; break;
+ case IH_CPU_MIPS: arch = "MIPS"; break;
+ case IH_CPU_MIPS64: arch = "MIPS 64 Bit"; break;
+ case IH_CPU_PPC: arch = "PowerPC"; break;
+ case IH_CPU_S390: arch = "IBM S390"; break;
+ case IH_CPU_SH: arch = "SuperH"; break;
+ case IH_CPU_SPARC: arch = "SPARC"; break;
+ case IH_CPU_SPARC64: arch = "SPARC 64 Bit"; break;
+ case IH_CPU_M68K: arch = "M68K"; break;
+ case IH_CPU_MICROBLAZE: arch = "Microblaze"; break;
+ case IH_CPU_NIOS: arch = "Nios"; break;
+ case IH_CPU_NIOS2: arch = "Nios-II"; break;
+ default: arch = "Unknown Architecture"; break;
+ }
+
+ switch (hdr->ih_type) {
+ case IH_TYPE_INVALID: type = "Invalid Image"; break;
+ case IH_TYPE_STANDALONE:type = "Standalone Program"; break;
+ case IH_TYPE_KERNEL: type = "Kernel Image"; break;
+ case IH_TYPE_RAMDISK: type = "RAMDisk Image"; break;
+ case IH_TYPE_MULTI: type = "Multi-File Image"; break;
+ case IH_TYPE_FIRMWARE: type = "Firmware"; break;
+ case IH_TYPE_SCRIPT: type = "Script"; break;
+ default: type = "Unknown Image"; break;
+ }
+
+ switch (hdr->ih_comp) {
+ case IH_COMP_NONE: comp = "uncompressed"; break;
+ case IH_COMP_GZIP: comp = "gzip compressed"; break;
+ case IH_COMP_BZIP2: comp = "bzip2 compressed"; break;
+ default: comp = "unknown compression"; break;
+ }
+
+ printf ("%s %s %s (%s)", arch, os, type, comp);
+}
+
+#define ZALLOC_ALIGNMENT 16
+
+static void *zalloc(void *x, unsigned items, unsigned size)
+{
+ void *p;
+
+ size *= items;
+ size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
+
+ p = malloc (size);
+
+ return (p);
+}
+
+static void zfree(void *x, void *addr, unsigned nb)
+{
+ free (addr);
+}
+
+#define HEAD_CRC 2
+#define EXTRA_FIELD 4
+#define ORIG_NAME 8
+#define COMMENT 0x10
+#define RESERVED 0xe0
+
+#define DEFLATED 8
+
+int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
+{
+ z_stream s;
+ int r, i, flags;
+
+ /* skip header */
+ i = 10;
+ flags = src[3];
+ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+ puts ("Error: Bad gzipped data\n");
+ return (-1);
+ }
+ if ((flags & EXTRA_FIELD) != 0)
+ i = 12 + src[10] + (src[11] << 8);
+ if ((flags & ORIG_NAME) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & COMMENT) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & HEAD_CRC) != 0)
+ i += 2;
+ if (i >= *lenp) {
+ puts ("Error: gunzip out of data in header\n");
+ return (-1);
+ }
+
+ s.zalloc = zalloc;
+ s.zfree = zfree;
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ s.outcb = (cb_func)WATCHDOG_RESET;
+#else
+ s.outcb = Z_NULL;
+#endif /* CONFIG_HW_WATCHDOG */
+
+ r = inflateInit2(&s, -MAX_WBITS);
+ if (r != Z_OK) {
+ printf ("Error: inflateInit2() returned %d\n", r);
+ return (-1);
+ }
+ s.next_in = src + i;
+ s.avail_in = *lenp - i;
+ s.next_out = dst;
+ s.avail_out = dstlen;
+ r = inflate(&s, Z_FINISH);
+ if (r != Z_OK && r != Z_STREAM_END) {
+ printf ("Error: inflate() returned %d\n", r);
+ return (-1);
+ }
+ *lenp = s.next_out - (unsigned char *) dst;
+ inflateEnd(&s);
+
+ return (0);
+}
+
+#ifdef CONFIG_BZIP2
+void bz_internal_error(int errcode)
+{
+ printf ("BZIP2 internal error %d\n", errcode);
+}
+#endif /* CONFIG_BZIP2 */
+
+static void
+do_bootm_rtems (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
+ ulong addr, ulong *len_ptr, int verify)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ image_header_t *hdr = &header;
+ void (*entry_point)(bd_t *);
+
+ entry_point = (void (*)(bd_t *)) hdr->ih_ep;
+
+ printf ("## Transferring control to RTEMS (at address %08lx) ...\n",
+ (ulong)entry_point);
+
+ SHOW_BOOT_PROGRESS (15);
+
+ /*
+ * RTEMS Parameters:
+ * r3: ptr to board info data
+ */
+
+ (*entry_point ) ( gd->bd );
+}
+
+#if (CONFIG_COMMANDS & CFG_CMD_ELF)
+static void
+do_bootm_vxworks (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
+ ulong addr, ulong *len_ptr, int verify)
+{
+ image_header_t *hdr = &header;
+ char str[80];
+
+ sprintf(str, "%x", hdr->ih_ep); /* write entry-point into string */
+ setenv("loadaddr", str);
+ do_bootvx(cmdtp, 0, 0, NULL);
+}
+
+static void
+do_bootm_qnxelf (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
+ ulong addr, ulong *len_ptr, int verify)
+{
+ image_header_t *hdr = &header;
+ char *local_args[2];
+ char str[16];
+
+ sprintf(str, "%x", hdr->ih_ep); /* write entry-point into string */
+ local_args[0] = argv[0];
+ local_args[1] = str; /* and provide it via the arguments */
+ do_bootelf(cmdtp, 0, 2, local_args);
+}
+#endif /* CFG_CMD_ELF */
+
+#ifdef CONFIG_LYNXKDI
+static void
+do_bootm_lynxkdi (cmd_tbl_t *cmdtp, int flag,
+ int argc, char *argv[],
+ ulong addr,
+ ulong *len_ptr,
+ int verify)
+{
+ lynxkdi_boot( &header );
+}
+
+#endif /* CONFIG_LYNXKDI */
--- /dev/null
+/*
+ * OF flat tree builder
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <environment.h>
+
+#include <asm/errno.h>
+#include <stddef.h>
+
+#include <ft_build.h>
+
+#ifdef CONFIG_OF_FLAT_TREE
+
+/* align addr on a size boundary - adjust address up if needed -- Cort */
+#define _ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1)))
+
+static void ft_put_word(struct ft_cxt *cxt, u32 v)
+{
+ if (cxt->overflow) /* do nothing */
+ return;
+
+ /* check for overflow */
+ if (cxt->p + 4 > cxt->pstr) {
+ cxt->overflow = 1;
+ return;
+ }
+
+ *(u32 *) cxt->p = cpu_to_be32(v);
+ cxt->p += 4;
+}
+
+static inline void ft_put_bin(struct ft_cxt *cxt, const void *data, int sz)
+{
+ u8 *p;
+
+ if (cxt->overflow) /* do nothing */
+ return;
+
+ /* next pointer pos */
+ p = (u8 *) _ALIGN((unsigned long)cxt->p + sz, 4);
+
+ /* check for overflow */
+ if (p > cxt->pstr) {
+ cxt->overflow = 1;
+ return;
+ }
+
+ memcpy(cxt->p, data, sz);
+ if ((sz & 3) != 0)
+ memset(cxt->p + sz, 0, 4 - (sz & 3));
+ cxt->p = p;
+}
+
+void ft_begin_node(struct ft_cxt *cxt, const char *name)
+{
+ ft_put_word(cxt, OF_DT_BEGIN_NODE);
+ ft_put_bin(cxt, name, strlen(name) + 1);
+}
+
+void ft_end_node(struct ft_cxt *cxt)
+{
+ ft_put_word(cxt, OF_DT_END_NODE);
+}
+
+void ft_nop(struct ft_cxt *cxt)
+{
+ ft_put_word(cxt, OF_DT_NOP);
+}
+
+static int lookup_string(struct ft_cxt *cxt, const char *name)
+{
+ u8 *p;
+
+ p = cxt->pstr;
+ while (p < cxt->pstr_begin) {
+ if (strcmp(p, name) == 0)
+ return p - cxt->p_begin;
+ p += strlen(p) + 1;
+ }
+
+ return -1;
+}
+
+void ft_prop(struct ft_cxt *cxt, const char *name, const void *data, int sz)
+{
+ int len, off;
+
+ if (cxt->overflow)
+ return;
+
+ len = strlen(name) + 1;
+
+ off = lookup_string(cxt, name);
+ if (off == -1) {
+ /* check if we have space */
+ if (cxt->p + 12 + sz + len > cxt->pstr) {
+ cxt->overflow = 1;
+ return;
+ }
+
+ cxt->pstr -= len;
+ memcpy(cxt->pstr, name, len);
+ off = cxt->pstr - cxt->p_begin;
+ }
+
+ /* now put offset from beginning of *STRUCTURE* */
+ /* will be fixed up at the end */
+ ft_put_word(cxt, OF_DT_PROP);
+ ft_put_word(cxt, sz);
+ ft_put_word(cxt, off);
+ ft_put_bin(cxt, data, sz);
+}
+
+void ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str)
+{
+ ft_prop(cxt, name, str, strlen(str) + 1);
+}
+
+void ft_prop_int(struct ft_cxt *cxt, const char *name, int val)
+{
+ u32 v = cpu_to_be32((u32) val);
+
+ ft_prop(cxt, name, &v, 4);
+}
+
+/* start construction of the flat OF tree */
+void ft_begin(struct ft_cxt *cxt, void *blob, int max_size)
+{
+ struct boot_param_header *bph = blob;
+ u32 off;
+
+ /* clear the cxt */
+ memset(cxt, 0, sizeof(*cxt));
+
+ cxt->bph = bph;
+ cxt->max_size = max_size;
+
+ /* zero everything in the header area */
+ memset(bph, 0, sizeof(*bph));
+
+ bph->magic = cpu_to_be32(OF_DT_HEADER);
+ bph->version = cpu_to_be32(0x10);
+ bph->last_comp_version = cpu_to_be32(0x10);
+
+ /* start pointers */
+ cxt->pres_begin = (u8 *) _ALIGN((unsigned long)(bph + 1), 8);
+ cxt->pres = cxt->pres_begin;
+
+ off = (unsigned long)cxt->pres_begin - (unsigned long)bph;
+ bph->off_mem_rsvmap = cpu_to_be32(off);
+
+ ((u64 *) cxt->pres)[0] = 0; /* phys = 0, size = 0, terminate */
+ ((u64 *) cxt->pres)[1] = 0;
+
+ cxt->p_anchor = cxt->pres + 16; /* over the terminator */
+}
+
+/* add a reserver physical area to the rsvmap */
+void ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size)
+{
+ ((u64 *) cxt->pres)[0] = cpu_to_be64(physaddr); /* phys = 0, size = 0, terminate */
+ ((u64 *) cxt->pres)[1] = cpu_to_be64(size);
+
+ cxt->pres += 18; /* advance */
+
+ ((u64 *) cxt->pres)[0] = 0; /* phys = 0, size = 0, terminate */
+ ((u64 *) cxt->pres)[1] = 0;
+
+ /* keep track of size */
+ cxt->res_size = cxt->pres + 16 - cxt->pres_begin;
+
+ cxt->p_anchor = cxt->pres + 16; /* over the terminator */
+}
+
+void ft_begin_tree(struct ft_cxt *cxt)
+{
+ cxt->p_begin = cxt->p_anchor;
+ cxt->pstr_begin = (char *)cxt->bph + cxt->max_size; /* point at the end */
+
+ cxt->p = cxt->p_begin;
+ cxt->pstr = cxt->pstr_begin;
+}
+
+int ft_end_tree(struct ft_cxt *cxt)
+{
+ struct boot_param_header *bph = cxt->bph;
+ int off, sz, sz1;
+ u32 tag, v;
+ u8 *p;
+
+ ft_put_word(cxt, OF_DT_END);
+
+ if (cxt->overflow)
+ return -ENOMEM;
+
+ /* size of the areas */
+ cxt->struct_size = cxt->p - cxt->p_begin;
+ cxt->strings_size = cxt->pstr_begin - cxt->pstr;
+
+ /* the offset we must move */
+ off = (cxt->pstr_begin - cxt->p_begin) - cxt->strings_size;
+
+ /* the new strings start */
+ cxt->pstr_begin = cxt->p_begin + cxt->struct_size;
+
+ /* move the whole string area */
+ memmove(cxt->pstr_begin, cxt->pstr, cxt->strings_size);
+
+ /* now perform the fixup of the strings */
+ p = cxt->p_begin;
+ while ((tag = be32_to_cpu(*(u32 *) p)) != OF_DT_END) {
+ p += 4;
+
+ if (tag == OF_DT_BEGIN_NODE) {
+ p = (u8 *) _ALIGN((unsigned long)p + strlen(p) + 1, 4);
+ continue;
+ }
+
+ if (tag == OF_DT_END_NODE || tag == OF_DT_NOP)
+ continue;
+
+ if (tag != OF_DT_PROP)
+ return -EINVAL;
+
+ sz = be32_to_cpu(*(u32 *) p);
+ p += 4;
+
+ v = be32_to_cpu(*(u32 *) p);
+ v -= off;
+ *(u32 *) p = cpu_to_be32(v); /* move down */
+ p += 4;
+
+ p = (u8 *) _ALIGN((unsigned long)p + sz, 4);
+ }
+
+ /* fix sizes */
+ p = (char *)cxt->bph;
+ sz = (cxt->pstr_begin + cxt->strings_size) - p;
+ sz1 = _ALIGN(sz, 16); /* align at 16 bytes */
+ if (sz != sz1)
+ memset(p + sz, 0, sz1 - sz);
+ bph->totalsize = cpu_to_be32(sz1);
+ bph->off_dt_struct = cpu_to_be32(cxt->p_begin - p);
+ bph->off_dt_strings = cpu_to_be32(cxt->pstr_begin - p);
+
+ /* the new strings start */
+ cxt->pstr_begin = cxt->p_begin + cxt->struct_size;
+ cxt->pstr = cxt->pstr_begin + cxt->strings_size;
+
+ return 0;
+}
+
+/**********************************************************************/
+
+static inline int isprint(int c)
+{
+ return c >= 0x20 && c <= 0x7e;
+}
+
+static int is_printable_string(const void *data, int len)
+{
+ const char *s = data;
+ const char *ss;
+
+ /* zero length is not */
+ if (len == 0)
+ return 0;
+
+ /* must terminate with zero */
+ if (s[len - 1] != '\0')
+ return 0;
+
+ ss = s;
+ while (*s && isprint(*s))
+ s++;
+
+ /* not zero, or not done yet */
+ if (*s != '\0' || (s + 1 - ss) < len)
+ return 0;
+
+ return 1;
+}
+
+static void print_data(const void *data, int len)
+{
+ int i;
+ const u8 *s;
+
+ /* no data, don't print */
+ if (len == 0)
+ return;
+
+ if (is_printable_string(data, len)) {
+ printf(" = \"%s\"", (char *)data);
+ return;
+ }
+
+ switch (len) {
+ case 1: /* byte */
+ printf(" = <0x%02x>", (*(u8 *) data) & 0xff);
+ break;
+ case 2: /* half-word */
+ printf(" = <0x%04x>", be16_to_cpu(*(u16 *) data) & 0xffff);
+ break;
+ case 4: /* word */
+ printf(" = <0x%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
+ break;
+ case 8: /* double-word */
+ printf(" = <0x%16llx>", be64_to_cpu(*(uint64_t *) data));
+ break;
+ default: /* anything else... hexdump */
+ printf(" = [");
+ for (i = 0, s = data; i < len; i++)
+ printf("%02x%s", s[i], i < len - 1 ? " " : "");
+ printf("]");
+
+ break;
+ }
+}
+
+void ft_dump_blob(const void *bphp)
+{
+ const struct boot_param_header *bph = bphp;
+ const uint64_t *p_rsvmap = (const uint64_t *)
+ ((const char *)bph + be32_to_cpu(bph->off_mem_rsvmap));
+ const u32 *p_struct = (const u32 *)
+ ((const char *)bph + be32_to_cpu(bph->off_dt_struct));
+ const u32 *p_strings = (const u32 *)
+ ((const char *)bph + be32_to_cpu(bph->off_dt_strings));
+ u32 tag;
+ const u32 *p;
+ const char *s, *t;
+ int depth, sz, shift;
+ int i;
+ uint64_t addr, size;
+
+ if (be32_to_cpu(bph->magic) != OF_DT_HEADER) {
+ /* not valid tree */
+ return;
+ }
+
+ depth = 0;
+ shift = 4;
+
+ for (i = 0;; i++) {
+ addr = be64_to_cpu(p_rsvmap[i * 2]);
+ size = be64_to_cpu(p_rsvmap[i * 2 + 1]);
+ if (addr == 0 && size == 0)
+ break;
+
+ printf("/memreserve/ 0x%llx 0x%llx;\n", addr, size);
+ }
+
+ p = p_struct;
+ while ((tag = be32_to_cpu(*p++)) != OF_DT_END) {
+
+ /* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */
+
+ if (tag == OF_DT_BEGIN_NODE) {
+ s = (const char *)p;
+ p = (u32 *) _ALIGN((unsigned long)p + strlen(s) + 1, 4);
+
+ printf("%*s%s {\n", depth * shift, "", s);
+
+ depth++;
+ continue;
+ }
+
+ if (tag == OF_DT_END_NODE) {
+ depth--;
+
+ printf("%*s};\n", depth * shift, "");
+ continue;
+ }
+
+ if (tag == OF_DT_NOP) {
+ printf("%*s[NOP]\n", depth * shift, "");
+ continue;
+ }
+
+ if (tag != OF_DT_PROP) {
+ fprintf(stderr, "%*s ** Unknown tag 0x%08x\n",
+ depth * shift, "", tag);
+ break;
+ }
+ sz = be32_to_cpu(*p++);
+ s = (const char *)p_strings + be32_to_cpu(*p++);
+ t = (const char *)p;
+ p = (const u32 *)_ALIGN((unsigned long)p + sz, 4);
+ printf("%*s%s", depth * shift, "", s);
+ print_data(t, sz);
+ printf(";\n");
+ }
+}
+
+void ft_backtrack_node(struct ft_cxt *cxt)
+{
+ if (be32_to_cpu(*(u32 *) (cxt->p - 4)) != OF_DT_END_NODE)
+ return; /* XXX only for node */
+
+ cxt->p -= 4;
+}
+
+/* note that the root node of the blob is "peeled" off */
+void ft_merge_blob(struct ft_cxt *cxt, void *blob)
+{
+ struct boot_param_header *bph = (struct boot_param_header *)blob;
+ u32 *p_struct = (u32 *) ((char *)bph + be32_to_cpu(bph->off_dt_struct));
+ u32 *p_strings =
+ (u32 *) ((char *)bph + be32_to_cpu(bph->off_dt_strings));
+ u32 tag, *p;
+ char *s, *t;
+ int depth, sz;
+
+ if (be32_to_cpu(*(u32 *) (cxt->p - 4)) != OF_DT_END_NODE)
+ return; /* XXX only for node */
+
+ cxt->p -= 4;
+
+ depth = 0;
+ p = p_struct;
+ while ((tag = be32_to_cpu(*p++)) != OF_DT_END) {
+
+ /* printf("tag: 0x%08x (%d) - %d\n", tag, p - p_struct, depth); */
+
+ if (tag == OF_DT_BEGIN_NODE) {
+ s = (char *)p;
+ p = (u32 *) _ALIGN((unsigned long)p + strlen(s) + 1, 4);
+
+ if (depth++ > 0)
+ ft_begin_node(cxt, s);
+
+ continue;
+ }
+
+ if (tag == OF_DT_END_NODE) {
+ ft_end_node(cxt);
+ if (--depth == 0)
+ break;
+ continue;
+ }
+
+ if (tag == OF_DT_NOP)
+ continue;
+
+ if (tag != OF_DT_PROP)
+ break;
+
+ sz = be32_to_cpu(*p++);
+ s = (char *)p_strings + be32_to_cpu(*p++);
+ t = (char *)p;
+ p = (u32 *) _ALIGN((unsigned long)p + sz, 4);
+
+ ft_prop(cxt, s, t, sz);
+ }
+}
+
+void *ft_get_prop(void *bphp, const char *propname, int *szp)
+{
+ struct boot_param_header *bph = bphp;
+ uint32_t *p_struct =
+ (uint32_t *) ((char *)bph + be32_to_cpu(bph->off_dt_struct));
+ uint32_t *p_strings =
+ (uint32_t *) ((char *)bph + be32_to_cpu(bph->off_dt_strings));
+ uint32_t version = be32_to_cpu(bph->version);
+ uint32_t tag;
+ uint32_t *p;
+ char *s, *t;
+ char *ss;
+ int sz;
+ static char path[256], prop[256];
+
+ path[0] = '\0';
+
+ p = p_struct;
+ while ((tag = be32_to_cpu(*p++)) != OF_DT_END) {
+
+ if (tag == OF_DT_BEGIN_NODE) {
+ s = (char *)p;
+ p = (uint32_t *) _ALIGN((unsigned long)p + strlen(s) +
+ 1, 4);
+ strcat(path, s);
+ strcat(path, "/");
+ continue;
+ }
+
+ if (tag == OF_DT_END_NODE) {
+ path[strlen(path) - 1] = '\0';
+ ss = strrchr(path, '/');
+ if (ss != NULL)
+ ss[1] = '\0';
+ continue;
+ }
+
+ if (tag == OF_DT_NOP)
+ continue;
+
+ if (tag != OF_DT_PROP)
+ break;
+
+ sz = be32_to_cpu(*p++);
+ s = (char *)p_strings + be32_to_cpu(*p++);
+ if (version < 0x10 && sz >= 8)
+ p = (uint32_t *) _ALIGN((unsigned long)p, 8);
+ t = (char *)p;
+ p = (uint32_t *) _ALIGN((unsigned long)p + sz, 4);
+
+ strcpy(prop, path);
+ strcat(prop, s);
+
+ if (strcmp(prop, propname) == 0) {
+ *szp = sz;
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
+/********************************************************************/
+
+extern unsigned char oftree_dtb[];
+extern unsigned int oftree_dtb_len;
+
+/* Function that returns a character from the environment */
+extern uchar(*env_get_char) (int);
+
+#define BDM(x) { .name = #x, .offset = offsetof(bd_t, bi_ ##x ) }
+
+static const struct {
+ const char *name;
+ int offset;
+} bd_map[] = {
+ BDM(memstart),
+ BDM(memsize),
+ BDM(flashstart),
+ BDM(flashsize),
+ BDM(flashoffset),
+ BDM(sramstart),
+ BDM(sramsize),
+#if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) \
+ || defined(CONFIG_E500)
+ BDM(immr_base),
+#endif
+#if defined(CONFIG_MPC5xxx)
+ BDM(mbar_base),
+#endif
+#if defined(CONFIG_MPC83XX)
+ BDM(immrbar),
+#endif
+#if defined(CONFIG_MPC8220)
+ BDM(mbar_base),
+ BDM(inpfreq),
+ BDM(pcifreq),
+ BDM(pevfreq),
+ BDM(flbfreq),
+ BDM(vcofreq),
+#endif
+ BDM(bootflags),
+ BDM(ip_addr),
+ BDM(intfreq),
+ BDM(busfreq),
+#ifdef CONFIG_CPM2
+ BDM(cpmfreq),
+ BDM(brgfreq),
+ BDM(sccfreq),
+ BDM(vco),
+#endif
+#if defined(CONFIG_MPC5xxx)
+ BDM(ipbfreq),
+ BDM(pcifreq),
+#endif
+ BDM(baudrate),
+};
+
+void ft_setup(void *blob, int size, bd_t * bd)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ u8 *end;
+ u32 *p;
+ int len;
+ struct ft_cxt cxt;
+ int i, k, nxt;
+ static char tmpenv[256];
+ char *s, *lval, *rval;
+ ulong clock;
+ uint32_t v;
+
+ /* disable OF tree; booting old kernel */
+ if (getenv("disable_of") != NULL) {
+ memcpy(blob, bd, sizeof(*bd));
+ return;
+ }
+
+ ft_begin(&cxt, blob, size);
+
+ /* fs_add_rsvmap not used */
+
+ ft_begin_tree(&cxt);
+
+ ft_begin_node(&cxt, "");
+
+ ft_end_node(&cxt);
+
+ /* copy RO tree */
+ ft_merge_blob(&cxt, oftree_dtb);
+
+ /* back into root */
+ ft_backtrack_node(&cxt);
+
+ ft_begin_node(&cxt, "u-boot-env");
+
+ for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
+ for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) ;
+ s = tmpenv;
+ for (k = i; k < nxt && s < &tmpenv[sizeof(tmpenv) - 1]; ++k)
+ *s++ = env_get_char(k);
+ *s++ = '\0';
+ lval = tmpenv;
+ s = strchr(tmpenv, '=');
+ if (s != NULL) {
+ *s++ = '\0';
+ rval = s;
+ } else
+ continue;
+ ft_prop_str(&cxt, lval, rval);
+ }
+
+ ft_end_node(&cxt);
+
+ ft_begin_node(&cxt, "chosen");
+
+ ft_prop_str(&cxt, "name", "chosen");
+ ft_prop_str(&cxt, "bootargs", getenv("bootargs"));
+ ft_prop_int(&cxt, "linux,platform", 0x600); /* what is this? */
+
+ ft_end_node(&cxt);
+
+ ft_end_node(&cxt); /* end root */
+
+ ft_end_tree(&cxt);
+
+ /*
+ printf("merged OF-tree\n");
+ ft_dump_blob(blob);
+ */
+
+ /* paste the bd_t at the end of the flat tree */
+ end = (char *)blob +
+ be32_to_cpu(((struct boot_param_header *)blob)->totalsize);
+ memcpy(end, bd, sizeof(*bd));
+
+#ifdef CONFIG_PPC
+
+ for (i = 0; i < sizeof(bd_map)/sizeof(bd_map[0]); i++) {
+ sprintf(tmpenv, "/bd_t/%s", bd_map[i].name);
+ v = *(uint32_t *)((char *)bd + bd_map[i].offset);
+
+ p = ft_get_prop(blob, tmpenv, &len);
+ if (p != NULL)
+ *p = cpu_to_be32(v);
+ }
+
+ p = ft_get_prop(blob, "/bd_t/enetaddr", &len);
+ if (p != NULL)
+ memcpy(p, bd->bi_enetaddr, 6);
+
+ p = ft_get_prop(blob, "/bd_t/ethspeed", &len);
+ if (p != NULL)
+ *p = cpu_to_be32((uint32_t) bd->bi_ethspeed);
+
+ clock = bd->bi_intfreq;
+ p = ft_get_prop(blob, "/cpus/" OF_CPU "/clock-frequency", &len);
+ if (p != NULL)
+ *p = cpu_to_be32(clock);
+
+#ifdef OF_TBCLK
+ clock = OF_TBCLK;
+ p = ft_get_prop(blob, "/cpus/" OF_CPU "/timebase-frequency", &len);
+ if (p != NULL)
+ *p = cpu_to_be32(OF_TBCLK);
+#endif
+
+#endif /* __powerpc__ */
+
+ /*
+ printf("final OF-tree\n");
+ ft_dump_blob(blob);
+ */
+
+}
+
+#endif