main.c 9.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * Copyright (C) Paul Mackerras 1997.
 *
 * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */
11 12 13 14 15 16
#include <stdarg.h>
#include <stddef.h>
#include "elf.h"
#include "page.h"
#include "string.h"
#include "stdio.h"
L
Linus Torvalds 已提交
17
#include "zlib.h"
18 19
#include "ops.h"
#include "flatdevtree.h"
20 21 22

extern void flush_cache(void *, unsigned long);

L
Linus Torvalds 已提交
23
extern char _start[];
24
extern char __bss_start[];
25
extern char _end[];
L
Linus Torvalds 已提交
26 27 28 29
extern char _vmlinux_start[];
extern char _vmlinux_end[];
extern char _initrd_start[];
extern char _initrd_end[];
30 31
extern char _dtb_start[];
extern char _dtb_end[];
L
Linus Torvalds 已提交
32 33 34 35 36 37

struct addr_range {
	unsigned long addr;
	unsigned long size;
	unsigned long memsize;
};
38 39 40
static struct addr_range vmlinux;
static struct addr_range vmlinuz;
static struct addr_range initrd;
L
Linus Torvalds 已提交
41

42
static unsigned long elfoffset;
43
static int is_64bit;
44

45 46
/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */
static char scratch[46912];
47
static char elfheader[256];
O
Olaf Hering 已提交
48

49
typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *);
L
Linus Torvalds 已提交
50 51 52

#undef DEBUG

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#define HEAD_CRC	2
#define EXTRA_FIELD	4
#define ORIG_NAME	8
#define COMMENT		0x10
#define RESERVED	0xe0

static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
{
	z_stream s;
	int r, i, flags;

	/* skip header */
	i = 10;
	flags = src[3];
	if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
		printf("bad gzipped data\n\r");
		exit();
	}
	if ((flags & EXTRA_FIELD) != 0)
		i = 12 + src[10] + (src[11] << 8);
	if ((flags & ORIG_NAME) != 0)
		while (src[i++] != 0)
			;
	if ((flags & COMMENT) != 0)
		while (src[i++] != 0)
			;
	if ((flags & HEAD_CRC) != 0)
		i += 2;
	if (i >= *lenp) {
		printf("gunzip: ran out of data in header\n\r");
		exit();
	}

	if (zlib_inflate_workspacesize() > sizeof(scratch)) {
		printf("gunzip needs more mem\n");
		exit();
	}
	memset(&s, 0, sizeof(s));
	s.workspace = scratch;
	r = zlib_inflateInit2(&s, -MAX_WBITS);
	if (r != Z_OK) {
		printf("inflateInit2 returned %d\n\r", r);
		exit();
	}
	s.next_in = src + i;
	s.avail_in = *lenp - i;
	s.next_out = dst;
	s.avail_out = dstlen;
	r = zlib_inflate(&s, Z_FULL_FLUSH);
	if (r != Z_OK && r != Z_STREAM_END) {
		printf("inflate returned %d msg: %s\n\r", r, s.msg);
		exit();
	}
	*lenp = s.next_out - (unsigned char *) dst;
	zlib_inflateEnd(&s);
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
static int is_elf64(void *hdr)
{
	Elf64_Ehdr *elf64 = hdr;
	Elf64_Phdr *elf64ph;
	unsigned int i;

	if (!(elf64->e_ident[EI_MAG0]  == ELFMAG0	&&
	      elf64->e_ident[EI_MAG1]  == ELFMAG1	&&
	      elf64->e_ident[EI_MAG2]  == ELFMAG2	&&
	      elf64->e_ident[EI_MAG3]  == ELFMAG3	&&
	      elf64->e_ident[EI_CLASS] == ELFCLASS64	&&
	      elf64->e_ident[EI_DATA]  == ELFDATA2MSB	&&
	      elf64->e_type            == ET_EXEC	&&
	      elf64->e_machine         == EM_PPC64))
		return 0;

	elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
				 (unsigned long)elf64->e_phoff);
	for (i = 0; i < (unsigned int)elf64->e_phnum; i++, elf64ph++)
129
		if (elf64ph->p_type == PT_LOAD)
130 131 132 133 134 135 136
			break;
	if (i >= (unsigned int)elf64->e_phnum)
		return 0;

	elfoffset = (unsigned long)elf64ph->p_offset;
	vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset;
	vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset;
