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