1/*
2 * BK Id: %F% %I% %G% %U% %#%
3 *
4 * Originally adapted by Gary Thomas.  Much additional work by
5 * Cort Dougan <cort@fsmlabs.com>.  On top of that still more work by
6 * Dan Malek <dmalek@jlc.net>.
7 *
8 * Currently maintained by: Tom Rini <trini@kernel.crashing.org>
9 *
10 * This program is free software; you can redistribute  it and/or modify it
11 * under  the terms of  the GNU General  Public License as published by the
12 * Free Software Foundation;  either version 2 of the  License, or (at your
13 * option) any later version.
14 *
15 */
16
17#include <linux/config.h>
18#include <linux/types.h>
19#include <linux/elf.h>
20#include <asm/bootinfo.h>
21#include <asm/mmu.h>
22#include <asm/mpc8xx.h>
23#include <asm/mpc8260.h>
24#include <asm/page.h>
25#include <asm/processor.h>
26#include <asm/residual.h>
27
28#include "nonstdio.h"
29#include "zlib.h"
30
31/* The linker tells us where the image is. */
32extern char __image_begin, __image_end;
33extern char __ramdisk_begin, __ramdisk_end;
34extern char _end[];
35
36/* Because of the limited amount of memory on embedded, it presents
37 * loading problems.  The biggest is that we load this boot program
38 * into a relatively low memory address, and the Linux kernel Bss often
39 * extends into this space when it get loaded.  When the kernel starts
40 * and zeros the BSS space, it also writes over the information we
41 * save here and pass to the kernel (usually board info).
42 * On these boards, we grab some known memory holes to hold this information.
43 */
44char cmd_buf[256];
45char *cmd_line = cmd_buf;
46char *avail_ram;
47char *end_avail;
48char *zimage_start;
49
50/* This is for 4xx treeboot.  It provides a place for the bootrom
51 * give us a pointer to a rom environment command line.
52 */
53char *bootrom_cmdline = "";
54
55/* This is the default cmdline that will be given to the user at boot time..
56 * If none was specified at compile time, we'll give it one that should work.
57 * -- Tom */
58#ifdef CONFIG_CMDLINE_BOOL
59char compiled_string[] = CONFIG_CMDLINE;
60#endif
61char ramroot_string[] = "root=/dev/ram";
62char netroot_string[] = "root=/dev/nfs rw ip=on";
63
64/* Serial port to use. */
65unsigned long com_port;
66
67/* We need to make sure that this is before the images to ensure
68 * that it's in a mapped location. - Tom */
69bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot")));
70bd_t *hold_residual = &hold_resid_buf;
71
72extern unsigned long serial_init(int chan, bd_t *bp);
73extern void serial_close(unsigned long com_port);
74extern unsigned long start;
75extern void flush_instruction_cache(void);
76extern void gunzip(void *, int, unsigned char *, int *);
77extern void embed_config(bd_t **bp);
78
79unsigned long
80decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp)
81{
82	char *cp, ch;
83	int timer = 0, zimage_size;
84	unsigned long initrd_size;
85
86	/* First, capture the embedded board information.  Then
87	 * initialize the serial console port.
88	 */
89	embed_config(&bp);
90#ifdef CONFIG_SERIAL_CONSOLE
91	com_port = serial_init(0, bp);
92#endif
93
94	/* copy board data */
95	if (bp)
96		memcpy(hold_residual,bp,sizeof(bd_t));
97
98	/* Set end of memory available to us.  It is always the highest
99	 * memory address provided by the board information.
100	 */
101	end_avail = (char *)(bp->bi_memsize);
102
103	puts("\nloaded at:     "); puthex(load_addr);
104	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
105	if ( (unsigned long)load_addr != (unsigned long)&start ) {
106		puts("relocated to:  "); puthex((unsigned long)&start);
107		puts(" ");
108		puthex((unsigned long)((unsigned long)&start + (4*num_words)));
109		puts("\n");
110	}
111
112	if ( bp ) {
113		puts("board data at: "); puthex((unsigned long)bp);
114		puts(" ");
115		puthex((unsigned long)((unsigned long)bp + sizeof(bd_t)));
116		puts("\nrelocated to:  ");
117		puthex((unsigned long)hold_residual);
118		puts(" ");
119		puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t)));
120		puts("\n");
121	}
122
123	/*
124	 * We link ourself to an arbitrary low address.  When we run, we
125	 * relocate outself to that address.  __image_being points to
126	 * the part of the image where the zImage is. -- Tom
127	 */
128	zimage_start = (char *)(unsigned long)(&__image_begin);
129	zimage_size = (unsigned long)(&__image_end) -
130			(unsigned long)(&__image_begin);
131
132	initrd_size = (unsigned long)(&__ramdisk_end) -
133		(unsigned long)(&__ramdisk_begin);
134
135	/*
136	 * The zImage and initrd will be between start and _end, so they've
137	 * already been moved once.  We're good to go now. -- Tom
138	 */
139	puts("zimage at:     "); puthex((unsigned long)zimage_start);
140	puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
141	puts("\n");
142
143	if ( initrd_size ) {
144		puts("initrd at:     ");
145		puthex((unsigned long)(&__ramdisk_begin));
146		puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n");
147	}
148
149	/*
150	 * setup avail_ram - this is the first part of ram usable
151	 * by the uncompress code.  Anything after this program in RAM
152	 * is now fair game. -- Tom
153	 */
154	avail_ram = (char *)PAGE_ALIGN((unsigned long)_end);
155
156	puts("avail ram:     "); puthex((unsigned long)avail_ram); puts(" ");
157	puthex((unsigned long)end_avail); puts("\n");
158	puts("\nLinux/PPC load: ");
159	cp = cmd_line;
160	/* This is where we try and pick the right command line for booting.
161	 * If we were given one at compile time, use it.  It Is Right.
162	 * If we weren't, see if we have a ramdisk.  If so, thats root.
163	 * When in doubt, give them the netroot (root=/dev/nfs rw) -- Tom
164	 */
165#ifdef CONFIG_CMDLINE_BOOL
166	memcpy (cmd_line, compiled_string, sizeof(compiled_string));
167#else
168	if ( initrd_size )
169		memcpy (cmd_line, ramroot_string, sizeof(ramroot_string));
170	else
171		memcpy (cmd_line, netroot_string, sizeof(netroot_string));
172#endif
173	while ( *cp )
174		putc(*cp++);
175#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_VGA_CONSOLE)
176	/*
177	 * If they have a console, allow them to edit the command line.
178	 * Otherwise, don't bother wasting the five seconds.
179	 */
180	while (timer++ < 5*1000) {
181		if (tstc()) {
182			while ((ch = getc()) != '\n' && ch != '\r') {
183				if (ch == '\b' || ch == '\177') {
184					if (cp != cmd_line) {
185						cp--;
186						puts("\b \b");
187					}
188				} else if (ch == '\030'		/* ^x */
189					   || ch == '\025') {	/* ^u */
190					while (cp != cmd_line) {
191						cp--;
192						puts("\b \b");
193					}
194				} else {
195					*cp++ = ch;
196					putc(ch);
197				}
198			}
199			break;  /* Exit 'timer' loop */
200		}
201		udelay(1000);  /* 1 msec */
202	}
203#endif
204	*cp = 0;
205	puts("\nUncompressing Linux...");
206
207	gunzip(0, 0x400000, zimage_start, &zimage_size);
208	flush_instruction_cache();
209	puts("done.\n");
210	{
211		struct bi_record *rec;
212
213		rec = (struct bi_record *)_ALIGN((unsigned long)zimage_size +
214				(1 << 20) - 1,(1 << 20));
215
216		rec->tag = BI_FIRST;
217		rec->size = sizeof(struct bi_record);
218		rec = (struct bi_record *)((unsigned long)rec + rec->size);
219
220		rec->tag = BI_CMD_LINE;
221		memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1);
222		rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1;
223		rec = (struct bi_record *)((unsigned long)rec + rec->size);
224
225		if ( initrd_size ) {
226			rec->tag = BI_INITRD;
227			rec->data[0] = (unsigned long)(&__ramdisk_begin);
228			rec->data[1] = initrd_size;
229			rec->size = sizeof(struct bi_record) + 2 *
230				sizeof(unsigned long);
231			rec = (struct bi_record *)((unsigned long)rec +
232					rec->size);
233		}
234
235		rec->tag = BI_LAST;
236		rec->size = sizeof(struct bi_record);
237		rec = (struct bi_record *)((unsigned long)rec + rec->size);
238	}
239	puts("Now booting the kernel\n");
240	serial_close(com_port);
241
242	return (unsigned long)hold_residual;
243}
244