137

138
	is_64bit = 1;
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
	return 1;
}

static int is_elf32(void *hdr)
{
	Elf32_Ehdr *elf32 = hdr;
	Elf32_Phdr *elf32ph;
	unsigned int i;

	if (!(elf32->e_ident[EI_MAG0]  == ELFMAG0	&&
	      elf32->e_ident[EI_MAG1]  == ELFMAG1	&&
	      elf32->e_ident[EI_MAG2]  == ELFMAG2	&&
	      elf32->e_ident[EI_MAG3]  == ELFMAG3	&&
	      elf32->e_ident[EI_CLASS] == ELFCLASS32	&&
	      elf32->e_ident[EI_DATA]  == ELFDATA2MSB	&&
	      elf32->e_type            == ET_EXEC	&&
	      elf32->e_machine         == EM_PPC))
		return 0;

	elf32 = (Elf32_Ehdr *)elfheader;
	elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff);
	for (i = 0; i < elf32->e_phnum; i++, elf32ph++)
161
		if (elf32ph->p_type == PT_LOAD)
162 163 164 165 166 167 168 169 170 171
			break;
	if (i >= elf32->e_phnum)
		return 0;

	elfoffset = elf32ph->p_offset;
	vmlinux.size = elf32ph->p_filesz + elf32ph->p_offset;
	vmlinux.memsize = elf32ph->p_memsz + elf32ph->p_offset;
	return 1;
}

172
static void prep_kernel(unsigned long a1, unsigned long a2)
L
Linus Torvalds 已提交
173
{
174
	int len;
175

176 177 178 179 180 181 182 183 184
	vmlinuz.addr = (unsigned long)_vmlinux_start;
	vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);

	/* gunzip the ELF header of the kernel */
	if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
		len = vmlinuz.size;
		gunzip(elfheader, sizeof(elfheader),
				(unsigned char *)vmlinuz.addr, &len);
	} else
185 186
		memcpy(elfheader, (const void *)vmlinuz.addr,
		       sizeof(elfheader));
187 188 189 190 191

	if (!is_elf64(elfheader) && !is_elf32(elfheader)) {
		printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
		exit();
	}
192 193
	if (platform_ops.image_hdr)
		platform_ops.image_hdr(elfheader);
194

195
	/* We need to alloc the memsize plus the file offset since gzip
196 197 198 199
	 * will expand the header (file offset), then the kernel, then
	 * possible rubbish we don't care about. But the kernel bss must
	 * be claimed (it will be zero'd by the kernel itself)
	 */
200
	printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize);
201
	vmlinux.addr = (unsigned long)malloc(vmlinux.memsize);
L
Linus Torvalds 已提交
202 203 204 205 206 207
	if (vmlinux.addr == 0) {
		printf("Can't allocate memory for kernel image !\n\r");
		exit();
	}

	/*
208 209 210 211
	 * Now find the initrd
	 *
	 * First see if we have an image attached to us.  If so
	 * allocate memory for it and copy it there.
L
Linus Torvalds 已提交
212 213 214
	 */
	initrd.size = (unsigned long)(_initrd_end - _initrd_start);
	initrd.memsize = initrd.size;
215
	if (initrd.size > 0) {
216 217 218
		printf("Allocating 0x%lx bytes for initrd ...\n\r",
		       initrd.size);
		initrd.addr = (unsigned long)malloc((u32)initrd.size);
L
Linus Torvalds 已提交
219
		if (initrd.addr == 0) {
220 221
			printf("Can't allocate memory for initial "
					"ramdisk !\n\r");
L
Linus Torvalds 已提交
222 223
			exit();
		}
224 225 226 227 228 229 230
		printf("initial ramdisk moving 0x%lx <- 0x%lx "
			"(0x%lx bytes)\n\r", initrd.addr,
			(unsigned long)_initrd_start, initrd.size);
		memmove((void *)initrd.addr, (void *)_initrd_start,
			initrd.size);
		printf("initrd head: 0x%lx\n\r",
				*((unsigned long *)initrd.addr));
231 232 233 234 235 236
	} else if (a2 != 0) {
		/* Otherwise, see if yaboot or another loader gave us an initrd */
		initrd.addr = a1;
		initrd.memsize = initrd.size = a2;
		printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r",
		       initrd.addr, initrd.size);
