Skip to content
Snippets Groups Projects
mips_linux.c 6.51 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * (C) Copyright 2003
     * 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
     *
     */
    
    #include <common.h>
    #include <command.h>
    #include <image.h>
    #include <zlib.h>
    #include <asm/byteorder.h>
    #include <asm/addrspace.h>
    
    #define	LINUX_MAX_ENVS		256
    #define	LINUX_MAX_ARGS		256
    
    #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
    
    extern image_header_t header;           /* from cmd_bootm.c */
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
    
    
    static int	linux_argc;
    static char **	linux_argv;
    
    static char **	linux_env;
    static char *	linux_env_p;
    static int	linux_env_idx;
    
    static void linux_params_init (ulong start, char * commandline);
    static void linux_env_set (char * env_name, char * env_val);
    
    
    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 len = 0, checksum;
        ulong initrd_start, initrd_end;
        ulong data;
        void (*theKernel)(int, char **, char **, int *);
        image_header_t *hdr = &header;
        char *commandline = getenv("bootargs");
        char env_buf[12];
    
        theKernel = (void (*)(int, char **, char **, int *))ntohl(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 */
    	memcpy (&header, (char *)addr, sizeof(image_header_t));
    
    	if (ntohl(hdr->ih_magic) != IH_MAGIC) {
    	    printf ("Bad Magic Number\n");
    	    SHOW_BOOT_PROGRESS (-10);
    	    do_reset (cmdtp, flag, argc, argv);
    	}
    
    	data = (ulong)&header;
    	len  = sizeof(image_header_t);
    
    	checksum = ntohl(hdr->ih_hcrc);
    	hdr->ih_hcrc = 0;
    
    	if (crc32 (0, (char *)data, len) != checksum) {
    	    printf ("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  = ntohl(hdr->ih_size);
    
    	if (verify) {
    	    ulong csum = 0;
    
    	    printf ("   Verifying Checksum ... ");
    	    csum = crc32 (0, (char *)data, len);
    	    if (csum != ntohl(hdr->ih_dcrc)) {
    		printf ("Bad Data CRC\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		SHOW_BOOT_PROGRESS (-12);
    
    		do_reset (cmdtp, flag, argc, argv);
    	    }
    	    printf ("OK\n");
    	}
    
    	SHOW_BOOT_PROGRESS (11);
    
    	if ((hdr->ih_os   != IH_OS_LINUX)	||
    	    (hdr->ih_arch != IH_CPU_MIPS)	||
    	    (hdr->ih_type != IH_TYPE_RAMDISK)	) {
    	    printf ("No Linux MIPS 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])) {
    	ulong 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
    	 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	SHOW_BOOT_PROGRESS (14);
    
    
    	data = 0;
        }
    
    #ifdef	DEBUG
        if (!data) {
    	printf ("No initrd\n");
        }
    #endif
    
        if (data) {
    	initrd_start = data;
    	initrd_end   = initrd_start + len;
        } else {
    	initrd_start = 0;
    	initrd_end = 0;
        }
    
        SHOW_BOOT_PROGRESS (15);
    
    #ifdef DEBUG
        printf ("## Transferring control to Linux (at address %08lx) ...\n",
    	    (ulong)theKernel);
    #endif
    
        linux_params_init (PHYSADDR(gd->bd->bi_boot_params), commandline);
    
        sprintf (env_buf, "%lu", gd->ram_size >> 20);
        linux_env_set ("memsize", env_buf);
    
        sprintf (env_buf, "0x%08X", (uint)PHYSADDR(initrd_start));
        linux_env_set ("initrd_start", env_buf);
    
        sprintf (env_buf, "0x%X", (uint)(initrd_end - initrd_start));
        linux_env_set ("initrd_size", env_buf);
    
        sprintf (env_buf, "0x%08X", (uint)(gd->bd->bi_flashstart));
        linux_env_set ("flash_start", env_buf);
    
        sprintf (env_buf, "0x%X", (uint)(gd->bd->bi_flashsize));
        linux_env_set ("flash_size", env_buf);
    
        /* we assume that the kernel is in place */
        printf("\nStarting kernel ...\n\n");
    
        theKernel(linux_argc, linux_argv, linux_env, 0);
    }
    
    static void linux_params_init (ulong start, char * line)
    {
        char * next, * quote, * argp;
    
        linux_argc = 1;
        linux_argv = (char **) start;
        linux_argv[0] = 0;
        argp = (char *)(linux_argv + LINUX_MAX_ARGS);
    
        next = line;
    
        while (line && *line && linux_argc < LINUX_MAX_ARGS)
        {
    	quote = strchr (line, '"');
    	next = strchr (line, ' ');
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	while (next != NULL && quote != NULL && quote < next)
    
    	{
    	    /* we found a left quote before the next blank
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	     * now we have to find the matching right quote
    	     */
    	    next = strchr (quote + 1, '"');
    	    if (next != NULL)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		quote = strchr (next + 1, '"');
    		next = strchr (next + 1, ' ');
    	    }
    	}
    
    
    	if (next == NULL)
    	{
    	    next = line + strlen (line);
    	}
    
    	linux_argv [linux_argc] = argp;
    	memcpy (argp, line, next - line);
    	argp [next - line] = 0;
    
    	argp += next - line + 1;
    	linux_argc ++;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if (*next) next ++;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	line = next;
        }
    
        linux_env = (char **)(((ulong)argp + 15) & ~15);
        linux_env [0] = 0;
        linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS);
        linux_env_idx = 0;
    }
    
    static void linux_env_set (char * env_name, char * env_val)
    {
        if (linux_env_idx < LINUX_MAX_ENVS - 1)
        {
    	linux_env [linux_env_idx] = linux_env_p;
    
    	strcpy (linux_env_p, env_name);
    	linux_env_p += strlen (env_name);
    
    	strcpy (linux_env_p, "=");
    	linux_env_p += 1;
    
    	strcpy (linux_env_p, env_val);
    	linux_env_p += strlen (env_val);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	linux_env_p ++;
    	linux_env [++ linux_env_idx] = 0;
        }
    }