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