ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc64 / boot / main.c
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 #include <asm/bootinfo.h>
18
19 extern void *finddevice(const char *);
20 extern int getprop(void *, const char *, void *, int);
21 extern void printk(char *fmt, ...);
22 extern void printf(const char *fmt, ...);
23 extern int sprintf(char *buf, const char *fmt, ...);
24 void gunzip(void *, int, unsigned char *, int *);
25 void *claim(unsigned int, unsigned int, unsigned int);
26 void flush_cache(void *, unsigned long);
27 void pause(void);
28 extern void exit(void);
29
30 unsigned long strlen(const char *s);
31 void *memmove(void *dest, const void *src, unsigned long n);
32 void *memcpy(void *dest, const void *src, unsigned long n);
33
34 static struct bi_record *make_bi_recs(unsigned long);
35
36 #define RAM_START       0x00000000
37 #define RAM_END         (64<<20)
38
39 /* Value picked to match that used by yaboot */
40 #define PROG_START      0x01400000
41
42 char *avail_ram;
43 char *begin_avail, *end_avail;
44 char *avail_high;
45 unsigned int heap_use;
46 unsigned int heap_max;
47
48 extern char _start[];
49 extern char _vmlinux_start[];
50 extern char _vmlinux_end[];
51 extern char _sysmap_start[];
52 extern char _sysmap_end[];
53 extern char _initrd_start[];
54 extern char _initrd_end[];
55 extern unsigned long vmlinux_filesize;
56 extern unsigned long vmlinux_memsize;
57
58 struct addr_range {
59         unsigned long addr;
60         unsigned long size;
61         unsigned long memsize;
62 };
63 struct addr_range vmlinux = {0, 0, 0};
64 struct addr_range vmlinuz = {0, 0, 0};
65 struct addr_range sysmap  = {0, 0, 0};
66 struct addr_range initrd  = {0, 0, 0};
67
68 static char scratch[128<<10];   /* 128kB of scratch space for gunzip */
69
70 typedef void (*kernel_entry_t)( unsigned long,
71                                 unsigned long,
72                                 void *,
73                                 struct bi_record *);
74
75
76 int (*prom)(void *);
77
78 void *chosen_handle;
79 void *stdin;
80 void *stdout;
81 void *stderr;
82
83
84 void
85 start(unsigned long a1, unsigned long a2, void *promptr)
86 {
87         unsigned long i, claim_addr, claim_size;
88         struct bi_record *bi_recs;
89         kernel_entry_t kernel_entry;
90         Elf64_Ehdr *elf64;
91         Elf64_Phdr *elf64ph;
92
93         prom = (int (*)(void *)) promptr;
94         chosen_handle = finddevice("/chosen");
95         if (chosen_handle == (void *) -1)
96                 exit();
97         if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
98                 exit();
99         stderr = stdout;
100         if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4)
101                 exit();
102
103         printf("zImage starting: loaded at 0x%x\n\r", (unsigned)_start);
104
105 #if 0
106         sysmap.size = (unsigned long)(_sysmap_end - _sysmap_start);
107         sysmap.memsize = sysmap.size;
108         if ( sysmap.size > 0 ) {
109                 sysmap.addr = (RAM_END - sysmap.size) & ~0xFFF;
110                 claim(sysmap.addr, RAM_END - sysmap.addr, 0);
111                 printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r",
112                        sysmap.addr, (unsigned long)_sysmap_start, sysmap.size);
113                 memcpy((void *)sysmap.addr, (void *)_sysmap_start, sysmap.size);
114         }
115 #endif
116
117         initrd.size = (unsigned long)(_initrd_end - _initrd_start);
118         initrd.memsize = initrd.size;
119         if ( initrd.size > 0 ) {
120                 initrd.addr = (RAM_END - initrd.size) & ~0xFFF;
121                 a1 = a2 = 0;
122                 claim(initrd.addr, RAM_END - initrd.addr, 0);
123                 printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r",
124                        initrd.addr, (unsigned long)_initrd_start, initrd.size);
125                 memcpy((void *)initrd.addr, (void *)_initrd_start, initrd.size);
126         }
127
128         vmlinuz.addr = (unsigned long)_vmlinux_start;
129         vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
130         vmlinux.addr = (unsigned long)(void *)-1;
131         vmlinux.size = PAGE_ALIGN(vmlinux_filesize);
132         vmlinux.memsize = vmlinux_memsize;
133
134         claim_size = vmlinux.memsize /* PPPBBB: + fudge for bi_recs */;
135         for(claim_addr = PROG_START; 
136             claim_addr <= PROG_START * 8; 
137             claim_addr += 0x100000) {
138 #ifdef DEBUG
139                 printf("    trying: 0x%08lx\n\r", claim_addr);
140 #endif
141                 vmlinux.addr = (unsigned long)claim(claim_addr, claim_size, 0);
142                 if ((void *)vmlinux.addr != (void *)-1) break;
143         }
144         if ((void *)vmlinux.addr == (void *)-1) {
145                 printf("claim error, can't allocate kernel memory\n\r");
146                 exit();
147         }
148
149         /* PPPBBB: should kernel always be gziped? */
150         if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
151                 avail_ram = scratch;
152                 begin_avail = avail_high = avail_ram;
153                 end_avail = scratch + sizeof(scratch);
154                 printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...",
155                        vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size);
156                 gunzip((void *)vmlinux.addr, vmlinux.size,
157                         (unsigned char *)vmlinuz.addr, (int *)&vmlinuz.size);
158                 printf("done %lu bytes\n\r", vmlinuz.size);
159                 printf("%u bytes of heap consumed, max in use %u\n\r",
160                        (unsigned)(avail_high - begin_avail), heap_max);
161         } else {
162                 memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size);
163         }
164
165         /* Skip over the ELF header */
166         elf64 = (Elf64_Ehdr *)vmlinux.addr;
167         if ( elf64->e_ident[EI_MAG0]  != ELFMAG0        ||
168              elf64->e_ident[EI_MAG1]  != ELFMAG1        ||
169              elf64->e_ident[EI_MAG2]  != ELFMAG2        ||
170              elf64->e_ident[EI_MAG3]  != ELFMAG3        ||
171              elf64->e_ident[EI_CLASS] != ELFCLASS64     ||
172              elf64->e_ident[EI_DATA]  != ELFDATA2MSB    ||
173              elf64->e_type            != ET_EXEC        ||
174              elf64->e_machine         != EM_PPC64 )
175         {
176                 printf("Error: not a valid PPC64 ELF file!\n\r");
177                 exit();
178         }
179
180         elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
181                                 (unsigned long)elf64->e_phoff);
182         for(i=0; i < (unsigned int)elf64->e_phnum ;i++,elf64ph++) {
183                 if (elf64ph->p_type == PT_LOAD && elf64ph->p_offset != 0)
184                         break;
185         }
186 #ifdef DEBUG
187         printf("... skipping 0x%lx bytes of ELF header\n\r",
188                         (unsigned long)elf64ph->p_offset);
189 #endif
190         vmlinux.addr += (unsigned long)elf64ph->p_offset;
191         vmlinux.size -= (unsigned long)elf64ph->p_offset;
192
193         flush_cache((void *)vmlinux.addr, vmlinux.memsize);
194
195         bi_recs = make_bi_recs(vmlinux.addr + vmlinux.memsize);
196
197         kernel_entry = (kernel_entry_t)vmlinux.addr;
198 #ifdef DEBUG
199         printf( "kernel:\n\r"
200                 "        entry addr = 0x%lx\n\r"
201                 "        a1         = 0x%lx,\n\r"
202                 "        a2         = 0x%lx,\n\r"
203                 "        prom       = 0x%lx,\n\r"
204                 "        bi_recs    = 0x%lx,\n\r",
205                 (unsigned long)kernel_entry, a1, a2,
206                 (unsigned long)prom, (unsigned long)bi_recs);
207 #endif
208
209         kernel_entry( a1, a2, prom, bi_recs );
210
211         printf("Error: Linux kernel returned to zImage bootloader!\n\r");
212
213         exit();
214 }
215
216 static struct bi_record *
217 make_bi_recs(unsigned long addr)
218 {
219         struct bi_record *bi_recs;
220         struct bi_record *rec;
221
222         bi_recs = rec = bi_rec_init(addr);
223
224         rec = bi_rec_alloc(rec, 2);
225         rec->tag = BI_FIRST;
226         /* rec->data[0] = ...;  # Written below before return */
227         /* rec->data[1] = ...;  # Written below before return */
228
229         rec = bi_rec_alloc_bytes(rec, strlen("chrpboot")+1);
230         rec->tag = BI_BOOTLOADER_ID;
231         sprintf( (char *)rec->data, "chrpboot");
232
233         rec = bi_rec_alloc(rec, 2);
234         rec->tag = BI_MACHTYPE;
235         rec->data[0] = PLATFORM_PSERIES;
236         rec->data[1] = 1;
237
238         if ( initrd.size > 0 ) {
239                 rec = bi_rec_alloc(rec, 2);
240                 rec->tag = BI_INITRD;
241                 rec->data[0] = initrd.addr;
242                 rec->data[1] = initrd.size;
243         }
244
245         if ( sysmap.size > 0 ) {
246                 rec = bi_rec_alloc(rec, 2);
247                 rec->tag = BI_SYSMAP;
248                 rec->data[0] = (unsigned long)sysmap.addr;
249                 rec->data[1] = (unsigned long)sysmap.size;
250         }
251
252         rec = bi_rec_alloc(rec, 1);
253         rec->tag = BI_LAST;
254         rec->data[0] = (bi_rec_field)bi_recs;
255
256         /* Save the _end_ address of the bi_rec's in the first bi_rec
257          * data field for easy access by the kernel.
258          */
259         bi_recs->data[0] = (bi_rec_field)rec;
260         bi_recs->data[1] = (bi_rec_field)rec + rec->size - (bi_rec_field)bi_recs;
261
262         return bi_recs;
263 }
264
265 struct memchunk {
266         unsigned int size;
267         unsigned int pad;
268         struct memchunk *next;
269 };
270
271 static struct memchunk *freechunks;
272
273 void *zalloc(void *x, unsigned items, unsigned size)
274 {
275         void *p;
276         struct memchunk **mpp, *mp;
277
278         size *= items;
279         size = _ALIGN(size, sizeof(struct memchunk));
280         heap_use += size;
281         if (heap_use > heap_max)
282                 heap_max = heap_use;
283         for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) {
284                 if (mp->size == size) {
285                         *mpp = mp->next;
286                         return mp;
287                 }
288         }
289         p = avail_ram;
290         avail_ram += size;
291         if (avail_ram > avail_high)
292                 avail_high = avail_ram;
293         if (avail_ram > end_avail) {
294                 printf("oops... out of memory\n\r");
295                 pause();
296         }
297         return p;
298 }
299
300 void zfree(void *x, void *addr, unsigned nb)
301 {
302         struct memchunk *mp = addr;
303
304         nb = _ALIGN(nb, sizeof(struct memchunk));
305         heap_use -= nb;
306         if (avail_ram == addr + nb) {
307                 avail_ram = addr;
308                 return;
309         }
310         mp->size = nb;
311         mp->next = freechunks;
312         freechunks = mp;
313 }
314
315 #define HEAD_CRC        2
316 #define EXTRA_FIELD     4
317 #define ORIG_NAME       8
318 #define COMMENT         0x10
319 #define RESERVED        0xe0
320
321 #define DEFLATED        8
322
323 void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
324 {
325         z_stream s;
326         int r, i, flags;
327
328         /* skip header */
329         i = 10;
330         flags = src[3];
331         if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
332                 printf("bad gzipped data\n\r");
333                 exit();
334         }
335         if ((flags & EXTRA_FIELD) != 0)
336                 i = 12 + src[10] + (src[11] << 8);
337         if ((flags & ORIG_NAME) != 0)
338                 while (src[i++] != 0)
339                         ;
340         if ((flags & COMMENT) != 0)
341                 while (src[i++] != 0)
342                         ;
343         if ((flags & HEAD_CRC) != 0)
344                 i += 2;
345         if (i >= *lenp) {
346                 printf("gunzip: ran out of data in header\n\r");
347                 exit();
348         }
349
350         s.zalloc = zalloc;
351         s.zfree = zfree;
352         r = inflateInit2(&s, -MAX_WBITS);
353         if (r != Z_OK) {
354                 printf("inflateInit2 returned %d\n\r", r);
355                 exit();
356         }
357         s.next_in = src + i;
358         s.avail_in = *lenp - i;
359         s.next_out = dst;
360         s.avail_out = dstlen;
361         r = inflate(&s, Z_FINISH);
362         if (r != Z_OK && r != Z_STREAM_END) {
363                 printf("inflate returned %d msg: %s\n\r", r, s.msg);
364                 exit();
365         }
366         *lenp = s.next_out - (unsigned char *) dst;
367         inflateEnd(&s);
368 }
369