diff options
author | Paul Mackerras <paulus@samba.org> | 2006-01-13 23:04:06 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-01-13 23:04:06 -0500 |
commit | 66a45dd3620ee5f913ba1af3d2dca8b9bdfa2b96 (patch) | |
tree | f98b25a1004d6e6bc84244186db61e76d5b2fe1c /arch/powerpc/boot/prom.c | |
parent | 36874579dbf4cafa31486d4207c6807efbbf1378 (diff) |
powerpc: Make COFF zImages for old 32-bit powermacs
This adds code to build zImage.coff and/or zImage.initrd.coff when
CONFIG_PPC32 and CONFIG_PPC_PMAC are defined. It also restructures
the OF client code and adds some workarounds for OF quirks on the
older machines.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/boot/prom.c')
-rw-r--r-- | arch/powerpc/boot/prom.c | 538 |
1 files changed, 102 insertions, 436 deletions
diff --git a/arch/powerpc/boot/prom.c b/arch/powerpc/boot/prom.c index 4bea2f4dcb06..fa0057736f6b 100644 --- a/arch/powerpc/boot/prom.c +++ b/arch/powerpc/boot/prom.c | |||
@@ -13,487 +13,153 @@ | |||
13 | #include "prom.h" | 13 | #include "prom.h" |
14 | 14 | ||
15 | int (*prom)(void *); | 15 | int (*prom)(void *); |
16 | phandle chosen_handle; | ||
17 | ihandle stdout; | ||
16 | 18 | ||
17 | void *chosen_handle; | 19 | int call_prom(const char *service, int nargs, int nret, ...) |
18 | |||
19 | void *stdin; | ||
20 | void *stdout; | ||
21 | void *stderr; | ||
22 | |||
23 | |||
24 | int | ||
25 | write(void *handle, void *ptr, int nb) | ||
26 | { | ||
27 | struct prom_args { | ||
28 | char *service; | ||
29 | int nargs; | ||
30 | int nret; | ||
31 | void *ihandle; | ||
32 | void *addr; | ||
33 | int len; | ||
34 | int actual; | ||
35 | } args; | ||
36 | |||
37 | args.service = "write"; | ||
38 | args.nargs = 3; | ||
39 | args.nret = 1; | ||
40 | args.ihandle = handle; | ||
41 | args.addr = ptr; | ||
42 | args.len = nb; | ||
43 | args.actual = -1; | ||
44 | (*prom)(&args); | ||
45 | return args.actual; | ||
46 | } | ||
47 | |||
48 | int | ||
49 | read(void *handle, void *ptr, int nb) | ||
50 | { | 20 | { |
21 | int i; | ||
51 | struct prom_args { | 22 | struct prom_args { |
52 | char *service; | 23 | const char *service; |
53 | int nargs; | 24 | int nargs; |
54 | int nret; | 25 | int nret; |
55 | void *ihandle; | 26 | unsigned int args[12]; |
56 | void *addr; | ||
57 | int len; | ||
58 | int actual; | ||
59 | } args; | ||
60 | |||
61 | args.service = "read"; | ||
62 | args.nargs = 3; | ||
63 | args.nret = 1; | ||
64 | args.ihandle = handle; | ||
65 | args.addr = ptr; | ||
66 | args.len = nb; | ||
67 | args.actual = -1; | ||
68 | (*prom)(&args); | ||
69 | return args.actual; | ||
70 | } | ||
71 | |||
72 | void | ||
73 | exit() | ||
74 | { | ||
75 | struct prom_args { | ||
76 | char *service; | ||
77 | } args; | ||
78 | |||
79 | for (;;) { | ||
80 | args.service = "exit"; | ||
81 | (*prom)(&args); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | void | ||
86 | pause(void) | ||
87 | { | ||
88 | struct prom_args { | ||
89 | char *service; | ||
90 | } args; | 27 | } args; |
28 | va_list list; | ||
91 | 29 | ||
92 | args.service = "enter"; | 30 | args.service = service; |
93 | (*prom)(&args); | 31 | args.nargs = nargs; |
94 | } | 32 | args.nret = nret; |
95 | 33 | ||
96 | void * | 34 | va_start(list, nret); |
97 | finddevice(const char *name) | 35 | for (i = 0; i < nargs; i++) |
98 | { | 36 | args.args[i] = va_arg(list, unsigned int); |
99 | struct prom_args { | 37 | va_end(list); |
100 | char *service; | ||
101 | int nargs; | ||
102 | int nret; | ||
103 | const char *devspec; | ||
104 | void *phandle; | ||
105 | } args; | ||
106 | 38 | ||
107 | args.service = "finddevice"; | 39 | for (i = 0; i < nret; i++) |
108 | args.nargs = 1; | 40 | args.args[nargs+i] = 0; |
109 | args.nret = 1; | ||
110 | args.devspec = name; | ||
111 | args.phandle = (void *) -1; | ||
112 | (*prom)(&args); | ||
113 | return args.phandle; | ||
114 | } | ||
115 | 41 | ||
116 | void * | 42 | if (prom(&args) < 0) |
117 | claim(unsigned long virt, unsigned long size, unsigned long align) | 43 | return -1; |
118 | { | ||
119 | struct prom_args { | ||
120 | char *service; | ||
121 | int nargs; | ||
122 | int nret; | ||
123 | unsigned int virt; | ||
124 | unsigned int size; | ||
125 | unsigned int align; | ||
126 | void *ret; | ||
127 | } args; | ||
128 | 44 | ||
129 | args.service = "claim"; | 45 | return (nret > 0)? args.args[nargs]: 0; |
130 | args.nargs = 3; | ||
131 | args.nret = 1; | ||
132 | args.virt = virt; | ||
133 | args.size = size; | ||
134 | args.align = align; | ||
135 | (*prom)(&args); | ||
136 | return args.ret; | ||
137 | } | 46 | } |
138 | 47 | ||
139 | int | 48 | int call_prom_ret(const char *service, int nargs, int nret, |
140 | getprop(void *phandle, const char *name, void *buf, int buflen) | 49 | unsigned int *rets, ...) |
141 | { | 50 | { |
51 | int i; | ||
142 | struct prom_args { | 52 | struct prom_args { |
143 | char *service; | 53 | const char *service; |
144 | int nargs; | 54 | int nargs; |
145 | int nret; | 55 | int nret; |
146 | void *phandle; | 56 | unsigned int args[12]; |
147 | const char *name; | ||
148 | void *buf; | ||
149 | int buflen; | ||
150 | int size; | ||
151 | } args; | 57 | } args; |
58 | va_list list; | ||
152 | 59 | ||
153 | args.service = "getprop"; | 60 | args.service = service; |
154 | args.nargs = 4; | 61 | args.nargs = nargs; |
155 | args.nret = 1; | 62 | args.nret = nret; |
156 | args.phandle = phandle; | ||
157 | args.name = name; | ||
158 | args.buf = buf; | ||
159 | args.buflen = buflen; | ||
160 | args.size = -1; | ||
161 | (*prom)(&args); | ||
162 | return args.size; | ||
163 | } | ||
164 | 63 | ||
165 | int | 64 | va_start(list, rets); |
166 | putc(int c, void *f) | 65 | for (i = 0; i < nargs; i++) |
167 | { | 66 | args.args[i] = va_arg(list, unsigned int); |
168 | char ch = c; | 67 | va_end(list); |
169 | 68 | ||
170 | if (c == '\n') | 69 | for (i = 0; i < nret; i++) |
171 | putc('\r', f); | 70 | args.args[nargs+i] = 0; |
172 | return write(f, &ch, 1) == 1? c: -1; | ||
173 | } | ||
174 | 71 | ||
175 | int | 72 | if (prom(&args) < 0) |
176 | putchar(int c) | 73 | return -1; |
177 | { | ||
178 | return putc(c, stdout); | ||
179 | } | ||
180 | 74 | ||
181 | int | 75 | if (rets != (void *) 0) |
182 | fputs(char *str, void *f) | 76 | for (i = 1; i < nret; ++i) |
183 | { | 77 | rets[i-1] = args.args[nargs+i]; |
184 | int n = strlen(str); | ||
185 | 78 | ||
186 | return write(f, str, n) == n? 0: -1; | 79 | return (nret > 0)? args.args[nargs]: 0; |
187 | } | 80 | } |
188 | 81 | ||
189 | size_t strnlen(const char * s, size_t count) | 82 | int write(void *handle, void *ptr, int nb) |
190 | { | 83 | { |
191 | const char *sc; | 84 | return call_prom("write", 3, 1, handle, ptr, nb); |
192 | |||
193 | for (sc = s; count-- && *sc != '\0'; ++sc) | ||
194 | /* nothing */; | ||
195 | return sc - s; | ||
196 | } | 85 | } |
197 | 86 | ||
198 | extern unsigned int __div64_32(unsigned long long *dividend, | 87 | /* |
199 | unsigned int divisor); | 88 | * Older OF's require that when claiming a specific range of addresses, |
200 | 89 | * we claim the physical space in the /memory node and the virtual | |
201 | /* The unnecessary pointer compare is there | 90 | * space in the chosen mmu node, and then do a map operation to |
202 | * to check for type safety (n must be 64bit) | 91 | * map virtual to physical. |
203 | */ | 92 | */ |
204 | # define do_div(n,base) ({ \ | 93 | static int need_map = -1; |
205 | unsigned int __base = (base); \ | 94 | static ihandle chosen_mmu; |
206 | unsigned int __rem; \ | 95 | static phandle memory; |
207 | (void)(((typeof((n)) *)0) == ((unsigned long long *)0)); \ | ||
208 | if (((n) >> 32) == 0) { \ | ||
209 | __rem = (unsigned int)(n) % __base; \ | ||
210 | (n) = (unsigned int)(n) / __base; \ | ||
211 | } else \ | ||
212 | __rem = __div64_32(&(n), __base); \ | ||
213 | __rem; \ | ||
214 | }) | ||
215 | 96 | ||
216 | static int skip_atoi(const char **s) | 97 | /* returns true if s2 is a prefix of s1 */ |
98 | static int string_match(const char *s1, const char *s2) | ||
217 | { | 99 | { |
218 | int i, c; | 100 | for (; *s2; ++s2) |
219 | 101 | if (*s1++ != *s2) | |
220 | for (i = 0; '0' <= (c = **s) && c <= '9'; ++*s) | 102 | return 0; |
221 | i = i*10 + c - '0'; | 103 | return 1; |
222 | return i; | ||
223 | } | 104 | } |
224 | 105 | ||
225 | #define ZEROPAD 1 /* pad with zero */ | 106 | static int check_of_version(void) |
226 | #define SIGN 2 /* unsigned/signed long */ | ||
227 | #define PLUS 4 /* show plus */ | ||
228 | #define SPACE 8 /* space if plus */ | ||
229 | #define LEFT 16 /* left justified */ | ||
230 | #define SPECIAL 32 /* 0x */ | ||
231 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ | ||
232 | |||
233 | static char * number(char * str, unsigned long long num, int base, int size, int precision, int type) | ||
234 | { | 107 | { |
235 | char c,sign,tmp[66]; | 108 | phandle oprom, chosen; |
236 | const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; | 109 | char version[64]; |
237 | int i; | ||
238 | 110 | ||
239 | if (type & LARGE) | 111 | oprom = finddevice("/openprom"); |
240 | digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 112 | if (oprom == (phandle) -1) |
241 | if (type & LEFT) | ||
242 | type &= ~ZEROPAD; | ||
243 | if (base < 2 || base > 36) | ||
244 | return 0; | 113 | return 0; |
245 | c = (type & ZEROPAD) ? '0' : ' '; | 114 | if (getprop(oprom, "model", version, sizeof(version)) <= 0) |
246 | sign = 0; | 115 | return 0; |
247 | if (type & SIGN) { | 116 | version[sizeof(version)-1] = 0; |
248 | if ((signed long long)num < 0) { | 117 | printf("OF version = '%s'\r\n", version); |
249 | sign = '-'; | 118 | if (!string_match(version, "Open Firmware, 1.") |
250 | num = - (signed long long)num; | 119 | && !string_match(version, "FirmWorks,3.")) |
251 | size--; | 120 | return 0; |
252 | } else if (type & PLUS) { | 121 | chosen = finddevice("/chosen"); |
253 | sign = '+'; | 122 | if (chosen == (phandle) -1) { |
254 | size--; | 123 | chosen = finddevice("/chosen@0"); |
255 | } else if (type & SPACE) { | 124 | if (chosen == (phandle) -1) { |
256 | sign = ' '; | 125 | printf("no chosen\n"); |
257 | size--; | 126 | return 0; |
258 | } | 127 | } |
259 | } | 128 | } |
260 | if (type & SPECIAL) { | 129 | if (getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { |
261 | if (base == 16) | 130 | printf("no mmu\n"); |
262 | size -= 2; | 131 | return 0; |
263 | else if (base == 8) | ||
264 | size--; | ||
265 | } | ||
266 | i = 0; | ||
267 | if (num == 0) | ||
268 | tmp[i++]='0'; | ||
269 | else while (num != 0) { | ||
270 | tmp[i++] = digits[do_div(num, base)]; | ||
271 | } | 132 | } |
272 | if (i > precision) | 133 | memory = (ihandle) call_prom("open", 1, 1, "/memory"); |
273 | precision = i; | 134 | if (memory == (ihandle) -1) { |
274 | size -= precision; | 135 | memory = (ihandle) call_prom("open", 1, 1, "/memory@0"); |
275 | if (!(type&(ZEROPAD+LEFT))) | 136 | if (memory == (ihandle) -1) { |
276 | while(size-->0) | 137 | printf("no memory node\n"); |
277 | *str++ = ' '; | 138 | return 0; |
278 | if (sign) | ||
279 | *str++ = sign; | ||
280 | if (type & SPECIAL) { | ||
281 | if (base==8) | ||
282 | *str++ = '0'; | ||
283 | else if (base==16) { | ||
284 | *str++ = '0'; | ||
285 | *str++ = digits[33]; | ||
286 | } | 139 | } |
287 | } | 140 | } |
288 | if (!(type & LEFT)) | 141 | printf("old OF detected\r\n"); |
289 | while (size-- > 0) | 142 | return 1; |
290 | *str++ = c; | ||
291 | while (i < precision--) | ||
292 | *str++ = '0'; | ||
293 | while (i-- > 0) | ||
294 | *str++ = tmp[i]; | ||
295 | while (size-- > 0) | ||
296 | *str++ = ' '; | ||
297 | return str; | ||
298 | } | 143 | } |
299 | 144 | ||
300 | int vsprintf(char *buf, const char *fmt, va_list args) | 145 | void *claim(unsigned long virt, unsigned long size, unsigned long align) |
301 | { | 146 | { |
302 | int len; | 147 | int ret; |
303 | unsigned long long num; | 148 | unsigned int result; |
304 | int i, base; | ||
305 | char * str; | ||
306 | const char *s; | ||
307 | |||
308 | int flags; /* flags to number() */ | ||
309 | |||
310 | int field_width; /* width of output field */ | ||
311 | int precision; /* min. # of digits for integers; max | ||
312 | number of chars for from string */ | ||
313 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ | ||
314 | /* 'z' support added 23/7/1999 S.H. */ | ||
315 | /* 'z' changed to 'Z' --davidm 1/25/99 */ | ||
316 | 149 | ||
150 | if (need_map < 0) | ||
151 | need_map = check_of_version(); | ||
152 | if (align || !need_map) | ||
153 | return (void *) call_prom("claim", 3, 1, virt, size, align); | ||
317 | 154 | ||
318 | for (str=buf ; *fmt ; ++fmt) { | 155 | ret = call_prom_ret("call-method", 5, 2, &result, "claim", memory, |
319 | if (*fmt != '%') { | 156 | align, size, virt); |
320 | *str++ = *fmt; | 157 | if (ret != 0 || result == -1) |
321 | continue; | 158 | return (void *) -1; |
322 | } | 159 | ret = call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, |
323 | 160 | align, size, virt); | |
324 | /* process flags */ | 161 | /* 0x12 == coherent + read/write */ |
325 | flags = 0; | 162 | ret = call_prom("call-method", 6, 1, "map", chosen_mmu, |
326 | repeat: | 163 | 0x12, size, virt, virt); |
327 | ++fmt; /* this also skips first '%' */ | 164 | return (void *) virt; |
328 | switch (*fmt) { | ||
329 | case '-': flags |= LEFT; goto repeat; | ||
330 | case '+': flags |= PLUS; goto repeat; | ||
331 | case ' ': flags |= SPACE; goto repeat; | ||
332 | case '#': flags |= SPECIAL; goto repeat; | ||
333 | case '0': flags |= ZEROPAD; goto repeat; | ||
334 | } | ||
335 | |||
336 | /* get field width */ | ||
337 | field_width = -1; | ||
338 | if ('0' <= *fmt && *fmt <= '9') | ||
339 | field_width = skip_atoi(&fmt); | ||
340 | else if (*fmt == '*') { | ||
341 | ++fmt; | ||
342 | /* it's the next argument */ | ||
343 | field_width = va_arg(args, int); | ||
344 | if (field_width < 0) { | ||
345 | field_width = -field_width; | ||
346 | flags |= LEFT; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | /* get the precision */ | ||
351 | precision = -1; | ||
352 | if (*fmt == '.') { | ||
353 | ++fmt; | ||
354 | if ('0' <= *fmt && *fmt <= '9') | ||
355 | precision = skip_atoi(&fmt); | ||
356 | else if (*fmt == '*') { | ||
357 | ++fmt; | ||
358 | /* it's the next argument */ | ||
359 | precision = va_arg(args, int); | ||
360 | } | ||
361 | if (precision < 0) | ||
362 | precision = 0; | ||
363 | } | ||
364 | |||
365 | /* get the conversion qualifier */ | ||
366 | qualifier = -1; | ||
367 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { | ||
368 | qualifier = *fmt; | ||
369 | ++fmt; | ||
370 | } | ||
371 | |||
372 | /* default base */ | ||
373 | base = 10; | ||
374 | |||
375 | switch (*fmt) { | ||
376 | case 'c': | ||
377 | if (!(flags & LEFT)) | ||
378 | while (--field_width > 0) | ||
379 | *str++ = ' '; | ||
380 | *str++ = (unsigned char) va_arg(args, int); | ||
381 | while (--field_width > 0) | ||
382 | *str++ = ' '; | ||
383 | continue; | ||
384 | |||
385 | case 's': | ||
386 | s = va_arg(args, char *); | ||
387 | if (!s) | ||
388 | s = "<NULL>"; | ||
389 | |||
390 | len = strnlen(s, precision); | ||
391 | |||
392 | if (!(flags & LEFT)) | ||
393 | while (len < field_width--) | ||
394 | *str++ = ' '; | ||
395 | for (i = 0; i < len; ++i) | ||
396 | *str++ = *s++; | ||
397 | while (len < field_width--) | ||
398 | *str++ = ' '; | ||
399 | continue; | ||
400 | |||
401 | case 'p': | ||
402 | if (field_width == -1) { | ||
403 | field_width = 2*sizeof(void *); | ||
404 | flags |= ZEROPAD; | ||
405 | } | ||
406 | str = number(str, | ||
407 | (unsigned long) va_arg(args, void *), 16, | ||
408 | field_width, precision, flags); | ||
409 | continue; | ||
410 | |||
411 | |||
412 | case 'n': | ||
413 | if (qualifier == 'l') { | ||
414 | long * ip = va_arg(args, long *); | ||
415 | *ip = (str - buf); | ||
416 | } else if (qualifier == 'Z') { | ||
417 | size_t * ip = va_arg(args, size_t *); | ||
418 | *ip = (str - buf); | ||
419 | } else { | ||
420 | int * ip = va_arg(args, int *); | ||
421 | *ip = (str - buf); | ||
422 | } | ||
423 | continue; | ||
424 | |||
425 | case '%': | ||
426 | *str++ = '%'; | ||
427 | continue; | ||
428 | |||
429 | /* integer number formats - set up the flags and "break" */ | ||
430 | case 'o': | ||
431 | base = 8; | ||
432 | break; | ||
433 | |||
434 | case 'X': | ||
435 | flags |= LARGE; | ||
436 | case 'x': | ||
437 | base = 16; | ||
438 | break; | ||
439 | |||
440 | case 'd': | ||
441 | case 'i': | ||
442 | flags |= SIGN; | ||
443 | case 'u': | ||
444 | break; | ||
445 | |||
446 | default: | ||
447 | *str++ = '%'; | ||
448 | if (*fmt) | ||
449 | *str++ = *fmt; | ||
450 | else | ||
451 | --fmt; | ||
452 | continue; | ||
453 | } | ||
454 | if (qualifier == 'l') { | ||
455 | num = va_arg(args, unsigned long); | ||
456 | if (flags & SIGN) | ||
457 | num = (signed long) num; | ||
458 | } else if (qualifier == 'Z') { | ||
459 | num = va_arg(args, size_t); | ||
460 | } else if (qualifier == 'h') { | ||
461 | num = (unsigned short) va_arg(args, int); | ||
462 | if (flags & SIGN) | ||
463 | num = (signed short) num; | ||
464 | } else { | ||
465 | num = va_arg(args, unsigned int); | ||
466 | if (flags & SIGN) | ||
467 | num = (signed int) num; | ||
468 | } | ||
469 | str = number(str, num, base, field_width, precision, flags); | ||
470 | } | ||
471 | *str = '\0'; | ||
472 | return str-buf; | ||
473 | } | ||
474 | |||
475 | int sprintf(char * buf, const char *fmt, ...) | ||
476 | { | ||
477 | va_list args; | ||
478 | int i; | ||
479 | |||
480 | va_start(args, fmt); | ||
481 | i=vsprintf(buf,fmt,args); | ||
482 | va_end(args); | ||
483 | return i; | ||
484 | } | ||
485 | |||
486 | static char sprint_buf[1024]; | ||
487 | |||
488 | int | ||
489 | printf(const char *fmt, ...) | ||
490 | { | ||
491 | va_list args; | ||
492 | int n; | ||
493 | |||
494 | va_start(args, fmt); | ||
495 | n = vsprintf(sprint_buf, fmt, args); | ||
496 | va_end(args); | ||
497 | write(stdout, sprint_buf, n); | ||
498 | return n; | ||
499 | } | 165 | } |