at v2.6.12 330 lines 8.8 kB view raw
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#include "ppc32-types.h" 12#include "zlib.h" 13#include <linux/elf.h> 14#include <linux/string.h> 15#include <asm/processor.h> 16#include <asm/page.h> 17 18extern void *finddevice(const char *); 19extern int getprop(void *, const char *, void *, int); 20extern void printk(char *fmt, ...); 21extern void printf(const char *fmt, ...); 22extern int sprintf(char *buf, const char *fmt, ...); 23void gunzip(void *, int, unsigned char *, int *); 24void *claim(unsigned int, unsigned int, unsigned int); 25void flush_cache(void *, unsigned long); 26void pause(void); 27extern void exit(void); 28 29unsigned long strlen(const char *s); 30void *memmove(void *dest, const void *src, unsigned long n); 31void *memcpy(void *dest, const void *src, unsigned long n); 32 33/* Value picked to match that used by yaboot */ 34#define PROG_START 0x01400000 35#define RAM_END (256<<20) // Fixme: use OF */ 36 37char *avail_ram; 38char *begin_avail, *end_avail; 39char *avail_high; 40unsigned int heap_use; 41unsigned int heap_max; 42 43extern char _start[]; 44extern char _vmlinux_start[]; 45extern char _vmlinux_end[]; 46extern char _initrd_start[]; 47extern char _initrd_end[]; 48extern unsigned long vmlinux_filesize; 49extern unsigned long vmlinux_memsize; 50 51struct addr_range { 52 unsigned long addr; 53 unsigned long size; 54 unsigned long memsize; 55}; 56struct addr_range vmlinux = {0, 0, 0}; 57struct addr_range vmlinuz = {0, 0, 0}; 58struct addr_range initrd = {0, 0, 0}; 59 60static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ 61 62typedef void (*kernel_entry_t)( unsigned long, 63 unsigned long, 64 void *, 65 void *); 66 67 68int (*prom)(void *); 69 70void *chosen_handle; 71void *stdin; 72void *stdout; 73void *stderr; 74 75#undef DEBUG 76 77static unsigned long claim_base = PROG_START; 78 79static unsigned long try_claim(unsigned long size) 80{ 81 unsigned long addr = 0; 82 83 for(; claim_base < RAM_END; claim_base += 0x100000) { 84#ifdef DEBUG 85 printf(" trying: 0x%08lx\n\r", claim_base); 86#endif 87 addr = (unsigned long)claim(claim_base, size, 0); 88 if ((void *)addr != (void *)-1) 89 break; 90 } 91 if (addr == 0) 92 return 0; 93 claim_base = PAGE_ALIGN(claim_base + size); 94 return addr; 95} 96 97void start(unsigned long a1, unsigned long a2, void *promptr) 98{ 99 unsigned long i; 100 kernel_entry_t kernel_entry; 101 Elf64_Ehdr *elf64; 102 Elf64_Phdr *elf64ph; 103 104 prom = (int (*)(void *)) promptr; 105 chosen_handle = finddevice("/chosen"); 106 if (chosen_handle == (void *) -1) 107 exit(); 108 if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) 109 exit(); 110 stderr = stdout; 111 if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) 112 exit(); 113 114 printf("\n\rzImage starting: loaded at 0x%x\n\r", (unsigned)_start); 115 116 /* 117 * Now we try to claim some memory for the kernel itself 118 * our "vmlinux_memsize" is the memory footprint in RAM, _HOWEVER_, what 119 * our Makefile stuffs in is an image containing all sort of junk including 120 * an ELF header. We need to do some calculations here to find the right 121 * size... In practice we add 1Mb, that is enough, but we should really 122 * consider fixing the Makefile to put a _raw_ kernel in there ! 123 */ 124 vmlinux_memsize += 0x100000; 125 printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux_memsize); 126 vmlinux.addr = try_claim(vmlinux_memsize); 127 if (vmlinux.addr == 0) { 128 printf("Can't allocate memory for kernel image !\n\r"); 129 exit(); 130 } 131 vmlinuz.addr = (unsigned long)_vmlinux_start; 132 vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); 133 vmlinux.size = PAGE_ALIGN(vmlinux_filesize); 134 vmlinux.memsize = vmlinux_memsize; 135 136 /* 137 * Now we try to claim memory for the initrd (and copy it there) 138 */ 139 initrd.size = (unsigned long)(_initrd_end - _initrd_start); 140 initrd.memsize = initrd.size; 141 if ( initrd.size > 0 ) { 142 printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size); 143 initrd.addr = try_claim(initrd.size); 144 if (initrd.addr == 0) { 145 printf("Can't allocate memory for initial ramdisk !\n\r"); 146 exit(); 147 } 148 a1 = initrd.addr; 149 a2 = initrd.size; 150 printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r", 151 initrd.addr, (unsigned long)_initrd_start, initrd.size); 152 memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size); 153 printf("initrd head: 0x%lx\n\r", *((u32 *)initrd.addr)); 154 } 155 156 /* Eventually gunzip the kernel */ 157 if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { 158 int len; 159 avail_ram = scratch; 160 begin_avail = avail_high = avail_ram; 161 end_avail = scratch + sizeof(scratch); 162 printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", 163 vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); 164 len = vmlinuz.size; 165 gunzip((void *)vmlinux.addr, vmlinux.size, 166 (unsigned char *)vmlinuz.addr, &len); 167 printf("done 0x%lx bytes\n\r", len); 168 printf("0x%x bytes of heap consumed, max in use 0x%x\n\r", 169 (unsigned)(avail_high - begin_avail), heap_max); 170 } else { 171 memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size); 172 } 173 174 /* Skip over the ELF header */ 175 elf64 = (Elf64_Ehdr *)vmlinux.addr; 176 if ( elf64->e_ident[EI_MAG0] != ELFMAG0 || 177 elf64->e_ident[EI_MAG1] != ELFMAG1 || 178 elf64->e_ident[EI_MAG2] != ELFMAG2 || 179 elf64->e_ident[EI_MAG3] != ELFMAG3 || 180 elf64->e_ident[EI_CLASS] != ELFCLASS64 || 181 elf64->e_ident[EI_DATA] != ELFDATA2MSB || 182 elf64->e_type != ET_EXEC || 183 elf64->e_machine != EM_PPC64 ) 184 { 185 printf("Error: not a valid PPC64 ELF file!\n\r"); 186 exit(); 187 } 188 189 elf64ph = (Elf64_Phdr *)((unsigned long)elf64 + 190 (unsigned long)elf64->e_phoff); 191 for(i=0; i < (unsigned int)elf64->e_phnum ;i++,elf64ph++) { 192 if (elf64ph->p_type == PT_LOAD && elf64ph->p_offset != 0) 193 break; 194 } 195#ifdef DEBUG 196 printf("... skipping 0x%lx bytes of ELF header\n\r", 197 (unsigned long)elf64ph->p_offset); 198#endif 199 vmlinux.addr += (unsigned long)elf64ph->p_offset; 200 vmlinux.size -= (unsigned long)elf64ph->p_offset; 201 202 flush_cache((void *)vmlinux.addr, vmlinux.size); 203 204 if (a1) 205 printf("initrd head: 0x%lx\n\r", *((u32 *)initrd.addr)); 206 207 kernel_entry = (kernel_entry_t)vmlinux.addr; 208#ifdef DEBUG 209 printf( "kernel:\n\r" 210 " entry addr = 0x%lx\n\r" 211 " a1 = 0x%lx,\n\r" 212 " a2 = 0x%lx,\n\r" 213 " prom = 0x%lx,\n\r" 214 " bi_recs = 0x%lx,\n\r", 215 (unsigned long)kernel_entry, a1, a2, 216 (unsigned long)prom, NULL); 217#endif 218 219 kernel_entry( a1, a2, prom, NULL ); 220 221 printf("Error: Linux kernel returned to zImage bootloader!\n\r"); 222 223 exit(); 224} 225 226struct memchunk { 227 unsigned int size; 228 unsigned int pad; 229 struct memchunk *next; 230}; 231 232static struct memchunk *freechunks; 233 234void *zalloc(void *x, unsigned items, unsigned size) 235{ 236 void *p; 237 struct memchunk **mpp, *mp; 238 239 size *= items; 240 size = _ALIGN(size, sizeof(struct memchunk)); 241 heap_use += size; 242 if (heap_use > heap_max) 243 heap_max = heap_use; 244 for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) { 245 if (mp->size == size) { 246 *mpp = mp->next; 247 return mp; 248 } 249 } 250 p = avail_ram; 251 avail_ram += size; 252 if (avail_ram > avail_high) 253 avail_high = avail_ram; 254 if (avail_ram > end_avail) { 255 printf("oops... out of memory\n\r"); 256 pause(); 257 } 258 return p; 259} 260 261void zfree(void *x, void *addr, unsigned nb) 262{ 263 struct memchunk *mp = addr; 264 265 nb = _ALIGN(nb, sizeof(struct memchunk)); 266 heap_use -= nb; 267 if (avail_ram == addr + nb) { 268 avail_ram = addr; 269 return; 270 } 271 mp->size = nb; 272 mp->next = freechunks; 273 freechunks = mp; 274} 275 276#define HEAD_CRC 2 277#define EXTRA_FIELD 4 278#define ORIG_NAME 8 279#define COMMENT 0x10 280#define RESERVED 0xe0 281 282#define DEFLATED 8 283 284void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) 285{ 286 z_stream s; 287 int r, i, flags; 288 289 /* skip header */ 290 i = 10; 291 flags = src[3]; 292 if (src[2] != DEFLATED || (flags & RESERVED) != 0) { 293 printf("bad gzipped data\n\r"); 294 exit(); 295 } 296 if ((flags & EXTRA_FIELD) != 0) 297 i = 12 + src[10] + (src[11] << 8); 298 if ((flags & ORIG_NAME) != 0) 299 while (src[i++] != 0) 300 ; 301 if ((flags & COMMENT) != 0) 302 while (src[i++] != 0) 303 ; 304 if ((flags & HEAD_CRC) != 0) 305 i += 2; 306 if (i >= *lenp) { 307 printf("gunzip: ran out of data in header\n\r"); 308 exit(); 309 } 310 311 s.zalloc = zalloc; 312 s.zfree = zfree; 313 r = inflateInit2(&s, -MAX_WBITS); 314 if (r != Z_OK) { 315 printf("inflateInit2 returned %d\n\r", r); 316 exit(); 317 } 318 s.next_in = src + i; 319 s.avail_in = *lenp - i; 320 s.next_out = dst; 321 s.avail_out = dstlen; 322 r = inflate(&s, Z_FINISH); 323 if (r != Z_OK && r != Z_STREAM_END) { 324 printf("inflate returned %d msg: %s\n\r", r, s.msg); 325 exit(); 326 } 327 *lenp = s.next_out - (unsigned char *) dst; 328 inflateEnd(&s); 329} 330