L
Linus Torvalds 已提交
237 238 239 240 241 242 243
	}

	/* Eventually gunzip the kernel */
	if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
		printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...",
		       vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size);
		len = vmlinuz.size;
244
		gunzip((void *)vmlinux.addr, vmlinux.memsize,
L
Linus Torvalds 已提交
245 246 247
			(unsigned char *)vmlinuz.addr, &len);
		printf("done 0x%lx bytes\n\r", len);
	} else {
248 249
		memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,
			vmlinuz.size);
L
Linus Torvalds 已提交
250 251 252 253 254
	}

	/* Skip over the ELF header */
#ifdef DEBUG
	printf("... skipping 0x%lx bytes of ELF header\n\r",
255
			elfoffset);
L
Linus Torvalds 已提交
256
#endif
257
	vmlinux.addr += elfoffset;
L
Linus Torvalds 已提交
258 259

	flush_cache((void *)vmlinux.addr, vmlinux.size);
260
}
L
Linus Torvalds 已提交
261

262 263 264 265 266 267
/* A buffer that may be edited by tools operating on a zImage binary so as to
 * edit the command line passed to vmlinux (by setting /chosen/bootargs).
 * The buffer is put in it's own section so that tools may locate it easier.
 */
static char builtin_cmdline[COMMAND_LINE_SIZE]
	__attribute__((__section__("__builtin_cmdline")));
L
Linus Torvalds 已提交
268

269 270 271 272
static void get_cmdline(char *buf, int size)
{
	void *devp;
	int len = strlen(builtin_cmdline);
L
Linus Torvalds 已提交
273

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
	buf[0] = '\0';

	if (len > 0) { /* builtin_cmdline overrides dt's /chosen/bootargs */
		len = min(len, size-1);
		strncpy(buf, builtin_cmdline, len);
		buf[len] = '\0';
	}
	else if ((devp = finddevice("/chosen")))
		getprop(devp, "bootargs", buf, size);
}

static void set_cmdline(char *buf)
{
	void *devp;

	if ((devp = finddevice("/chosen")))
		setprop(devp, "bootargs", buf, strlen(buf) + 1);
L
Linus Torvalds 已提交
291 292
}

293 294 295 296 297 298 299 300 301 302 303 304 305 306
struct platform_ops platform_ops;
struct dt_ops dt_ops;
struct console_ops console_ops;

void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
{
	kernel_entry_t kentry;
	char cmdline[COMMAND_LINE_SIZE];

	memset(__bss_start, 0, _end - __bss_start);
	memset(&platform_ops, 0, sizeof(platform_ops));
	memset(&dt_ops, 0, sizeof(dt_ops));
	memset(&console_ops, 0, sizeof(console_ops));

307
	if (platform_init(promptr, _dtb_start, _dtb_end))
308 309 310 311 312 313 314 315 316
		exit();
	if (console_ops.open && (console_ops.open() < 0))
		exit();
	if (platform_ops.fixups)
		platform_ops.fixups();

	printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r",
	       _start, sp);

317
	prep_kernel(a1, a2);
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334

	/* If cmdline came from zimage wrapper or if we can edit the one
	 * in the dt, print it out and edit it, if possible.
	 */
	if ((strlen(builtin_cmdline) > 0) || console_ops.edit_cmdline) {
		get_cmdline(cmdline, COMMAND_LINE_SIZE);
		printf("\n\rLinux/PowerPC load: %s", cmdline);
		if (console_ops.edit_cmdline)
			console_ops.edit_cmdline(cmdline, COMMAND_LINE_SIZE);
		printf("\n\r");
		set_cmdline(cmdline);
	}

	if (console_ops.close)
		console_ops.close();

	kentry = (kernel_entry_t) vmlinux.addr;
335 336
	if (_dtb_end > _dtb_start) {
		dt_ops.ft_pack();
337
		kentry(dt_ops.ft_addr(), 0, NULL);
338
	}
339 340
	else
		/* XXX initrd addr/size should be passed in properties */
341
		kentry(initrd.addr, initrd.size, promptr);
342 343 344 345 346

	/* console closed so printf below may not work */
	printf("Error: Linux kernel returned to zImage boot wrapper!\n\r");
	exit();
}