diff options
Diffstat (limited to 'arch/powerpc/boot/main.c')
-rw-r--r-- | arch/powerpc/boot/main.c | 250 |
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 | ||
20 | extern void flush_cache(void *, unsigned long); | 21 | extern 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 | |||
28 | extern char _start[]; | 23 | extern char _start[]; |
29 | extern char __bss_start[]; | 24 | extern char __bss_start[]; |
30 | extern char _end[]; | 25 | extern char _end[]; |
@@ -33,14 +28,6 @@ extern char _vmlinux_end[]; | |||
33 | extern char _initrd_start[]; | 28 | extern char _initrd_start[]; |
34 | extern char _initrd_end[]; | 29 | extern 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 | */ | ||
40 | static char builtin_cmdline[512] | ||
41 | __attribute__((section("__builtin_cmdline"))); | ||
42 | |||
43 | |||
44 | struct addr_range { | 31 | struct 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; | |||
51 | static struct addr_range initrd; | 38 | static struct addr_range initrd; |
52 | 39 | ||
53 | static unsigned long elfoffset; | 40 | static unsigned long elfoffset; |
41 | static int is_64bit; | ||
54 | 42 | ||
55 | static char scratch[46912]; /* scratch space for gunzip, from zlib_inflate_workspacesize() */ | 43 | /* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ |
44 | static char scratch[46912]; | ||
56 | static char elfheader[256]; | 45 | static char elfheader[256]; |
57 | 46 | ||
58 | 47 | typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); | |
59 | typedef void (*kernel_entry_t)( unsigned long, | ||
60 | unsigned long, | ||
61 | void *, | ||
62 | void *); | ||
63 | |||
64 | 48 | ||
65 | #undef DEBUG | 49 | #undef DEBUG |
66 | 50 | ||
67 | static 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 | ||
126 | static 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 | |||
144 | static int is_elf64(void *hdr) | 108 | static 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 | ||
215 | void export_cmdline(void* chosen_handle) | 170 | static 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 | |||
232 | void 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; | 253 | void __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 | */ | ||
261 | static char builtin_cmdline[COMMAND_LINE_SIZE] | ||
262 | __attribute__((__section__("__builtin_cmdline"))); | ||
341 | 263 | ||
342 | printf("Error: Linux kernel returned to zImage bootloader!\n\r"); | 264 | static 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 | |||
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); | ||
345 | } | 286 | } |
346 | 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 | } | ||