diff options
Diffstat (limited to 'arch/ppc/boot/simple/misc-embedded.c')
-rw-r--r-- | arch/ppc/boot/simple/misc-embedded.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/arch/ppc/boot/simple/misc-embedded.c b/arch/ppc/boot/simple/misc-embedded.c new file mode 100644 index 00000000000..3865f3f8dcd --- /dev/null +++ b/arch/ppc/boot/simple/misc-embedded.c @@ -0,0 +1,275 @@ +/* + * Originally adapted by Gary Thomas. Much additional work by + * Cort Dougan <cort@fsmlabs.com>. On top of that still more work by + * Dan Malek <dmalek@jlc.net>. + * + * Currently maintained by: Tom Rini <trini@kernel.crashing.org> + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/string.h> +#include <asm/bootinfo.h> +#include <asm/mmu.h> +#include <asm/page.h> +#include <asm/residual.h> +#if defined(CONFIG_4xx) +#include <asm/ibm4xx.h> +#elif defined(CONFIG_8xx) +#include <asm/mpc8xx.h> +#elif defined(CONFIG_8260) +#include <asm/mpc8260.h> +#endif + +#include "nonstdio.h" + +/* The linker tells us where the image is. */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _end[]; + +/* Because of the limited amount of memory on embedded, it presents + * loading problems. The biggest is that we load this boot program + * into a relatively low memory address, and the Linux kernel Bss often + * extends into this space when it get loaded. When the kernel starts + * and zeros the BSS space, it also writes over the information we + * save here and pass to the kernel (usually board info). + * On these boards, we grab some known memory holes to hold this information. + */ +char cmd_buf[256]; +char *cmd_line = cmd_buf; +char *avail_ram; +char *end_avail; +char *zimage_start; + +/* This is for 4xx treeboot. It provides a place for the bootrom + * give us a pointer to a rom environment command line. + */ +char *bootrom_cmdline = ""; + +/* This is the default cmdline that will be given to the user at boot time.. + * If none was specified at compile time, we'll give it one that should work. + * -- Tom */ +#ifdef CONFIG_CMDLINE_BOOL +char compiled_string[] = CONFIG_CMDLINE; +#endif +char ramroot_string[] = "root=/dev/ram"; +char netroot_string[] = "root=/dev/nfs rw ip=on"; + +/* Serial port to use. */ +unsigned long com_port; + +/* We need to make sure that this is before the images to ensure + * that it's in a mapped location. - Tom */ +bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot"))); +bd_t *hold_residual = &hold_resid_buf; + +extern unsigned long serial_init(int chan, bd_t *bp); +extern void serial_close(unsigned long com_port); +extern unsigned long start; +extern void flush_instruction_cache(void); +extern void gunzip(void *, int, unsigned char *, int *); +extern void embed_config(bd_t **bp); + +/* Weak function for boards which don't need to build the + * board info struct because they are using PPCBoot/U-Boot. + */ +void __attribute__ ((weak)) +embed_config(bd_t **bdp) +{ +} + +unsigned long +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp) +{ + char *cp, ch; + int timer = 0, zimage_size; + unsigned long initrd_size; + + /* First, capture the embedded board information. Then + * initialize the serial console port. + */ + embed_config(&bp); +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) + com_port = serial_init(0, bp); +#endif + + /* Grab some space for the command line and board info. Since + * we no longer use the ELF header, but it was loaded, grab + * that space. + */ +#ifdef CONFIG_MBX + /* Because of the way the MBX loads the ELF image, we can't + * tell where we started. We read a magic variable from the NVRAM + * that gives us the intermediate buffer load address. + */ + load_addr = *(uint *)0xfa000020; + load_addr += 0x10000; /* Skip ELF header */ +#endif + /* copy board data */ + if (bp) + memcpy(hold_residual,bp,sizeof(bd_t)); + + /* Set end of memory available to us. It is always the highest + * memory address provided by the board information. + */ + end_avail = (char *)(bp->bi_memsize); + + puts("\nloaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + if ( bp ) { + puts("board data at: "); puthex((unsigned long)bp); + puts(" "); + puthex((unsigned long)((unsigned long)bp + sizeof(bd_t))); + puts("\nrelocated to: "); + puthex((unsigned long)hold_residual); + puts(" "); + puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); + puts("\n"); + } + + /* + * We link ourself to an arbitrary low address. When we run, we + * relocate outself to that address. __image_being points to + * the part of the image where the zImage is. -- Tom + */ + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); + + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); + + /* + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom + */ + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); + } + + /* + * setup avail_ram - this is the first part of ram usable + * by the uncompress code. Anything after this program in RAM + * is now fair game. -- Tom + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); + + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + puts("\nLinux/PPC load: "); + cp = cmd_line; + /* This is where we try and pick the right command line for booting. + * If we were given one at compile time, use it. It Is Right. + * If we weren't, see if we have a ramdisk. If so, thats root. + * When in doubt, give them the netroot (root=/dev/nfs rw) -- Tom + */ +#ifdef CONFIG_CMDLINE_BOOL + memcpy (cmd_line, compiled_string, sizeof(compiled_string)); +#else + if ( initrd_size ) + memcpy (cmd_line, ramroot_string, sizeof(ramroot_string)); + else + memcpy (cmd_line, netroot_string, sizeof(netroot_string)); +#endif + while ( *cp ) + putc(*cp++); + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b' || ch == '\177') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else if (ch == '\030' /* ^x */ + || ch == '\025') { /* ^u */ + while (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\nUncompressing Linux..."); + + gunzip(0, 0x400000, zimage_start, &zimage_size); + flush_instruction_cache(); + puts("done.\n"); + { + struct bi_record *rec; + unsigned long initrd_loc = 0; + unsigned long rec_loc = _ALIGN((unsigned long)(zimage_size) + + (1 << 20) - 1, (1 << 20)); + rec = (struct bi_record *)rec_loc; + + /* We need to make sure that the initrd and bi_recs do not + * overlap. */ + if ( initrd_size ) { + initrd_loc = (unsigned long)(&__ramdisk_begin); + /* If the bi_recs are in the middle of the current + * initrd, move the initrd to the next MB + * boundary. */ + if ((rec_loc > initrd_loc) && + ((initrd_loc + initrd_size) + > rec_loc)) { + initrd_loc = _ALIGN((unsigned long)(zimage_size) + + (2 << 20) - 1, (2 << 20)); + memmove((void *)initrd_loc, &__ramdisk_begin, + initrd_size); + puts("initrd moved: "); puthex(initrd_loc); + puts(" "); puthex(initrd_loc + initrd_size); + puts("\n"); + } + } + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_CMD_LINE; + memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); + rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if ( initrd_size ) { + rec->tag = BI_INITRD; + rec->data[0] = initrd_loc; + rec->data[1] = initrd_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + + rec->size); + } + + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + } + puts("Now booting the kernel\n"); +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) + serial_close(com_port); +#endif + + return (unsigned long)hold_residual; +} |