diff options
Diffstat (limited to 'lib_m68k/m68k_linux.c')
| -rw-r--r-- | lib_m68k/m68k_linux.c | 360 | 
1 files changed, 215 insertions, 145 deletions
| diff --git a/lib_m68k/m68k_linux.c b/lib_m68k/m68k_linux.c index 6c194f80a..bea97441b 100644 --- a/lib_m68k/m68k_linux.c +++ b/lib_m68k/m68k_linux.c @@ -25,6 +25,8 @@  #include <command.h>  #include <image.h>  #include <zlib.h> +#include <bzlib.h> +#include <environment.h>  #include <asm/byteorder.h>  DECLARE_GLOBAL_DATA_PTR; @@ -34,103 +36,190 @@ DECLARE_GLOBAL_DATA_PTR;  #define LINUX_MAX_ENVS		256  #define LINUX_MAX_ARGS		256 -extern image_header_t header;	/* from cmd_bootm.c */ - -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; +#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 -static void linux_params_init (ulong start, char *commandline); -static void linux_env_set (char *env_name, char *env_val); +extern image_header_t header; -void do_bootm_linux (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[], -		     ulong addr, ulong * len_ptr, int verify) +void do_bootm_linux(cmd_tbl_t * cmdtp, int flag, +		    int argc, char *argv[], +		    ulong addr, ulong * len_ptr, int verify)  { -	ulong len = 0, checksum; +	ulong sp; +	ulong len, checksum;  	ulong initrd_start, initrd_end; +	ulong cmd_start, cmd_end; +	ulong initrd_high;  	ulong data; -	void (*theKernel) (int, char **, char **, int *); +	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; -	char *commandline = getenv ("bootargs"); -	char env_buf[12]; -	theKernel = -		(void (*)(int, char **, char **, int *)) ntohl (hdr->ih_ep); +	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("movel %%a7, %%d0\n" +	    "movel %%d0, %0\n": "=d"(sp): :"%d0"); + +	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; +	} + +	kernel = +	    (void (*)(bd_t *, ulong, ulong, ulong, ulong))ntohl(hdr->ih_ep);  	/*  	 * Check if there is an initrd image  	 */ +  	if (argc >= 3) { -		show_boot_progress (9); +		debug("Not skipping initrd\n"); +		SHOW_BOOT_PROGRESS(9); -		addr = simple_strtoul (argv[2], NULL, 16); +		addr = simple_strtoul(argv[2], NULL, 16); -		printf ("## Loading Ramdisk Image at %08lx ...\n", addr); +		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)); +		memmove(&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); +		if (ntohl(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); +		len = sizeof(image_header_t); -		checksum = ntohl (hdr->ih_hcrc); +		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); +		if (crc32(0, (uchar *) data, len) != checksum) { +			puts("Bad Header Checksum\n"); +			SHOW_BOOT_PROGRESS(-11); +			do_reset(cmdtp, flag, argc, argv);  		} -		show_boot_progress (10); +		SHOW_BOOT_PROGRESS(10); -		print_image_hdr (hdr); +		print_image_hdr(hdr); -		data = addr + sizeof (image_header_t); -		len = ntohl (hdr->ih_size); +		data = addr + sizeof(image_header_t); +		len = ntohl(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, (uchar *) cdata, chunk); +				cdata += chunk; + +				WATCHDOG_RESET(); +			} +#else				/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ +			csum = crc32(0, (uchar *) data, len); +#endif				/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ -			printf ("   Verifying Checksum ... "); -			csum = crc32 (0, (char *) data, len); -			if (csum != ntohl (hdr->ih_dcrc)) { -				printf ("Bad Data CRC\n"); -				show_boot_progress (-12); -				do_reset (cmdtp, flag, argc, argv); +			if (csum != ntohl(hdr->ih_dcrc)) { +				puts("Bad Data CRC\n"); +				SHOW_BOOT_PROGRESS(-12); +				do_reset(cmdtp, flag, argc, argv);  			} -			printf ("OK\n"); +			puts("OK\n");  		} -		show_boot_progress (11); +		SHOW_BOOT_PROGRESS(11);  		if ((hdr->ih_os != IH_OS_LINUX) ||  		    (hdr->ih_arch != IH_CPU_M68K) ||  		    (hdr->ih_type != IH_TYPE_RAMDISK)) { -			printf ("No Linux M68K Ramdisk Image\n"); -			show_boot_progress (-13); -			do_reset (cmdtp, flag, argc, argv); +			puts("No Linux ColdFire 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; +		u_long tail = ntohl(len_ptr[0]) % 4;  		int i; -		show_boot_progress (13); +		SHOW_BOOT_PROGRESS(13);  		/* skip kernel length and terminator */  		data = (ulong) (&len_ptr[2]); @@ -138,130 +227,111 @@ void do_bootm_linux (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[],  		for (i = 1; len_ptr[i]; ++i)  			data += 4;  		/* add kernel length, and align */ -		data += ntohl (len_ptr[0]); +		data += ntohl(len_ptr[0]);  		if (tail) {  			data += 4 - tail;  		} -		len = ntohl (len_ptr[1]); +		len = ntohl(len_ptr[1]);  	} else {  		/*  		 * no initrd image  		 */ -		show_boot_progress (14); +		SHOW_BOOT_PROGRESS(14); -		data = 0; +		len = data = 0;  	} -#ifdef	DEBUG  	if (!data) { -		printf ("No initrd\n"); +		debug("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); +		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 */ -	sprintf (env_buf, "0x%X", (uint) (initrd_end - initrd_start)); -	linux_env_set ("initrd_size", env_buf); +			if (initrd_high) { +				ulong nsp; -	sprintf (env_buf, "0x%08X", (uint) (gd->bd->bi_flashstart)); -	linux_env_set ("flash_start", env_buf); +				/* +				 * 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("movel %%a7, %%d0\n" +				    "movel %%d0, %0\n": "=d"(nsp): :"%d0"); -	sprintf (env_buf, "0x%X", (uint) (gd->bd->bi_flashsize)); -	linux_env_set ("flash_size", env_buf); +				nsp -= 2048;	/* just to be sure */ +				nsp &= ~0xF; -	/* we assume that the kernel is in place */ -	printf ("\nStarting kernel ...\n\n"); +				if (nsp > initrd_high)	/* limit as specified */ +					nsp = initrd_high; -	theKernel (linux_argc, linux_argv, linux_env, 0); -} +					nsp -= len; +				nsp &= ~(4096 - 1);	/* align on page */ -static void linux_params_init (ulong start, char *line) -{ -	char *next, *quote, *argp; +				if (nsp >= sp) +					initrd_start = nsp; +			} -	linux_argc = 1; -	linux_argv = (char **) start; -	linux_argv[0] = 0; -	argp = (char *) (linux_argv + LINUX_MAX_ARGS); +			SHOW_BOOT_PROGRESS(12); -	next = line; +			debug +			    ("## initrd at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n", +			     data, data + len - 1, len, len); -	while (line && *line && linux_argc < LINUX_MAX_ARGS) { -		quote = strchr (line, '"'); -		next = strchr (line, ' '); +			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 (next != NULL && quote != NULL && quote < next) { -			/* we found a left quote before the next blank -			 * now we have to find the matching right quote -			 */ -			next = strchr (quote + 1, '"'); -			if (next != NULL) { -				quote = strchr (next + 1, '"'); -				next = strchr (next + 1, ' '); +				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");  		} - -		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++; - -		if (*next) -			next++; - -		line = next; +	} else { +		initrd_start = 0; +		initrd_end = 0;  	} -	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); +	debug("## Transferring control to Linux (at address %08lx) ...\n", +	      (ulong) kernel); -		strcpy (linux_env_p, "="); -		linux_env_p += 1; +	SHOW_BOOT_PROGRESS(15); -		strcpy (linux_env_p, env_val); -		linux_env_p += strlen (env_val); - -		linux_env_p++; -		linux_env[++linux_env_idx] = 0; -	} +	/* +	 * Linux Kernel Parameters (passing board info data): +	 *   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); +	/* does not return */  } |