Newer
Older
/*
* (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 <watchdog.h>
DECLARE_GLOBAL_DATA_PTR;
#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
int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
void do_bootm_linux(cmd_tbl_t * cmdtp, int flag,
int argc, char *argv[],
ulong addr, ulong * len_ptr, int verify)
ulong cmd_start, cmd_end;
ulong initrd_high;
int initrd_copy_to_ram = 1;
char *cmdline;
char *s;
bd_t *kbd;
void (*kernel) (bd_t *, ulong, ulong, ulong, ulong);
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");
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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))image_get_ep (hdr);
/*
* Check if there is an initrd image
*/
debug("Not skipping initrd\n");
SHOW_BOOT_PROGRESS(9);
addr = simple_strtoul(argv[2], NULL, 16);
hdr = (image_header_t *)addr;
printf("## Loading RAMDisk Image at %08lx ...\n", addr);
if (!image_check_magic (hdr)) {
puts("Bad Magic Number\n");
SHOW_BOOT_PROGRESS(-10);
do_reset(cmdtp, flag, argc, argv);
if (!image_check_hcrc (hdr)) {
puts("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS(-11);
do_reset(cmdtp, flag, argc, argv);
print_image_hdr (hdr);
data = image_get_data (hdr);
len = image_get_data_size (hdr);
if (!image_check_dcrc_wd (hdr, CHUNKSZ)) {
puts("Bad Data CRC\n");
SHOW_BOOT_PROGRESS(-12);
do_reset(cmdtp, flag, argc, argv);
}
puts("OK\n");
if (!image_check_os (hdr, IH_OS_LINUX) ||
!image_check_arch (hdr, IH_ARCH_M68K) ||
!image_check_type (hdr, IH_TYPE_RAMDISK)) {
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 (image_check_type (hdr, IH_TYPE_MULTI) && (len_ptr[1])) {
u_long tail = image_to_cpu (len_ptr[0]) % 4;
/* 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 += image_to_cpu (len_ptr[0]);
if (tail) {
data += 4 - tail;
}
len = image_to_cpu (len_ptr[1]);
} else {
/*
* no initrd image
*/
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("movel %%a7, %%d0\n"
"movel %%d0, %0\n": "=d"(nsp): :"%d0");
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;
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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);
/*
* 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 */