]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/cmd_bootm.c
Add zero-copy ramdisk support (requires corresponding kernel support!)
[karo-tx-uboot.git] / common / cmd_bootm.c
index 70ca999c06e5d11735b5e4f41179f17a9522aed9..af62becf4a9eaa6f6868c08a4aa0c122eaebeb30 100644 (file)
@@ -31,6 +31,7 @@
 #include <image.h>
 #include <malloc.h>
 #include <zlib.h>
+#include <environment.h>
 #include <asm/byteorder.h>
 #if (CONFIG_COMMANDS & CFG_CMD_DATE) || defined(CONFIG_TIMESTAMP)
 #include <rtc.h>
 #include <asm/cache.h>
 #endif
 
+#ifdef CONFIG_LOGBUFFER
+#include <logbuff.h>
+#endif
+
 /*
  * Some systems (for example LWMON) have very short watchdog periods;
  * we must make sure to split long operations like memmove() or
@@ -70,6 +75,10 @@ static int image_info (unsigned long addr);
 #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'
@@ -84,18 +93,22 @@ typedef void boot_os_Fcn (cmd_tbl_t *cmdtp, int flag,
                          ulong *len_ptr,       /* multi-file image length table */
                          int   verify);        /* getenv("verify")[0] != 'n' */
 
-#ifndef CONFIG_ARM
+#ifdef CONFIG_PPC
 static boot_os_Fcn do_bootm_linux;
 #else
 extern boot_os_Fcn do_bootm_linux;
 #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
 
 image_header_t header;
 
@@ -128,9 +141,21 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        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__ */
+           {
                printf ("Bad Magic Number\n");
                SHOW_BOOT_PROGRESS (-1);
                return 1;
+           }
        }
        SHOW_BOOT_PROGRESS (2);
 
@@ -148,7 +173,7 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        SHOW_BOOT_PROGRESS (3);
 
        /* for multi-file images we need the data part, too */
-       print_image_hdr ((image_header_t *)addr);
+       print_image_hdr (hdr);
 
        data = addr + sizeof(image_header_t);
        len  = ntohl(hdr->ih_size);
@@ -166,8 +191,19 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 
        len_ptr = (ulong *)data;
 
-       if (hdr->ih_arch != IH_CPU_PPC && hdr->ih_arch != IH_CPU_ARM) {
-               printf ("Unsupported Architecture\n");
+#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)        
+#else
+# error Unknown CPU type
+#endif
+       {
+               printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
                SHOW_BOOT_PROGRESS (-4);
                return 1;
        }
@@ -199,9 +235,20 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 
        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(hdr->ih_load == addr) {
+               if(ntohl(hdr->ih_load) == addr) {
                        printf ("   XIP %s ... ", name);
                } else {
 #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
@@ -245,12 +292,17 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 
        switch (hdr->ih_type) {
        case IH_TYPE_STANDALONE:
-               appl = (int (*)(cmd_tbl_t *, int, int, char *[]))ntohl(hdr->ih_ep);
                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))
+                       return 0;
+               appl = (int (*)(cmd_tbl_t *, int, int, char *[]))ntohl(hdr->ih_ep);
                (*appl)(cmdtp, flag, argc-1, &argv[1]);
-               break;
+               return 0;
        case IH_TYPE_KERNEL:
        case IH_TYPE_MULTI:
                /* handled below */
@@ -274,6 +326,12 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
            do_bootm_netbsd (cmdtp, flag, argc, argv,
                             addr, len_ptr, verify);
            break;
+           
+       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,
@@ -284,6 +342,12 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *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);
@@ -294,7 +358,7 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        return 1;
 }
 
-#ifndef CONFIG_ARM
+#ifdef CONFIG_PPC
 static void
 do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
                int     argc, char *argv[],
@@ -310,6 +374,7 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
        ulong   cmd_start, cmd_end;
        ulong   initrd_high;
        ulong   data;
+       int     initrd_copy_to_ram = 1;
        char    *cmdline;
        char    *s;
        bd_t    *kbd;
@@ -321,19 +386,18 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
                 * turning the "load high" feature off. This is intentional.
                 */
                initrd_high = simple_strtoul(s, NULL, 16);
-       } else {                        /* not set, no restrictions to load high */
+               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;
-       if ((s = getenv ("logstart")) != NULL) {
-               kbd->bi_sramstart = simple_strtoul(s, NULL, 16);
-               /* Prevent initrd from overwriting logbuffer */
-               if (initrd_high < kbd->bi_sramstart)
-                       initrd_high = kbd->bi_sramstart-1024;
-       }
-       debug ("## Logbuffer at 0x%08lX ", kbd->bi_sramstart);
+       /* 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
 
        /*
@@ -506,6 +570,10 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
        }
 
        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 */
 
@@ -560,6 +628,7 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
                memmove ((void *)initrd_start, (void *)data, len);
 #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
                printf ("OK\n");
+           }
        } else {
                initrd_start = 0;
                initrd_end = 0;
@@ -584,7 +653,7 @@ do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
         */
        (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end);
 }
-#endif /* CONFIG_ARM */
+#endif /* CONFIG_PPC */
 
 static void
 do_bootm_netbsd (cmd_tbl_t *cmdtp, int flag,
@@ -666,6 +735,99 @@ do_bootm_netbsd (cmd_tbl_t *cmdtp, int flag,
        (*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[])
 {
@@ -787,6 +949,10 @@ print_type (image_header_t *hdr)
        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
        default:                os = "Unknown OS";              break;
        }
 
@@ -909,6 +1075,29 @@ int gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
        return (0);
 }
 
+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[],