2 * Copyright (C) Paul Mackerras 1997.
4 * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner.
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.
11 #include "ppc32-types.h"
13 #include <linux/elf.h>
14 #include <linux/string.h>
15 #include <asm/processor.h>
17 #include <asm/bootinfo.h>
19 extern void *finddevice(const char *);
20 extern int getprop(void *, const char *, void *, int);
21 extern void printk(char *fmt, ...);
22 extern void printf(const char *fmt, ...);
23 extern int sprintf(char *buf, const char *fmt, ...);
24 void gunzip(void *, int, unsigned char *, int *);
25 void *claim(unsigned int, unsigned int, unsigned int);
26 void flush_cache(void *, unsigned long);
28 extern void exit(void);
30 unsigned long strlen(const char *s);
31 void *memmove(void *dest, const void *src, unsigned long n);
32 void *memcpy(void *dest, const void *src, unsigned long n);
34 static struct bi_record *make_bi_recs(unsigned long);
36 #define RAM_START 0x00000000
37 #define RAM_END (64<<20)
39 /* Value picked to match that used by yaboot */
40 #define PROG_START 0x01400000
43 char *begin_avail, *end_avail;
45 unsigned int heap_use;
46 unsigned int heap_max;
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 extern unsigned long vmlinux_filesize;
56 extern unsigned long vmlinux_memsize;
61 unsigned long memsize;
63 struct addr_range vmlinux = {0, 0, 0};
64 struct addr_range vmlinuz = {0, 0, 0};
65 struct addr_range sysmap = {0, 0, 0};
66 struct addr_range initrd = {0, 0, 0};
68 static char scratch[128<<10]; /* 128kB of scratch space for gunzip */
70 typedef void (*kernel_entry_t)( unsigned long,
85 start(unsigned long a1, unsigned long a2, void *promptr)
87 unsigned long i, claim_addr, claim_size;
88 struct bi_record *bi_recs;
89 kernel_entry_t kernel_entry;
93 prom = (int (*)(void *)) promptr;
94 chosen_handle = finddevice("/chosen");
95 if (chosen_handle == (void *) -1)
97 if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
100 if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4)
103 printf("zImage starting: loaded at 0x%x\n\r", (unsigned)_start);
106 sysmap.size = (unsigned long)(_sysmap_end - _sysmap_start);
107 sysmap.memsize = sysmap.size;
108 if ( sysmap.size > 0 ) {
109 sysmap.addr = (RAM_END - sysmap.size) & ~0xFFF;
110 claim(sysmap.addr, RAM_END - sysmap.addr, 0);
111 printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r",
112 sysmap.addr, (unsigned long)_sysmap_start, sysmap.size);
113 memcpy((void *)sysmap.addr, (void *)_sysmap_start, sysmap.size);
117 initrd.size = (unsigned long)(_initrd_end - _initrd_start);
118 initrd.memsize = initrd.size;
119 if ( initrd.size > 0 ) {
120 initrd.addr = (RAM_END - initrd.size) & ~0xFFF;
122 claim(initrd.addr, RAM_END - initrd.addr, 0);
123 printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r",
124 initrd.addr, (unsigned long)_initrd_start, initrd.size);
125 memcpy((void *)initrd.addr, (void *)_initrd_start, initrd.size);
128 vmlinuz.addr = (unsigned long)_vmlinux_start;
129 vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
130 vmlinux.addr = (unsigned long)(void *)-1;
131 vmlinux.size = PAGE_ALIGN(vmlinux_filesize);
132 vmlinux.memsize = vmlinux_memsize;
134 claim_size = vmlinux.memsize /* PPPBBB: + fudge for bi_recs */;
135 for(claim_addr = PROG_START;
136 claim_addr <= PROG_START * 8;
137 claim_addr += 0x100000) {
139 printf(" trying: 0x%08lx\n\r", claim_addr);
141 vmlinux.addr = (unsigned long)claim(claim_addr, claim_size, 0);
142 if ((void *)vmlinux.addr != (void *)-1) break;
144 if ((void *)vmlinux.addr == (void *)-1) {
145 printf("claim error, can't allocate kernel memory\n\r");
149 /* PPPBBB: should kernel always be gziped? */
150 if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
152 begin_avail = avail_high = avail_ram;
153 end_avail = scratch + sizeof(scratch);
154 printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...",
155 vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size);
156 gunzip((void *)vmlinux.addr, vmlinux.size,
157 (unsigned char *)vmlinuz.addr, (int *)&vmlinuz.size);
158 printf("done %lu bytes\n\r", vmlinuz.size);
159 printf("%u bytes of heap consumed, max in use %u\n\r",
160 (unsigned)(avail_high - begin_avail), heap_max);
162 memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size);
165 /* Skip over the ELF header */
166 elf64 = (Elf64_Ehdr *)vmlinux.addr;
167 if ( elf64->e_ident[EI_MAG0] != ELFMAG0 ||
168 elf64->e_ident[EI_MAG1] != ELFMAG1 ||
169 elf64->e_ident[EI_MAG2] != ELFMAG2 ||
170 elf64->e_ident[EI_MAG3] != ELFMAG3 ||
171 elf64->e_ident[EI_CLASS] != ELFCLASS64 ||
172 elf64->e_ident[EI_DATA] != ELFDATA2MSB ||
173 elf64->e_type != ET_EXEC ||
174 elf64->e_machine != EM_PPC64 )
176 printf("Error: not a valid PPC64 ELF file!\n\r");
180 elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
181 (unsigned long)elf64->e_phoff);
182 for(i=0; i < (unsigned int)elf64->e_phnum ;i++,elf64ph++) {
183 if (elf64ph->p_type == PT_LOAD && elf64ph->p_offset != 0)
187 printf("... skipping 0x%lx bytes of ELF header\n\r",
188 (unsigned long)elf64ph->p_offset);
190 vmlinux.addr += (unsigned long)elf64ph->p_offset;
191 vmlinux.size -= (unsigned long)elf64ph->p_offset;
193 flush_cache((void *)vmlinux.addr, vmlinux.memsize);
195 bi_recs = make_bi_recs(vmlinux.addr + vmlinux.memsize);
197 kernel_entry = (kernel_entry_t)vmlinux.addr;
199 printf( "kernel:\n\r"
200 " entry addr = 0x%lx\n\r"
204 " bi_recs = 0x%lx,\n\r",
205 (unsigned long)kernel_entry, a1, a2,
206 (unsigned long)prom, (unsigned long)bi_recs);
209 kernel_entry( a1, a2, prom, bi_recs );
211 printf("Error: Linux kernel returned to zImage bootloader!\n\r");
216 static struct bi_record *
217 make_bi_recs(unsigned long addr)
219 struct bi_record *bi_recs;
220 struct bi_record *rec;
222 bi_recs = rec = bi_rec_init(addr);
224 rec = bi_rec_alloc(rec, 2);
226 /* rec->data[0] = ...; # Written below before return */
227 /* rec->data[1] = ...; # Written below before return */
229 rec = bi_rec_alloc_bytes(rec, strlen("chrpboot")+1);
230 rec->tag = BI_BOOTLOADER_ID;
231 sprintf( (char *)rec->data, "chrpboot");
233 rec = bi_rec_alloc(rec, 2);
234 rec->tag = BI_MACHTYPE;
235 rec->data[0] = PLATFORM_PSERIES;
238 if ( initrd.size > 0 ) {
239 rec = bi_rec_alloc(rec, 2);
240 rec->tag = BI_INITRD;
241 rec->data[0] = initrd.addr;
242 rec->data[1] = initrd.size;
245 if ( sysmap.size > 0 ) {
246 rec = bi_rec_alloc(rec, 2);
247 rec->tag = BI_SYSMAP;
248 rec->data[0] = (unsigned long)sysmap.addr;
249 rec->data[1] = (unsigned long)sysmap.size;
252 rec = bi_rec_alloc(rec, 1);
254 rec->data[0] = (bi_rec_field)bi_recs;
256 /* Save the _end_ address of the bi_rec's in the first bi_rec
257 * data field for easy access by the kernel.
259 bi_recs->data[0] = (bi_rec_field)rec;
260 bi_recs->data[1] = (bi_rec_field)rec + rec->size - (bi_rec_field)bi_recs;
268 struct memchunk *next;
271 static struct memchunk *freechunks;
273 void *zalloc(void *x, unsigned items, unsigned size)
276 struct memchunk **mpp, *mp;
279 size = _ALIGN(size, sizeof(struct memchunk));
281 if (heap_use > heap_max)
283 for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) {
284 if (mp->size == size) {
291 if (avail_ram > avail_high)
292 avail_high = avail_ram;
293 if (avail_ram > end_avail) {
294 printf("oops... out of memory\n\r");
300 void zfree(void *x, void *addr, unsigned nb)
302 struct memchunk *mp = addr;
304 nb = _ALIGN(nb, sizeof(struct memchunk));
306 if (avail_ram == addr + nb) {
311 mp->next = freechunks;
316 #define EXTRA_FIELD 4
319 #define RESERVED 0xe0
323 void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
331 if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
332 printf("bad gzipped data\n\r");
335 if ((flags & EXTRA_FIELD) != 0)
336 i = 12 + src[10] + (src[11] << 8);
337 if ((flags & ORIG_NAME) != 0)
338 while (src[i++] != 0)
340 if ((flags & COMMENT) != 0)
341 while (src[i++] != 0)
343 if ((flags & HEAD_CRC) != 0)
346 printf("gunzip: ran out of data in header\n\r");
352 r = inflateInit2(&s, -MAX_WBITS);
354 printf("inflateInit2 returned %d\n\r", r);
358 s.avail_in = *lenp - i;
360 s.avail_out = dstlen;
361 r = inflate(&s, Z_FINISH);
362 if (r != Z_OK && r != Z_STREAM_END) {
363 printf("inflate returned %d msg: %s\n\r", r, s.msg);
366 *lenp = s.next_out - (unsigned char *) dst;