1 /*
2  * Copyright (C) Paul Mackerras 1997.
3  *
4  * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 #define __KERNEL__
12 #include "ppc32-types.h"
13 #include "zlib.h"
14 #include <linux/elf.h>
15 #include <asm/processor.h>
16 #include <asm/page.h>
17 #include <asm/bootinfo.h>
18 
19 void memmove(void *dst, void *im, int len);
20 void *memcpy(void *dest, const void *src, size_t n);
21 size_t strlen(const char *s);
22 
23 extern void *finddevice(const char *);
24 extern int getprop(void *, const char *, void *, int);
25 extern void printk(char *fmt, ...);
26 extern void printf(const char *fmt, ...);
27 extern int sprintf(char *buf, const char *fmt, ...);
28 void gunzip(void *, int, unsigned char *, int *);
29 void *claim(unsigned int, unsigned int, unsigned int);
30 void flush_cache(void *, unsigned long);
31 void pause(void);
32 extern void exit(void);
33 
34 static struct bi_record *make_bi_recs(unsigned long);
35 
36 #define RAM_START	0x00000000
37 #define RAM_END		(64<<20)
38 
39 /* Value picked to match that used by yaboot */
40 #define PROG_START	0x01400000
41 
42 char *avail_ram;
43 char *begin_avail, *end_avail;
44 char *avail_high;
45 unsigned int heap_use;
46 unsigned int heap_max;
47 
48 extern char _end[];
49 extern char _vmlinux_start[];
50 extern char _vmlinux_end[];
51 extern char _sysmap_start[];
52 extern char _sysmap_end[];
53 extern char _initrd_start[];
54 extern char _initrd_end[];
55 
56 extern void *_vmlinux_filesize;
57 extern void *_vmlinux_memsize;
58 
59 struct addr_range {
60 	unsigned long addr;
61 	unsigned long size;
62 	unsigned long memsize;
63 };
64 struct addr_range vmlinux = {0, 0, 0};
65 struct addr_range vmlinuz = {0, 0, 0};
66 struct addr_range sysmap  = {0, 0, 0};
67 struct addr_range initrd  = {0, 0, 0};
68 
69 static char scratch[128<<10];	/* 128kB of scratch space for gunzip */
70 
71 typedef void (*kernel_entry_t)( unsigned long,
72 		                unsigned long,
73 		                void *,
74 				struct bi_record *);
75 
76 
77 int (*prom)(void *);
78 
79 void *chosen_handle;
80 void *stdin;
81 void *stdout;
82 void *stderr;
83 
84 
85 void
start(unsigned long a1,unsigned long a2,void * promptr)86 start(unsigned long a1, unsigned long a2, void *promptr)
87 {
88 	unsigned long i, claim_addr, claim_size;
89 	unsigned long vmlinux_filesize;
90 	extern char _start;
91 	struct bi_record *bi_recs;
92 	kernel_entry_t kernel_entry;
93 	Elf64_Ehdr *elf64;
94 	Elf64_Phdr *elf64ph;
95 
96 	prom = (int (*)(void *)) promptr;
97 	chosen_handle = finddevice("/chosen");
98 	if (chosen_handle == (void *) -1)
99 		exit();
100 	if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
101 		exit();
102 	stderr = stdout;
103 	if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4)
104 		exit();
105 
106 	printf("\n\rzImage starting: loaded at 0x%x\n\r", (unsigned)&_start);
107 
108 	initrd.size = (unsigned long)(_initrd_end - _initrd_start);
109 	initrd.memsize = initrd.size;
110 	if ( initrd.size > 0 ) {
111 		initrd.addr = (RAM_END - initrd.size) & ~0xFFF;
112 		a1 = a2 = 0;
113 		claim(initrd.addr, RAM_END - initrd.addr, 0);
114 		printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r",
115 		       initrd.addr, (unsigned long)_initrd_start, initrd.size);
116 		memcpy((void *)initrd.addr, (void *)_initrd_start, initrd.size);
117 	}
118 
119 	vmlinuz.addr = (unsigned long)_vmlinux_start;
120 	vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
121 	vmlinux.addr = (unsigned long)(void *)-1;
122 	vmlinux_filesize = (unsigned long)&_vmlinux_filesize;
123 	vmlinux.size = PAGE_ALIGN(vmlinux_filesize);
124 	vmlinux.memsize = (unsigned long)&_vmlinux_memsize;
125 
126 	claim_size = vmlinux.memsize /* PPPBBB: + fudge for bi_recs */;
127 	for(claim_addr = PROG_START;
128 	    claim_addr <= PROG_START * 8;
129 	    claim_addr += 0x100000) {
130 		printf("    trying: 0x%08lx\n\r", claim_addr);
131 		vmlinux.addr = (unsigned long)claim(claim_addr, claim_size, 0);
132 		if ((void *)vmlinux.addr != (void *)-1) break;
133 	}
134 	if ((void *)vmlinux.addr == (void *)-1) {
135 		printf("claim error, can't allocate kernel memory\n\r");
136 		exit();
137 	}
138 
139 	/* PPPBBB: should kernel always be gziped? */
140 	if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
141 		avail_ram = scratch;
142 		begin_avail = avail_high = avail_ram;
143 		end_avail = scratch + sizeof(scratch);
144 		printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...",
145 		       vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size);
146 		gunzip((void *)vmlinux.addr, vmlinux.size,
147 			(unsigned char *)vmlinuz.addr, (int *)&vmlinuz.size);
148 		printf("done %lu bytes\n\r", vmlinuz.size);
149 		printf("%u bytes of heap consumed, max in use %u\n\r",
150 		       (unsigned)(avail_high - begin_avail), heap_max);
151 	} else {
152 		memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size);
153 	}
154 
155 	/* Skip over the ELF header */
156 	elf64 = (Elf64_Ehdr *)vmlinux.addr;
157 	if ( elf64->e_ident[EI_MAG0]  != ELFMAG0	||
158 	     elf64->e_ident[EI_MAG1]  != ELFMAG1	||
159 	     elf64->e_ident[EI_MAG2]  != ELFMAG2	||
160 	     elf64->e_ident[EI_MAG3]  != ELFMAG3	||
161 	     elf64->e_ident[EI_CLASS] != ELFCLASS64	||
162 	     elf64->e_ident[EI_DATA]  != ELFDATA2MSB	||
163 	     elf64->e_type            != ET_EXEC	||
164 	     elf64->e_machine         != EM_PPC64 )
165 	{
166 		printf("Error: not a valid PPC64 ELF file!\n\r");
167 		exit();
168 	}
169 
170 	elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
171 				(unsigned long)elf64->e_phoff);
172 	for(i=0; i < (unsigned int)elf64->e_phnum ;i++,elf64ph++) {
173 		if (elf64ph->p_type == PT_LOAD && elf64ph->p_offset != 0)
174 			break;
175 	}
176 	printf("... skipping 0x%lx bytes of ELF header\n\r",
177 			(unsigned long)elf64ph->p_offset);
178 	vmlinux.addr += (unsigned long)elf64ph->p_offset;
179 	vmlinux.size -= (unsigned long)elf64ph->p_offset;
180 
181 	flush_cache((void *)vmlinux.addr, vmlinux.memsize);
182 
183 	bi_recs = make_bi_recs(vmlinux.addr + vmlinux.memsize);
184 
185 	kernel_entry = (kernel_entry_t)vmlinux.addr;
186 	printf( "kernel:\n\r"
187 		"        entry addr = 0x%lx\n\r"
188 		"        a1         = 0x%lx,\n\r"
189 		"        a2         = 0x%lx,\n\r"
190 		"        prom       = 0x%lx,\n\r"
191 		"        bi_recs    = 0x%lx,\n\r",
192 		(unsigned long)kernel_entry, a1, a2,
193 		(unsigned long)prom, (unsigned long)bi_recs);
194 
195 	kernel_entry( a1, a2, prom, bi_recs );
196 
197 	printf("Error: Linux kernel returned to zImage bootloader!\n\r");
198 
199 	exit();
200 }
201 
202 static struct bi_record *
make_bi_recs(unsigned long addr)203 make_bi_recs(unsigned long addr)
204 {
205 	struct bi_record *bi_recs;
206 	struct bi_record *rec;
207 
208 	bi_recs = rec = bi_rec_init(addr);
209 
210 	rec = bi_rec_alloc(rec, 2);
211 	rec->tag = BI_FIRST;
212 	/* rec->data[0] = ...;	# Written below before return */
213 	/* rec->data[1] = ...;	# Written below before return */
214 
215 	rec = bi_rec_alloc_bytes(rec, strlen("chrpboot")+1);
216 	rec->tag = BI_BOOTLOADER_ID;
217 	sprintf( (char *)rec->data, "chrpboot");
218 
219 	rec = bi_rec_alloc(rec, 2);
220 	rec->tag = BI_MACHTYPE;
221 	rec->data[0] = PLATFORM_PSERIES;
222 	rec->data[1] = 1;
223 
224 	if ( initrd.size > 0 ) {
225 		rec = bi_rec_alloc(rec, 2);
226 		rec->tag = BI_INITRD;
227 		rec->data[0] = initrd.addr;
228 		rec->data[1] = initrd.size;
229 	}
230 
231 	if ( sysmap.size > 0 ) {
232 		rec = bi_rec_alloc(rec, 2);
233 		rec->tag = BI_SYSMAP;
234 		rec->data[0] = (unsigned long)sysmap.addr;
235 		rec->data[1] = (unsigned long)sysmap.size;
236 	}
237 
238 	rec = bi_rec_alloc(rec, 1);
239 	rec->tag = BI_LAST;
240 	rec->data[0] = (bi_rec_field)bi_recs;
241 
242 	/* Save the _end_ address of the bi_rec's in the first bi_rec
243 	 * data field for easy access by the kernel.
244 	 */
245 	bi_recs->data[0] = (bi_rec_field)rec;
246 	bi_recs->data[1] = (bi_rec_field)rec + rec->size - (bi_rec_field)bi_recs;
247 
248 	return bi_recs;
249 }
250 
251 struct memchunk {
252 	unsigned int size;
253 	unsigned int pad;
254 	struct memchunk *next;
255 };
256 
257 static struct memchunk *freechunks;
258 
zalloc(void * x,unsigned items,unsigned size)259 void *zalloc(void *x, unsigned items, unsigned size)
260 {
261 	void *p;
262 	struct memchunk **mpp, *mp;
263 
264 	size *= items;
265 	size = _ALIGN(size, sizeof(struct memchunk));
266 	heap_use += size;
267 	if (heap_use > heap_max)
268 		heap_max = heap_use;
269 	for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) {
270 		if (mp->size == size) {
271 			*mpp = mp->next;
272 			return mp;
273 		}
274 	}
275 	p = avail_ram;
276 	avail_ram += size;
277 	if (avail_ram > avail_high)
278 		avail_high = avail_ram;
279 	if (avail_ram > end_avail) {
280 		printf("oops... out of memory\n\r");
281 		pause();
282 	}
283 	return p;
284 }
285 
zfree(void * x,void * addr,unsigned nb)286 void zfree(void *x, void *addr, unsigned nb)
287 {
288 	struct memchunk *mp = addr;
289 
290 	nb = _ALIGN(nb, sizeof(struct memchunk));
291 	heap_use -= nb;
292 	if (avail_ram == addr + nb) {
293 		avail_ram = addr;
294 		return;
295 	}
296 	mp->size = nb;
297 	mp->next = freechunks;
298 	freechunks = mp;
299 }
300 
301 #define HEAD_CRC	2
302 #define EXTRA_FIELD	4
303 #define ORIG_NAME	8
304 #define COMMENT		0x10
305 #define RESERVED	0xe0
306 
307 #define DEFLATED	8
308 
gunzip(void * dst,int dstlen,unsigned char * src,int * lenp)309 void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
310 {
311 	z_stream s;
312 	int r, i, flags;
313 
314 	/* skip header */
315 	i = 10;
316 	flags = src[3];
317 	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
318 		printf("bad gzipped data\n\r");
319 		exit();
320 	}
321 	if ((flags & EXTRA_FIELD) != 0)
322 		i = 12 + src[10] + (src[11] << 8);
323 	if ((flags & ORIG_NAME) != 0)
324 		while (src[i++] != 0)
325 			;
326 	if ((flags & COMMENT) != 0)
327 		while (src[i++] != 0)
328 			;
329 	if ((flags & HEAD_CRC) != 0)
330 		i += 2;
331 	if (i >= *lenp) {
332 		printf("gunzip: ran out of data in header\n\r");
333 		exit();
334 	}
335 
336 	s.zalloc = zalloc;
337 	s.zfree = zfree;
338 	r = inflateInit2(&s, -MAX_WBITS);
339 	if (r != Z_OK) {
340 		printf("inflateInit2 returned %d\n\r", r);
341 		exit();
342 	}
343 	s.next_in = src + i;
344 	s.avail_in = *lenp - i;
345 	s.next_out = dst;
346 	s.avail_out = dstlen;
347 	r = inflate(&s, Z_FINISH);
348 	if (r != Z_OK && r != Z_STREAM_END) {
349 		printf("inflate returned %d msg: %s\n\r", r, s.msg);
350 		exit();
351 	}
352 	*lenp = s.next_out - (unsigned char *) dst;
353 	inflateEnd(&s);
354 }
355 
356