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