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 "zlib.h" 18 #include "ops.h" 19 #include "flatdevtree.h" 20 21 extern void flush_cache(void *, unsigned long); 22 23 extern char _start[]; 24 extern char __bss_start[]; 25 extern char _end[]; 26 extern char _vmlinux_start[]; 27 extern char _vmlinux_end[]; 28 extern char _initrd_start[]; 29 extern char _initrd_end[]; 30 31 struct addr_range { 32 unsigned long addr; 33 unsigned long size; 34 unsigned long memsize; 35 }; 36 static struct addr_range vmlinux; 37 static struct addr_range vmlinuz; 38 static struct addr_range initrd; 39 40 static unsigned long elfoffset; 41 static int is_64bit; 42 43 /* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ 44 static char scratch[46912]; 45 static char elfheader[256]; 46 47 typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); 48 49 #undef DEBUG 50 51 #define HEAD_CRC 2 52 #define EXTRA_FIELD 4 53 #define ORIG_NAME 8 54 #define COMMENT 0x10 55 #define RESERVED 0xe0 56 57 static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) 58 { 59 z_stream s; 60 int r, i, flags; 61 62 /* skip header */ 63 i = 10; 64 flags = src[3]; 65 if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { 66 printf("bad gzipped data\n\r"); 67 exit(); 68 } 69 if ((flags & EXTRA_FIELD) != 0) 70 i = 12 + src[10] + (src[11] << 8); 71 if ((flags & ORIG_NAME) != 0) 72 while (src[i++] != 0) 73 ; 74 if ((flags & COMMENT) != 0) 75 while (src[i++] != 0) 76 ; 77 if ((flags & HEAD_CRC) != 0) 78 i += 2; 79 if (i >= *lenp) { 80 printf("gunzip: ran out of data in header\n\r"); 81 exit(); 82 } 83 84 if (zlib_inflate_workspacesize() > sizeof(scratch)) { 85 printf("gunzip needs more mem\n"); 86 exit(); 87 } 88 memset(&s, 0, sizeof(s)); 89 s.workspace = scratch; 90 r = zlib_inflateInit2(&s, -MAX_WBITS); 91 if (r != Z_OK) { 92 printf("inflateInit2 returned %d\n\r", r); 93 exit(); 94 } 95 s.next_in = src + i; 96 s.avail_in = *lenp - i; 97 s.next_out = dst; 98 s.avail_out = dstlen; 99 r = zlib_inflate(&s, Z_FULL_FLUSH); 100 if (r != Z_OK && r != Z_STREAM_END) { 101 printf("inflate returned %d msg: %s\n\r", r, s.msg); 102 exit(); 103 } 104 *lenp = s.next_out - (unsigned char *) dst; 105 zlib_inflateEnd(&s); 106 } 107 108 static int is_elf64(void *hdr) 109 { 110 Elf64_Ehdr *elf64 = hdr; 111 Elf64_Phdr *elf64ph; 112 unsigned int i; 113 114 if (!(elf64->e_ident[EI_MAG0] == ELFMAG0 && 115 elf64->e_ident[EI_MAG1] == ELFMAG1 && 116 elf64->e_ident[EI_MAG2] == ELFMAG2 && 117 elf64->e_ident[EI_MAG3] == ELFMAG3 && 118 elf64->e_ident[EI_CLASS] == ELFCLASS64 && 119 elf64->e_ident[EI_DATA] == ELFDATA2MSB && 120 elf64->e_type == ET_EXEC && 121 elf64->e_machine == EM_PPC64)) 122 return 0; 123 124 elf64ph = (Elf64_Phdr *)((unsigned long)elf64 + 125 (unsigned long)elf64->e_phoff); 126 for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++) 127 if (elf64ph->p_type == PT_LOAD) 128 break; 129 if (i >= (unsigned int)elf64->e_phnum) 130 return 0; 131 132 elfoffset = (unsigned long)elf64ph->p_offset; 133 vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset; 134 vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset; 135 136 is_64bit = 1; 137 return 1; 138 } 139 140 static int is_elf32(void *hdr) 141 { 142 Elf32_Ehdr *elf32 = hdr; 143 Elf32_Phdr *elf32ph; 144 unsigned int i; 145 146 if (!(elf32->e_ident[EI_MAG0] == ELFMAG0 && 147 elf32->e_ident[EI_MAG1] == ELFMAG1 && 148 elf32->e_ident[EI_MAG2] == ELFMAG2 && 149 elf32->e_ident[EI_MAG3] == ELFMAG3 && 150 elf32->e_ident[EI_CLASS] == ELFCLASS32 && 151 elf32->e_ident[EI_DATA] == ELFDATA2MSB && 152 elf32->e_type == ET_EXEC && 153 elf32->e_machine == EM_PPC)) 154 return 0; 155 156 elf32 = (Elf32_Ehdr *)elfheader; 157 elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff); 158 for (i = 0; i < elf32->e_phnum; i++, elf32ph++) 159 if (elf32ph->p_type == PT_LOAD) 160 break; 161 if (i >= elf32->e_phnum) 162 return 0; 163 164 elfoffset = elf32ph->p_offset; 165 vmlinux.size = elf32ph->p_filesz + elf32ph->p_offset; 166 vmlinux.memsize = elf32ph->p_memsz + elf32ph->p_offset; 167 return 1; 168 } 169 170 static void prep_kernel(unsigned long *a1, unsigned long *a2) 171 { 172 int len; 173 174 vmlinuz.addr = (unsigned long)_vmlinux_start; 175 vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); 176 177 /* gunzip the ELF header of the kernel */ 178 if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { 179 len = vmlinuz.size; 180 gunzip(elfheader, sizeof(elfheader), 181 (unsigned char *)vmlinuz.addr, &len); 182 } else 183 memcpy(elfheader, (const void *)vmlinuz.addr, 184 sizeof(elfheader)); 185 186 if (!is_elf64(elfheader) && !is_elf32(elfheader)) { 187 printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); 188 exit(); 189 } 190 if (platform_ops.image_hdr) 191 platform_ops.image_hdr(elfheader); 192 193 /* We need to alloc the memsize plus the file offset since gzip 194 * will expand the header (file offset), then the kernel, then 195 * possible rubbish we don't care about. But the kernel bss must 196 * be claimed (it will be zero'd by the kernel itself) 197 */ 198 printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); 199 vmlinux.addr = (unsigned long)malloc(vmlinux.memsize); 200 if (vmlinux.addr == 0) { 201 printf("Can't allocate memory for kernel image !\n\r"); 202 exit(); 203 } 204 205 /* 206 * Now we try to alloc memory for the initrd (and copy it there) 207 */ 208 initrd.size = (unsigned long)(_initrd_end - _initrd_start); 209 initrd.memsize = initrd.size; 210 if ( initrd.size > 0 ) { 211 printf("Allocating 0x%lx bytes for initrd ...\n\r", 212 initrd.size); 213 initrd.addr = (unsigned long)malloc((u32)initrd.size); 214 if (initrd.addr == 0) { 215 printf("Can't allocate memory for initial " 216 "ramdisk !\n\r"); 217 exit(); 218 } 219 *a1 = initrd.addr; 220 *a2 = initrd.size; 221 printf("initial ramdisk moving 0x%lx <- 0x%lx " 222 "(0x%lx bytes)\n\r", initrd.addr, 223 (unsigned long)_initrd_start, initrd.size); 224 memmove((void *)initrd.addr, (void *)_initrd_start, 225 initrd.size); 226 printf("initrd head: 0x%lx\n\r", 227 *((unsigned long *)initrd.addr)); 228 } 229 230 /* Eventually gunzip the kernel */ 231 if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { 232 printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", 233 vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); 234 len = vmlinuz.size; 235 gunzip((void *)vmlinux.addr, vmlinux.memsize, 236 (unsigned char *)vmlinuz.addr, &len); 237 printf("done 0x%lx bytes\n\r", len); 238 } else { 239 memmove((void *)vmlinux.addr,(void *)vmlinuz.addr, 240 vmlinuz.size); 241 } 242 243 /* Skip over the ELF header */ 244 #ifdef DEBUG 245 printf("... skipping 0x%lx bytes of ELF header\n\r", 246 elfoffset); 247 #endif 248 vmlinux.addr += elfoffset; 249 250 flush_cache((void *)vmlinux.addr, vmlinux.size); 251 } 252 253 void __attribute__ ((weak)) ft_init(void *dt_blob) 254 { 255 } 256 257 /* A buffer that may be edited by tools operating on a zImage binary so as to 258 * edit the command line passed to vmlinux (by setting /chosen/bootargs). 259 * The buffer is put in it's own section so that tools may locate it easier. 260 */ 261 static char builtin_cmdline[COMMAND_LINE_SIZE] 262 __attribute__((__section__("__builtin_cmdline"))); 263 264 static void get_cmdline(char *buf, int size) 265 { 266 void *devp; 267 int len = strlen(builtin_cmdline); 268 269 buf[0] = '\0'; 270 271 if (len > 0) { /* builtin_cmdline overrides dt's /chosen/bootargs */ 272 len = min(len, size-1); 273 strncpy(buf, builtin_cmdline, len); 274 buf[len] = '\0'; 275 } 276 else if ((devp = finddevice("/chosen"))) 277 getprop(devp, "bootargs", buf, size); 278 } 279 280 static void set_cmdline(char *buf) 281 { 282 void *devp; 283 284 if ((devp = finddevice("/chosen"))) 285 setprop(devp, "bootargs", buf, strlen(buf) + 1); 286 } 287 288 /* Section where ft can be tacked on after zImage is built */ 289 union blobspace { 290 struct boot_param_header hdr; 291 char space[8*1024]; 292 } dt_blob __attribute__((__section__("__builtin_ft"))); 293 294 struct platform_ops platform_ops; 295 struct dt_ops dt_ops; 296 struct console_ops console_ops; 297 298 void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) 299 { 300 int have_dt = 0; 301 kernel_entry_t kentry; 302 char cmdline[COMMAND_LINE_SIZE]; 303 304 memset(__bss_start, 0, _end - __bss_start); 305 memset(&platform_ops, 0, sizeof(platform_ops)); 306 memset(&dt_ops, 0, sizeof(dt_ops)); 307 memset(&console_ops, 0, sizeof(console_ops)); 308 309 /* Override the dt_ops and device tree if there was an flat dev 310 * tree attached to the zImage. 311 */ 312 if (dt_blob.hdr.magic == OF_DT_HEADER) { 313 have_dt = 1; 314 ft_init(&dt_blob); 315 } 316 317 if (platform_init(promptr)) 318 exit(); 319 if (console_ops.open && (console_ops.open() < 0)) 320 exit(); 321 if (platform_ops.fixups) 322 platform_ops.fixups(); 323 324 printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", 325 _start, sp); 326 327 prep_kernel(&a1, &a2); 328 329 /* If cmdline came from zimage wrapper or if we can edit the one 330 * in the dt, print it out and edit it, if possible. 331 */ 332 if ((strlen(builtin_cmdline) > 0) || console_ops.edit_cmdline) { 333 get_cmdline(cmdline, COMMAND_LINE_SIZE); 334 printf("\n\rLinux/PowerPC load: %s", cmdline); 335 if (console_ops.edit_cmdline) 336 console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE); 337 printf("\n\r"); 338 set_cmdline(cmdline); 339 } 340 341 if (console_ops.close) 342 console_ops.close(); 343 344 kentry = (kernel_entry_t) vmlinux.addr; 345 if (have_dt) 346 kentry(dt_ops.ft_addr(), 0, NULL); 347 else 348 /* XXX initrd addr/size should be passed in properties */ 349 kentry(a1, a2, promptr); 350 351 /* console closed so printf below may not work */ 352 printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); 353 exit(); 354 } 355