aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/boot/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/boot/main.c')
-rw-r--r--arch/powerpc/boot/main.c250
1 files changed, 129 insertions, 121 deletions
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index b66634c9ea34..d719bb9333d1 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -14,17 +14,12 @@
14#include "page.h" 14#include "page.h"
15#include "string.h" 15#include "string.h"
16#include "stdio.h" 16#include "stdio.h"
17#include "prom.h"
18#include "zlib.h" 17#include "zlib.h"
18#include "ops.h"
19#include "flatdevtree.h"
19 20
20extern void flush_cache(void *, unsigned long); 21extern void flush_cache(void *, unsigned long);
21 22
22
23/* Value picked to match that used by yaboot */
24#define PROG_START 0x01400000 /* only used on 64-bit systems */
25#define RAM_END (512<<20) /* Fixme: use OF */
26#define ONE_MB 0x100000
27
28extern char _start[]; 23extern char _start[];
29extern char __bss_start[]; 24extern char __bss_start[];
30extern char _end[]; 25extern char _end[];
@@ -33,14 +28,6 @@ extern char _vmlinux_end[];
33extern char _initrd_start[]; 28extern char _initrd_start[];
34extern char _initrd_end[]; 29extern char _initrd_end[];
35 30
36/* A buffer that may be edited by tools operating on a zImage binary so as to
37 * edit the command line passed to vmlinux (by setting /chosen/bootargs).
38 * The buffer is put in it's own section so that tools may locate it easier.
39 */
40static char builtin_cmdline[512]
41 __attribute__((section("__builtin_cmdline")));
42
43
44struct addr_range { 31struct addr_range {
45 unsigned long addr; 32 unsigned long addr;
46 unsigned long size; 33 unsigned long size;
@@ -51,21 +38,16 @@ static struct addr_range vmlinuz;
51static struct addr_range initrd; 38static struct addr_range initrd;
52 39
53static unsigned long elfoffset; 40static unsigned long elfoffset;
41static int is_64bit;
54 42
55static char scratch[46912]; /* scratch space for gunzip, from zlib_inflate_workspacesize() */ 43/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */
44static char scratch[46912];
56static char elfheader[256]; 45static char elfheader[256];
57 46
58 47typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *);
59typedef void (*kernel_entry_t)( unsigned long,
60 unsigned long,
61 void *,
62 void *);
63
64 48
65#undef DEBUG 49#undef DEBUG
66 50
67static unsigned long claim_base;
68
69#define HEAD_CRC 2 51#define HEAD_CRC 2
70#define EXTRA_FIELD 4 52#define EXTRA_FIELD 4
71#define ORIG_NAME 8 53#define ORIG_NAME 8
@@ -123,24 +105,6 @@ static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
123 zlib_inflateEnd(&s); 105 zlib_inflateEnd(&s);
124} 106}
125 107
126static unsigned long try_claim(unsigned long size)
127{
128 unsigned long addr = 0;
129
130 for(; claim_base < RAM_END; claim_base += ONE_MB) {
131#ifdef DEBUG
132 printf(" trying: 0x%08lx\n\r", claim_base);
133#endif
134 addr = (unsigned long)claim(claim_base, size, 0);
135 if ((void *)addr != (void *)-1)
136 break;
137 }
138 if (addr == 0)
139 return 0;
140 claim_base = PAGE_ALIGN(claim_base + size);
141 return addr;
142}
143
144static int is_elf64(void *hdr) 108static int is_elf64(void *hdr)
145{ 109{
146 Elf64_Ehdr *elf64 = hdr; 110 Elf64_Ehdr *elf64 = hdr;
@@ -169,16 +133,7 @@ static int is_elf64(void *hdr)
169 vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset; 133 vmlinux.size = (unsigned long)elf64ph->p_filesz + elfoffset;
170 vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset; 134 vmlinux.memsize = (unsigned long)elf64ph->p_memsz + elfoffset;
171 135
172#if defined(PROG_START) 136 is_64bit = 1;
173 /*
174 * Maintain a "magic" minimum address. This keeps some older
175 * firmware platforms running.
176 */
177
178 if (claim_base < PROG_START)
179 claim_base = PROG_START;
180#endif
181
182 return 1; 137 return 1;
183} 138}
184 139
@@ -212,47 +167,9 @@ static int is_elf32(void *hdr)
212 return 1; 167 return 1;
213} 168}
214 169
215void export_cmdline(void* chosen_handle) 170static void prep_kernel(unsigned long *a1, unsigned long *a2)
216{
217 int len;
218 char cmdline[2] = { 0, 0 };
219
220 if (builtin_cmdline[0] == 0)
221 return;
222
223 len = getprop(chosen_handle, "bootargs", cmdline, sizeof(cmdline));
224 if (len > 0 && cmdline[0] != 0)
225 return;
226
227 setprop(chosen_handle, "bootargs", builtin_cmdline,
228 strlen(builtin_cmdline) + 1);
229}
230
231
232void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
233{ 171{
234 int len; 172 int len;
235 kernel_entry_t kernel_entry;
236
237 memset(__bss_start, 0, _end - __bss_start);
238
239 prom = (int (*)(void *)) promptr;
240 chosen_handle = finddevice("/chosen");
241 if (chosen_handle == (void *) -1)
242 exit();
243 if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
244 exit();
245
246 printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, sp);
247
248 /*
249 * The first available claim_base must be above the end of the
250 * the loaded kernel wrapper file (_start to _end includes the
251 * initrd image if it is present) and rounded up to a nice
252 * 1 MB boundary for good measure.
253 */
254
255 claim_base = _ALIGN_UP((unsigned long)_end, ONE_MB);
256 173
257 vmlinuz.addr = (unsigned long)_vmlinux_start; 174 vmlinuz.addr = (unsigned long)_vmlinux_start;
258 vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); 175 vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
@@ -263,43 +180,51 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
263 gunzip(elfheader, sizeof(elfheader), 180 gunzip(elfheader, sizeof(elfheader),
264 (unsigned char *)vmlinuz.addr, &len); 181 (unsigned char *)vmlinuz.addr, &len);
265 } else 182 } else
266 memcpy(elfheader, (const void *)vmlinuz.addr, sizeof(elfheader)); 183 memcpy(elfheader, (const void *)vmlinuz.addr,
184 sizeof(elfheader));
267 185
268 if (!is_elf64(elfheader) && !is_elf32(elfheader)) { 186 if (!is_elf64(elfheader) && !is_elf32(elfheader)) {
269 printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); 187 printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
270 exit(); 188 exit();
271 } 189 }
190 if (platform_ops.image_hdr)
191 platform_ops.image_hdr(elfheader);
272 192
273 /* We need to claim the memsize plus the file offset since gzip 193 /* We need to alloc the memsize plus the file offset since gzip
274 * will expand the header (file offset), then the kernel, then 194 * will expand the header (file offset), then the kernel, then
275 * possible rubbish we don't care about. But the kernel bss must 195 * possible rubbish we don't care about. But the kernel bss must
276 * be claimed (it will be zero'd by the kernel itself) 196 * be claimed (it will be zero'd by the kernel itself)
277 */ 197 */
278 printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize); 198 printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize);
279 vmlinux.addr = try_claim(vmlinux.memsize); 199 vmlinux.addr = (unsigned long)malloc(vmlinux.memsize);
280 if (vmlinux.addr == 0) { 200 if (vmlinux.addr == 0) {
281 printf("Can't allocate memory for kernel image !\n\r"); 201 printf("Can't allocate memory for kernel image !\n\r");
282 exit(); 202 exit();
283 } 203 }
284 204
285 /* 205 /*
286 * Now we try to claim memory for the initrd (and copy it there) 206 * Now we try to alloc memory for the initrd (and copy it there)
287 */ 207 */
288 initrd.size = (unsigned long)(_initrd_end - _initrd_start); 208 initrd.size = (unsigned long)(_initrd_end - _initrd_start);
289 initrd.memsize = initrd.size; 209 initrd.memsize = initrd.size;
290 if ( initrd.size > 0 ) { 210 if ( initrd.size > 0 ) {
291 printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size); 211 printf("Allocating 0x%lx bytes for initrd ...\n\r",
292 initrd.addr = try_claim(initrd.size); 212 initrd.size);
213 initrd.addr = (unsigned long)malloc((u32)initrd.size);
293 if (initrd.addr == 0) { 214 if (initrd.addr == 0) {
294 printf("Can't allocate memory for initial ramdisk !\n\r"); 215 printf("Can't allocate memory for initial "
216 "ramdisk !\n\r");
295 exit(); 217 exit();
296 } 218 }
297 a1 = initrd.addr; 219 *a1 = initrd.addr;
298 a2 = initrd.size; 220 *a2 = initrd.size;
299 printf("initial ramdisk moving 0x%lx <- 0x%lx (0x%lx bytes)\n\r", 221 printf("initial ramdisk moving 0x%lx <- 0x%lx "
300 initrd.addr, (unsigned long)_initrd_start, initrd.size); 222 "(0x%lx bytes)\n\r", initrd.addr,
301 memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size); 223 (unsigned long)_initrd_start, initrd.size);
302 printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd.addr)); 224 memmove((void *)initrd.addr, (void *)_initrd_start,
225 initrd.size);
226 printf("initrd head: 0x%lx\n\r",
227 *((unsigned long *)initrd.addr));
303 } 228 }
304 229
305 /* Eventually gunzip the kernel */ 230 /* Eventually gunzip the kernel */
@@ -311,11 +236,10 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
311 (unsigned char *)vmlinuz.addr, &len); 236 (unsigned char *)vmlinuz.addr, &len);
312 printf("done 0x%lx bytes\n\r", len); 237 printf("done 0x%lx bytes\n\r", len);
313 } else { 238 } else {
314 memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size); 239 memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,
240 vmlinuz.size);
315 } 241 }
316 242
317 export_cmdline(chosen_handle);
318
319 /* Skip over the ELF header */ 243 /* Skip over the ELF header */
320#ifdef DEBUG 244#ifdef DEBUG
321 printf("... skipping 0x%lx bytes of ELF header\n\r", 245 printf("... skipping 0x%lx bytes of ELF header\n\r",
@@ -324,23 +248,107 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
324 vmlinux.addr += elfoffset; 248 vmlinux.addr += elfoffset;
325 249
326 flush_cache((void *)vmlinux.addr, vmlinux.size); 250 flush_cache((void *)vmlinux.addr, vmlinux.size);
251}
327 252
328 kernel_entry = (kernel_entry_t)vmlinux.addr; 253void __attribute__ ((weak)) ft_init(void *dt_blob)
329#ifdef DEBUG 254{
330 printf( "kernel:\n\r" 255}
331 " entry addr = 0x%lx\n\r"
332 " a1 = 0x%lx,\n\r"
333 " a2 = 0x%lx,\n\r"
334 " prom = 0x%lx,\n\r"
335 " bi_recs = 0x%lx,\n\r",
336 (unsigned long)kernel_entry, a1, a2,
337 (unsigned long)prom, NULL);
338#endif
339 256
340 kernel_entry(a1, a2, prom, NULL); 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 */
261static char builtin_cmdline[COMMAND_LINE_SIZE]
262 __attribute__((__section__("__builtin_cmdline")));
341 263
342 printf("Error: Linux kernel returned to zImage bootloader!\n\r"); 264static void get_cmdline(char *buf, int size)
265{
266 void *devp;
267 int len = strlen(builtin_cmdline);
343 268
344 exit(); 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
280static void set_cmdline(char *buf)
281{
282 void *devp;
283
284 if ((devp = finddevice("/chosen")))
285 setprop(devp, "bootargs", buf, strlen(buf) + 1);
345} 286}
346 287
288/* Section where ft can be tacked on after zImage is built */
289union blobspace {
290 struct boot_param_header hdr;
291 char space[8*1024];
292} dt_blob __attribute__((__section__("__builtin_ft")));
293
294struct platform_ops platform_ops;
295struct dt_ops dt_ops;
296struct console_ops console_ops;
297
298void 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}