diff options
Diffstat (limited to 'arch/ppc/syslib/prom_init.c')
-rw-r--r-- | arch/ppc/syslib/prom_init.c | 1011 |
1 files changed, 0 insertions, 1011 deletions
diff --git a/arch/ppc/syslib/prom_init.c b/arch/ppc/syslib/prom_init.c deleted file mode 100644 index df14422ae1c6..000000000000 --- a/arch/ppc/syslib/prom_init.c +++ /dev/null | |||
@@ -1,1011 +0,0 @@ | |||
1 | /* | ||
2 | * Note that prom_init() and anything called from prom_init() | ||
3 | * may be running at an address that is different from the address | ||
4 | * that it was linked at. References to static data items are | ||
5 | * handled by compiling this file with -mrelocatable-lib. | ||
6 | */ | ||
7 | |||
8 | #include <linux/config.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/threads.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/ioport.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/bitops.h> | ||
18 | |||
19 | #include <asm/sections.h> | ||
20 | #include <asm/prom.h> | ||
21 | #include <asm/page.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/smp.h> | ||
25 | #include <asm/bootx.h> | ||
26 | #include <asm/system.h> | ||
27 | #include <asm/mmu.h> | ||
28 | #include <asm/pgtable.h> | ||
29 | #include <asm/bootinfo.h> | ||
30 | #include <asm/btext.h> | ||
31 | #include <asm/pci-bridge.h> | ||
32 | #include <asm/open_pic.h> | ||
33 | #include <asm/cacheflush.h> | ||
34 | |||
35 | #ifdef CONFIG_LOGO_LINUX_CLUT224 | ||
36 | #include <linux/linux_logo.h> | ||
37 | extern const struct linux_logo logo_linux_clut224; | ||
38 | #endif | ||
39 | |||
40 | /* | ||
41 | * Properties whose value is longer than this get excluded from our | ||
42 | * copy of the device tree. This way we don't waste space storing | ||
43 | * things like "driver,AAPL,MacOS,PowerPC" properties. But this value | ||
44 | * does need to be big enough to ensure that we don't lose things | ||
45 | * like the interrupt-map property on a PCI-PCI bridge. | ||
46 | */ | ||
47 | #define MAX_PROPERTY_LENGTH 4096 | ||
48 | |||
49 | #ifndef FB_MAX /* avoid pulling in all of the fb stuff */ | ||
50 | #define FB_MAX 8 | ||
51 | #endif | ||
52 | |||
53 | #define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) | ||
54 | |||
55 | typedef u32 prom_arg_t; | ||
56 | |||
57 | struct prom_args { | ||
58 | const char *service; | ||
59 | int nargs; | ||
60 | int nret; | ||
61 | prom_arg_t args[10]; | ||
62 | }; | ||
63 | |||
64 | struct pci_address { | ||
65 | unsigned a_hi; | ||
66 | unsigned a_mid; | ||
67 | unsigned a_lo; | ||
68 | }; | ||
69 | |||
70 | struct pci_reg_property { | ||
71 | struct pci_address addr; | ||
72 | unsigned size_hi; | ||
73 | unsigned size_lo; | ||
74 | }; | ||
75 | |||
76 | struct pci_range { | ||
77 | struct pci_address addr; | ||
78 | unsigned phys; | ||
79 | unsigned size_hi; | ||
80 | unsigned size_lo; | ||
81 | }; | ||
82 | |||
83 | struct isa_reg_property { | ||
84 | unsigned space; | ||
85 | unsigned address; | ||
86 | unsigned size; | ||
87 | }; | ||
88 | |||
89 | struct pci_intr_map { | ||
90 | struct pci_address addr; | ||
91 | unsigned dunno; | ||
92 | phandle int_ctrler; | ||
93 | unsigned intr; | ||
94 | }; | ||
95 | |||
96 | static void prom_exit(void); | ||
97 | static int call_prom(const char *service, int nargs, int nret, ...); | ||
98 | static int call_prom_ret(const char *service, int nargs, int nret, | ||
99 | prom_arg_t *rets, ...); | ||
100 | static void prom_print_hex(unsigned int v); | ||
101 | static int prom_set_color(ihandle ih, int i, int r, int g, int b); | ||
102 | static int prom_next_node(phandle *nodep); | ||
103 | static unsigned long check_display(unsigned long mem); | ||
104 | static void setup_disp_fake_bi(ihandle dp); | ||
105 | static unsigned long copy_device_tree(unsigned long mem_start, | ||
106 | unsigned long mem_end); | ||
107 | static unsigned long inspect_node(phandle node, struct device_node *dad, | ||
108 | unsigned long mem_start, unsigned long mem_end, | ||
109 | struct device_node ***allnextpp); | ||
110 | static void prom_hold_cpus(unsigned long mem); | ||
111 | static void prom_instantiate_rtas(void); | ||
112 | static void * early_get_property(unsigned long base, unsigned long node, | ||
113 | char *prop); | ||
114 | |||
115 | prom_entry prom __initdata; | ||
116 | ihandle prom_chosen __initdata; | ||
117 | ihandle prom_stdout __initdata; | ||
118 | |||
119 | static char *prom_display_paths[FB_MAX] __initdata; | ||
120 | static phandle prom_display_nodes[FB_MAX] __initdata; | ||
121 | static unsigned int prom_num_displays __initdata; | ||
122 | static ihandle prom_disp_node __initdata; | ||
123 | char *of_stdout_device __initdata; | ||
124 | |||
125 | unsigned int rtas_data; /* physical pointer */ | ||
126 | unsigned int rtas_entry; /* physical pointer */ | ||
127 | unsigned int rtas_size; | ||
128 | unsigned int old_rtas; | ||
129 | |||
130 | boot_infos_t *boot_infos; | ||
131 | char *bootpath; | ||
132 | char *bootdevice; | ||
133 | struct device_node *allnodes; | ||
134 | |||
135 | extern char *klimit; | ||
136 | |||
137 | static void __init | ||
138 | prom_exit(void) | ||
139 | { | ||
140 | struct prom_args args; | ||
141 | |||
142 | args.service = "exit"; | ||
143 | args.nargs = 0; | ||
144 | args.nret = 0; | ||
145 | prom(&args); | ||
146 | for (;;) /* should never get here */ | ||
147 | ; | ||
148 | } | ||
149 | |||
150 | static int __init | ||
151 | call_prom(const char *service, int nargs, int nret, ...) | ||
152 | { | ||
153 | va_list list; | ||
154 | int i; | ||
155 | struct prom_args prom_args; | ||
156 | |||
157 | prom_args.service = service; | ||
158 | prom_args.nargs = nargs; | ||
159 | prom_args.nret = nret; | ||
160 | va_start(list, nret); | ||
161 | for (i = 0; i < nargs; ++i) | ||
162 | prom_args.args[i] = va_arg(list, prom_arg_t); | ||
163 | va_end(list); | ||
164 | for (i = 0; i < nret; ++i) | ||
165 | prom_args.args[i + nargs] = 0; | ||
166 | prom(&prom_args); | ||
167 | return prom_args.args[nargs]; | ||
168 | } | ||
169 | |||
170 | static int __init | ||
171 | call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...) | ||
172 | { | ||
173 | va_list list; | ||
174 | int i; | ||
175 | struct prom_args prom_args; | ||
176 | |||
177 | prom_args.service = service; | ||
178 | prom_args.nargs = nargs; | ||
179 | prom_args.nret = nret; | ||
180 | va_start(list, rets); | ||
181 | for (i = 0; i < nargs; ++i) | ||
182 | prom_args.args[i] = va_arg(list, int); | ||
183 | va_end(list); | ||
184 | for (i = 0; i < nret; ++i) | ||
185 | prom_args.args[i + nargs] = 0; | ||
186 | prom(&prom_args); | ||
187 | for (i = 1; i < nret; ++i) | ||
188 | rets[i-1] = prom_args.args[nargs + i]; | ||
189 | return prom_args.args[nargs]; | ||
190 | } | ||
191 | |||
192 | void __init | ||
193 | prom_print(const char *msg) | ||
194 | { | ||
195 | const char *p, *q; | ||
196 | |||
197 | if (prom_stdout == 0) | ||
198 | return; | ||
199 | |||
200 | for (p = msg; *p != 0; p = q) { | ||
201 | for (q = p; *q != 0 && *q != '\n'; ++q) | ||
202 | ; | ||
203 | if (q > p) | ||
204 | call_prom("write", 3, 1, prom_stdout, p, q - p); | ||
205 | if (*q != 0) { | ||
206 | ++q; | ||
207 | call_prom("write", 3, 1, prom_stdout, "\r\n", 2); | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static void __init | ||
213 | prom_print_hex(unsigned int v) | ||
214 | { | ||
215 | char buf[16]; | ||
216 | int i, c; | ||
217 | |||
218 | for (i = 0; i < 8; ++i) { | ||
219 | c = (v >> ((7-i)*4)) & 0xf; | ||
220 | c += (c >= 10)? ('a' - 10): '0'; | ||
221 | buf[i] = c; | ||
222 | } | ||
223 | buf[i] = ' '; | ||
224 | buf[i+1] = 0; | ||
225 | prom_print(buf); | ||
226 | } | ||
227 | |||
228 | static int __init | ||
229 | prom_set_color(ihandle ih, int i, int r, int g, int b) | ||
230 | { | ||
231 | return call_prom("call-method", 6, 1, "color!", ih, i, b, g, r); | ||
232 | } | ||
233 | |||
234 | static int __init | ||
235 | prom_next_node(phandle *nodep) | ||
236 | { | ||
237 | phandle node; | ||
238 | |||
239 | if ((node = *nodep) != 0 | ||
240 | && (*nodep = call_prom("child", 1, 1, node)) != 0) | ||
241 | return 1; | ||
242 | if ((*nodep = call_prom("peer", 1, 1, node)) != 0) | ||
243 | return 1; | ||
244 | for (;;) { | ||
245 | if ((node = call_prom("parent", 1, 1, node)) == 0) | ||
246 | return 0; | ||
247 | if ((*nodep = call_prom("peer", 1, 1, node)) != 0) | ||
248 | return 1; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | #ifdef CONFIG_POWER4 | ||
253 | /* | ||
254 | * Set up a hash table with a set of entries in it to map the | ||
255 | * first 64MB of RAM. This is used on 64-bit machines since | ||
256 | * some of them don't have BATs. | ||
257 | */ | ||
258 | |||
259 | static inline void make_pte(unsigned long htab, unsigned int hsize, | ||
260 | unsigned int va, unsigned int pa, int mode) | ||
261 | { | ||
262 | unsigned int *pteg; | ||
263 | unsigned int hash, i, vsid; | ||
264 | |||
265 | vsid = ((va >> 28) * 0x111) << 12; | ||
266 | hash = ((va ^ vsid) >> 5) & 0x7fff80; | ||
267 | pteg = (unsigned int *)(htab + (hash & (hsize - 1))); | ||
268 | for (i = 0; i < 8; ++i, pteg += 4) { | ||
269 | if ((pteg[1] & 1) == 0) { | ||
270 | pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; | ||
271 | pteg[3] = pa | mode; | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | |||
277 | extern unsigned long _SDR1; | ||
278 | extern PTE *Hash; | ||
279 | extern unsigned long Hash_size; | ||
280 | |||
281 | static void __init | ||
282 | prom_alloc_htab(void) | ||
283 | { | ||
284 | unsigned int hsize; | ||
285 | unsigned long htab; | ||
286 | unsigned int addr; | ||
287 | |||
288 | /* | ||
289 | * Because of OF bugs we can't use the "claim" client | ||
290 | * interface to allocate memory for the hash table. | ||
291 | * This code is only used on 64-bit PPCs, and the only | ||
292 | * 64-bit PPCs at the moment are RS/6000s, and their | ||
293 | * OF is based at 0xc00000 (the 12M point), so we just | ||
294 | * arbitrarily use the 0x800000 - 0xc00000 region for the | ||
295 | * hash table. | ||
296 | * -- paulus. | ||
297 | */ | ||
298 | hsize = 4 << 20; /* POWER4 has no BATs */ | ||
299 | htab = (8 << 20); | ||
300 | call_prom("claim", 3, 1, htab, hsize, 0); | ||
301 | Hash = (void *)(htab + KERNELBASE); | ||
302 | Hash_size = hsize; | ||
303 | _SDR1 = htab + __ilog2(hsize) - 18; | ||
304 | |||
305 | /* | ||
306 | * Put in PTEs for the first 64MB of RAM | ||
307 | */ | ||
308 | memset((void *)htab, 0, hsize); | ||
309 | for (addr = 0; addr < 0x4000000; addr += 0x1000) | ||
310 | make_pte(htab, hsize, addr + KERNELBASE, addr, | ||
311 | _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); | ||
312 | #if 0 /* DEBUG stuff mapping the SCC */ | ||
313 | make_pte(htab, hsize, 0x80013000, 0x80013000, | ||
314 | _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); | ||
315 | #endif | ||
316 | } | ||
317 | #endif /* CONFIG_POWER4 */ | ||
318 | |||
319 | |||
320 | /* | ||
321 | * If we have a display that we don't know how to drive, | ||
322 | * we will want to try to execute OF's open method for it | ||
323 | * later. However, OF will probably fall over if we do that | ||
324 | * we've taken over the MMU. | ||
325 | * So we check whether we will need to open the display, | ||
326 | * and if so, open it now. | ||
327 | */ | ||
328 | static unsigned long __init | ||
329 | check_display(unsigned long mem) | ||
330 | { | ||
331 | phandle node; | ||
332 | ihandle ih; | ||
333 | int i, j; | ||
334 | char type[16], *path; | ||
335 | static unsigned char default_colors[] = { | ||
336 | 0x00, 0x00, 0x00, | ||
337 | 0x00, 0x00, 0xaa, | ||
338 | 0x00, 0xaa, 0x00, | ||
339 | 0x00, 0xaa, 0xaa, | ||
340 | 0xaa, 0x00, 0x00, | ||
341 | 0xaa, 0x00, 0xaa, | ||
342 | 0xaa, 0xaa, 0x00, | ||
343 | 0xaa, 0xaa, 0xaa, | ||
344 | 0x55, 0x55, 0x55, | ||
345 | 0x55, 0x55, 0xff, | ||
346 | 0x55, 0xff, 0x55, | ||
347 | 0x55, 0xff, 0xff, | ||
348 | 0xff, 0x55, 0x55, | ||
349 | 0xff, 0x55, 0xff, | ||
350 | 0xff, 0xff, 0x55, | ||
351 | 0xff, 0xff, 0xff | ||
352 | }; | ||
353 | const unsigned char *clut; | ||
354 | |||
355 | prom_disp_node = 0; | ||
356 | |||
357 | for (node = 0; prom_next_node(&node); ) { | ||
358 | type[0] = 0; | ||
359 | call_prom("getprop", 4, 1, node, "device_type", | ||
360 | type, sizeof(type)); | ||
361 | if (strcmp(type, "display") != 0) | ||
362 | continue; | ||
363 | /* It seems OF doesn't null-terminate the path :-( */ | ||
364 | path = (char *) mem; | ||
365 | memset(path, 0, 256); | ||
366 | if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) | ||
367 | continue; | ||
368 | |||
369 | /* | ||
370 | * If this display is the device that OF is using for stdout, | ||
371 | * move it to the front of the list. | ||
372 | */ | ||
373 | mem += strlen(path) + 1; | ||
374 | i = prom_num_displays++; | ||
375 | if (of_stdout_device != 0 && i > 0 | ||
376 | && strcmp(of_stdout_device, path) == 0) { | ||
377 | for (; i > 0; --i) { | ||
378 | prom_display_paths[i] | ||
379 | = prom_display_paths[i-1]; | ||
380 | prom_display_nodes[i] | ||
381 | = prom_display_nodes[i-1]; | ||
382 | } | ||
383 | } | ||
384 | prom_display_paths[i] = path; | ||
385 | prom_display_nodes[i] = node; | ||
386 | if (i == 0) | ||
387 | prom_disp_node = node; | ||
388 | if (prom_num_displays >= FB_MAX) | ||
389 | break; | ||
390 | } | ||
391 | |||
392 | for (j=0; j<prom_num_displays; j++) { | ||
393 | path = prom_display_paths[j]; | ||
394 | node = prom_display_nodes[j]; | ||
395 | prom_print("opening display "); | ||
396 | prom_print(path); | ||
397 | ih = call_prom("open", 1, 1, path); | ||
398 | if (ih == 0 || ih == (ihandle) -1) { | ||
399 | prom_print("... failed\n"); | ||
400 | for (i=j+1; i<prom_num_displays; i++) { | ||
401 | prom_display_paths[i-1] = prom_display_paths[i]; | ||
402 | prom_display_nodes[i-1] = prom_display_nodes[i]; | ||
403 | } | ||
404 | if (--prom_num_displays > 0) { | ||
405 | prom_disp_node = prom_display_nodes[j]; | ||
406 | j--; | ||
407 | } else | ||
408 | prom_disp_node = 0; | ||
409 | continue; | ||
410 | } else { | ||
411 | prom_print("... ok\n"); | ||
412 | call_prom("setprop", 4, 1, node, "linux,opened", 0, 0); | ||
413 | |||
414 | /* | ||
415 | * Setup a usable color table when the appropriate | ||
416 | * method is available. | ||
417 | * Should update this to use set-colors. | ||
418 | */ | ||
419 | clut = default_colors; | ||
420 | for (i = 0; i < 32; i++, clut += 3) | ||
421 | if (prom_set_color(ih, i, clut[0], clut[1], | ||
422 | clut[2]) != 0) | ||
423 | break; | ||
424 | |||
425 | #ifdef CONFIG_LOGO_LINUX_CLUT224 | ||
426 | clut = PTRRELOC(logo_linux_clut224.clut); | ||
427 | for (i = 0; i < logo_linux_clut224.clutsize; | ||
428 | i++, clut += 3) | ||
429 | if (prom_set_color(ih, i + 32, clut[0], | ||
430 | clut[1], clut[2]) != 0) | ||
431 | break; | ||
432 | #endif /* CONFIG_LOGO_LINUX_CLUT224 */ | ||
433 | } | ||
434 | } | ||
435 | |||
436 | if (prom_stdout) { | ||
437 | phandle p; | ||
438 | p = call_prom("instance-to-package", 1, 1, prom_stdout); | ||
439 | if (p && p != -1) { | ||
440 | type[0] = 0; | ||
441 | call_prom("getprop", 4, 1, p, "device_type", | ||
442 | type, sizeof(type)); | ||
443 | if (strcmp(type, "display") == 0) | ||
444 | call_prom("setprop", 4, 1, p, "linux,boot-display", | ||
445 | 0, 0); | ||
446 | } | ||
447 | } | ||
448 | |||
449 | return ALIGNUL(mem); | ||
450 | } | ||
451 | |||
452 | /* This function will enable the early boot text when doing OF booting. This | ||
453 | * way, xmon output should work too | ||
454 | */ | ||
455 | static void __init | ||
456 | setup_disp_fake_bi(ihandle dp) | ||
457 | { | ||
458 | #ifdef CONFIG_BOOTX_TEXT | ||
459 | int width = 640, height = 480, depth = 8, pitch; | ||
460 | unsigned address; | ||
461 | struct pci_reg_property addrs[8]; | ||
462 | int i, naddrs; | ||
463 | char name[32]; | ||
464 | char *getprop = "getprop"; | ||
465 | |||
466 | prom_print("Initializing fake screen: "); | ||
467 | |||
468 | memset(name, 0, sizeof(name)); | ||
469 | call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); | ||
470 | name[sizeof(name)-1] = 0; | ||
471 | prom_print(name); | ||
472 | prom_print("\n"); | ||
473 | call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); | ||
474 | call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); | ||
475 | call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); | ||
476 | pitch = width * ((depth + 7) / 8); | ||
477 | call_prom(getprop, 4, 1, dp, "linebytes", | ||
478 | &pitch, sizeof(pitch)); | ||
479 | if (pitch == 1) | ||
480 | pitch = 0x1000; /* for strange IBM display */ | ||
481 | address = 0; | ||
482 | call_prom(getprop, 4, 1, dp, "address", | ||
483 | &address, sizeof(address)); | ||
484 | if (address == 0) { | ||
485 | /* look for an assigned address with a size of >= 1MB */ | ||
486 | naddrs = call_prom(getprop, 4, 1, dp, "assigned-addresses", | ||
487 | addrs, sizeof(addrs)); | ||
488 | naddrs /= sizeof(struct pci_reg_property); | ||
489 | for (i = 0; i < naddrs; ++i) { | ||
490 | if (addrs[i].size_lo >= (1 << 20)) { | ||
491 | address = addrs[i].addr.a_lo; | ||
492 | /* use the BE aperture if possible */ | ||
493 | if (addrs[i].size_lo >= (16 << 20)) | ||
494 | address += (8 << 20); | ||
495 | break; | ||
496 | } | ||
497 | } | ||
498 | if (address == 0) { | ||
499 | prom_print("Failed to get address\n"); | ||
500 | return; | ||
501 | } | ||
502 | } | ||
503 | /* kludge for valkyrie */ | ||
504 | if (strcmp(name, "valkyrie") == 0) | ||
505 | address += 0x1000; | ||
506 | |||
507 | #ifdef CONFIG_POWER4 | ||
508 | #if CONFIG_TASK_SIZE > 0x80000000 | ||
509 | #error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5 | ||
510 | #endif | ||
511 | { | ||
512 | extern boot_infos_t disp_bi; | ||
513 | unsigned long va, pa, i, offset; | ||
514 | va = 0x90000000; | ||
515 | pa = address & 0xfffff000ul; | ||
516 | offset = address & 0x00000fff; | ||
517 | |||
518 | for (i=0; i<0x4000; i++) { | ||
519 | make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa, | ||
520 | _PAGE_ACCESSED | _PAGE_NO_CACHE | | ||
521 | _PAGE_GUARDED | PP_RWXX); | ||
522 | va += 0x1000; | ||
523 | pa += 0x1000; | ||
524 | } | ||
525 | btext_setup_display(width, height, depth, pitch, 0x90000000 | offset); | ||
526 | disp_bi.dispDeviceBase = (u8 *)address; | ||
527 | } | ||
528 | #else /* CONFIG_POWER4 */ | ||
529 | btext_setup_display(width, height, depth, pitch, address); | ||
530 | btext_prepare_BAT(); | ||
531 | #endif /* CONFIG_POWER4 */ | ||
532 | #endif /* CONFIG_BOOTX_TEXT */ | ||
533 | } | ||
534 | |||
535 | /* | ||
536 | * Make a copy of the device tree from the PROM. | ||
537 | */ | ||
538 | static unsigned long __init | ||
539 | copy_device_tree(unsigned long mem_start, unsigned long mem_end) | ||
540 | { | ||
541 | phandle root; | ||
542 | unsigned long new_start; | ||
543 | struct device_node **allnextp; | ||
544 | |||
545 | root = call_prom("peer", 1, 1, (phandle)0); | ||
546 | if (root == (phandle)0) { | ||
547 | prom_print("couldn't get device tree root\n"); | ||
548 | prom_exit(); | ||
549 | } | ||
550 | allnextp = &allnodes; | ||
551 | mem_start = ALIGNUL(mem_start); | ||
552 | new_start = inspect_node(root, NULL, mem_start, mem_end, &allnextp); | ||
553 | *allnextp = NULL; | ||
554 | return new_start; | ||
555 | } | ||
556 | |||
557 | static unsigned long __init | ||
558 | inspect_node(phandle node, struct device_node *dad, | ||
559 | unsigned long mem_start, unsigned long mem_end, | ||
560 | struct device_node ***allnextpp) | ||
561 | { | ||
562 | int l; | ||
563 | phandle child; | ||
564 | struct device_node *np; | ||
565 | struct property *pp, **prev_propp; | ||
566 | char *prev_name, *namep; | ||
567 | unsigned char *valp; | ||
568 | |||
569 | np = (struct device_node *) mem_start; | ||
570 | mem_start += sizeof(struct device_node); | ||
571 | memset(np, 0, sizeof(*np)); | ||
572 | np->node = node; | ||
573 | **allnextpp = PTRUNRELOC(np); | ||
574 | *allnextpp = &np->allnext; | ||
575 | if (dad != 0) { | ||
576 | np->parent = PTRUNRELOC(dad); | ||
577 | /* we temporarily use the `next' field as `last_child'. */ | ||
578 | if (dad->next == 0) | ||
579 | dad->child = PTRUNRELOC(np); | ||
580 | else | ||
581 | dad->next->sibling = PTRUNRELOC(np); | ||
582 | dad->next = np; | ||
583 | } | ||
584 | |||
585 | /* get and store all properties */ | ||
586 | prev_propp = &np->properties; | ||
587 | prev_name = ""; | ||
588 | for (;;) { | ||
589 | pp = (struct property *) mem_start; | ||
590 | namep = (char *) (pp + 1); | ||
591 | pp->name = PTRUNRELOC(namep); | ||
592 | if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0) | ||
593 | break; | ||
594 | mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); | ||
595 | prev_name = namep; | ||
596 | valp = (unsigned char *) mem_start; | ||
597 | pp->value = PTRUNRELOC(valp); | ||
598 | pp->length = call_prom("getprop", 4, 1, node, namep, | ||
599 | valp, mem_end - mem_start); | ||
600 | if (pp->length < 0) | ||
601 | continue; | ||
602 | #ifdef MAX_PROPERTY_LENGTH | ||
603 | if (pp->length > MAX_PROPERTY_LENGTH) | ||
604 | continue; /* ignore this property */ | ||
605 | #endif | ||
606 | mem_start = ALIGNUL(mem_start + pp->length); | ||
607 | *prev_propp = PTRUNRELOC(pp); | ||
608 | prev_propp = &pp->next; | ||
609 | } | ||
610 | if (np->node != 0) { | ||
611 | /* Add a "linux,phandle" property" */ | ||
612 | pp = (struct property *) mem_start; | ||
613 | *prev_propp = PTRUNRELOC(pp); | ||
614 | prev_propp = &pp->next; | ||
615 | namep = (char *) (pp + 1); | ||
616 | pp->name = PTRUNRELOC(namep); | ||
617 | strcpy(namep, "linux,phandle"); | ||
618 | mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); | ||
619 | pp->value = (unsigned char *) PTRUNRELOC(&np->node); | ||
620 | pp->length = sizeof(np->node); | ||
621 | } | ||
622 | *prev_propp = NULL; | ||
623 | |||
624 | /* get the node's full name */ | ||
625 | l = call_prom("package-to-path", 3, 1, node, | ||
626 | mem_start, mem_end - mem_start); | ||
627 | if (l >= 0) { | ||
628 | char *p, *ep; | ||
629 | |||
630 | np->full_name = PTRUNRELOC((char *) mem_start); | ||
631 | *(char *)(mem_start + l) = 0; | ||
632 | /* Fixup an Apple bug where they have bogus \0 chars in the | ||
633 | * middle of the path in some properties | ||
634 | */ | ||
635 | for (p = (char *)mem_start, ep = p + l; p < ep; p++) | ||
636 | if ((*p) == '\0') { | ||
637 | memmove(p, p+1, ep - p); | ||
638 | ep--; | ||
639 | } | ||
640 | mem_start = ALIGNUL(mem_start + l + 1); | ||
641 | } | ||
642 | |||
643 | /* do all our children */ | ||
644 | child = call_prom("child", 1, 1, node); | ||
645 | while (child != 0) { | ||
646 | mem_start = inspect_node(child, np, mem_start, mem_end, | ||
647 | allnextpp); | ||
648 | child = call_prom("peer", 1, 1, child); | ||
649 | } | ||
650 | |||
651 | return mem_start; | ||
652 | } | ||
653 | |||
654 | unsigned long smp_chrp_cpu_nr __initdata = 0; | ||
655 | |||
656 | /* | ||
657 | * With CHRP SMP we need to use the OF to start the other | ||
658 | * processors so we can't wait until smp_boot_cpus (the OF is | ||
659 | * trashed by then) so we have to put the processors into | ||
660 | * a holding pattern controlled by the kernel (not OF) before | ||
661 | * we destroy the OF. | ||
662 | * | ||
663 | * This uses a chunk of high memory, puts some holding pattern | ||
664 | * code there and sends the other processors off to there until | ||
665 | * smp_boot_cpus tells them to do something. We do that by using | ||
666 | * physical address 0x0. The holding pattern checks that address | ||
667 | * until its cpu # is there, when it is that cpu jumps to | ||
668 | * __secondary_start(). smp_boot_cpus() takes care of setting those | ||
669 | * values. | ||
670 | * | ||
671 | * We also use physical address 0x4 here to tell when a cpu | ||
672 | * is in its holding pattern code. | ||
673 | * | ||
674 | * -- Cort | ||
675 | * | ||
676 | * Note that we have to do this if we have more than one CPU, | ||
677 | * even if this is a UP kernel. Otherwise when we trash OF | ||
678 | * the other CPUs will start executing some random instructions | ||
679 | * and crash the system. -- paulus | ||
680 | */ | ||
681 | static void __init | ||
682 | prom_hold_cpus(unsigned long mem) | ||
683 | { | ||
684 | extern void __secondary_hold(void); | ||
685 | unsigned long i; | ||
686 | int cpu; | ||
687 | phandle node; | ||
688 | char type[16], *path; | ||
689 | unsigned int reg; | ||
690 | |||
691 | /* | ||
692 | * XXX: hack to make sure we're chrp, assume that if we're | ||
693 | * chrp we have a device_type property -- Cort | ||
694 | */ | ||
695 | node = call_prom("finddevice", 1, 1, "/"); | ||
696 | if (call_prom("getprop", 4, 1, node, | ||
697 | "device_type", type, sizeof(type)) <= 0) | ||
698 | return; | ||
699 | |||
700 | /* copy the holding pattern code to someplace safe (0) */ | ||
701 | /* the holding pattern is now within the first 0x100 | ||
702 | bytes of the kernel image -- paulus */ | ||
703 | memcpy((void *)0, _stext, 0x100); | ||
704 | flush_icache_range(0, 0x100); | ||
705 | |||
706 | /* look for cpus */ | ||
707 | *(unsigned long *)(0x0) = 0; | ||
708 | asm volatile("dcbf 0,%0": : "r" (0) : "memory"); | ||
709 | for (node = 0; prom_next_node(&node); ) { | ||
710 | type[0] = 0; | ||
711 | call_prom("getprop", 4, 1, node, "device_type", | ||
712 | type, sizeof(type)); | ||
713 | if (strcmp(type, "cpu") != 0) | ||
714 | continue; | ||
715 | path = (char *) mem; | ||
716 | memset(path, 0, 256); | ||
717 | if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) | ||
718 | continue; | ||
719 | reg = -1; | ||
720 | call_prom("getprop", 4, 1, node, "reg", ®, sizeof(reg)); | ||
721 | cpu = smp_chrp_cpu_nr++; | ||
722 | #ifdef CONFIG_SMP | ||
723 | smp_hw_index[cpu] = reg; | ||
724 | #endif /* CONFIG_SMP */ | ||
725 | /* XXX: hack - don't start cpu 0, this cpu -- Cort */ | ||
726 | if (cpu == 0) | ||
727 | continue; | ||
728 | prom_print("starting cpu "); | ||
729 | prom_print(path); | ||
730 | *(ulong *)(0x4) = 0; | ||
731 | call_prom("start-cpu", 3, 0, node, | ||
732 | (char *)__secondary_hold - _stext, cpu); | ||
733 | prom_print("..."); | ||
734 | for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) | ||
735 | ; | ||
736 | if (*(ulong *)(0x4) == cpu) | ||
737 | prom_print("ok\n"); | ||
738 | else { | ||
739 | prom_print("failed: "); | ||
740 | prom_print_hex(*(ulong *)0x4); | ||
741 | prom_print("\n"); | ||
742 | } | ||
743 | } | ||
744 | } | ||
745 | |||
746 | static void __init | ||
747 | prom_instantiate_rtas(void) | ||
748 | { | ||
749 | ihandle prom_rtas; | ||
750 | prom_arg_t result; | ||
751 | |||
752 | prom_rtas = call_prom("finddevice", 1, 1, "/rtas"); | ||
753 | if (prom_rtas == -1) | ||
754 | return; | ||
755 | |||
756 | rtas_size = 0; | ||
757 | call_prom("getprop", 4, 1, prom_rtas, | ||
758 | "rtas-size", &rtas_size, sizeof(rtas_size)); | ||
759 | prom_print("instantiating rtas"); | ||
760 | if (rtas_size == 0) { | ||
761 | rtas_data = 0; | ||
762 | } else { | ||
763 | /* | ||
764 | * Ask OF for some space for RTAS. | ||
765 | * Actually OF has bugs so we just arbitrarily | ||
766 | * use memory at the 6MB point. | ||
767 | */ | ||
768 | rtas_data = 6 << 20; | ||
769 | prom_print(" at "); | ||
770 | prom_print_hex(rtas_data); | ||
771 | } | ||
772 | |||
773 | prom_rtas = call_prom("open", 1, 1, "/rtas"); | ||
774 | prom_print("..."); | ||
775 | rtas_entry = 0; | ||
776 | if (call_prom_ret("call-method", 3, 2, &result, | ||
777 | "instantiate-rtas", prom_rtas, rtas_data) == 0) | ||
778 | rtas_entry = result; | ||
779 | if ((rtas_entry == -1) || (rtas_entry == 0)) | ||
780 | prom_print(" failed\n"); | ||
781 | else | ||
782 | prom_print(" done\n"); | ||
783 | } | ||
784 | |||
785 | /* | ||
786 | * We enter here early on, when the Open Firmware prom is still | ||
787 | * handling exceptions and the MMU hash table for us. | ||
788 | */ | ||
789 | unsigned long __init | ||
790 | prom_init(int r3, int r4, prom_entry pp) | ||
791 | { | ||
792 | unsigned long mem; | ||
793 | ihandle prom_mmu; | ||
794 | unsigned long offset = reloc_offset(); | ||
795 | int i, l; | ||
796 | char *p, *d; | ||
797 | unsigned long phys; | ||
798 | prom_arg_t result[3]; | ||
799 | char model[32]; | ||
800 | phandle node; | ||
801 | int rc; | ||
802 | |||
803 | /* Default */ | ||
804 | phys = (unsigned long) &_stext; | ||
805 | |||
806 | /* First get a handle for the stdout device */ | ||
807 | prom = pp; | ||
808 | prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); | ||
809 | if (prom_chosen == -1) | ||
810 | prom_exit(); | ||
811 | if (call_prom("getprop", 4, 1, prom_chosen, "stdout", | ||
812 | &prom_stdout, sizeof(prom_stdout)) <= 0) | ||
813 | prom_exit(); | ||
814 | |||
815 | /* Get the full OF pathname of the stdout device */ | ||
816 | mem = (unsigned long) klimit + offset; | ||
817 | p = (char *) mem; | ||
818 | memset(p, 0, 256); | ||
819 | call_prom("instance-to-path", 3, 1, prom_stdout, p, 255); | ||
820 | of_stdout_device = p; | ||
821 | mem += strlen(p) + 1; | ||
822 | |||
823 | /* Get the boot device and translate it to a full OF pathname. */ | ||
824 | p = (char *) mem; | ||
825 | l = call_prom("getprop", 4, 1, prom_chosen, "bootpath", p, 1<<20); | ||
826 | if (l > 0) { | ||
827 | p[l] = 0; /* should already be null-terminated */ | ||
828 | bootpath = PTRUNRELOC(p); | ||
829 | mem += l + 1; | ||
830 | d = (char *) mem; | ||
831 | *d = 0; | ||
832 | call_prom("canon", 3, 1, p, d, 1<<20); | ||
833 | bootdevice = PTRUNRELOC(d); | ||
834 | mem = ALIGNUL(mem + strlen(d) + 1); | ||
835 | } | ||
836 | |||
837 | prom_instantiate_rtas(); | ||
838 | |||
839 | #ifdef CONFIG_POWER4 | ||
840 | /* | ||
841 | * Find out how much memory we have and allocate a | ||
842 | * suitably-sized hash table. | ||
843 | */ | ||
844 | prom_alloc_htab(); | ||
845 | #endif | ||
846 | mem = check_display(mem); | ||
847 | |||
848 | prom_print("copying OF device tree..."); | ||
849 | mem = copy_device_tree(mem, mem + (1<<20)); | ||
850 | prom_print("done\n"); | ||
851 | |||
852 | prom_hold_cpus(mem); | ||
853 | |||
854 | klimit = (char *) (mem - offset); | ||
855 | |||
856 | node = call_prom("finddevice", 1, 1, "/"); | ||
857 | rc = call_prom("getprop", 4, 1, node, "model", model, sizeof(model)); | ||
858 | if (rc > 0 && !strncmp (model, "Pegasos", 7) | ||
859 | && strncmp (model, "Pegasos2", 8)) { | ||
860 | /* Pegasos 1 has a broken translate method in the OF, | ||
861 | * and furthermore the BATs are mapped 1:1 so the phys | ||
862 | * address calculated above is correct, so let's use | ||
863 | * it directly. | ||
864 | */ | ||
865 | } else if (offset == 0) { | ||
866 | /* If we are already running at 0xc0000000, we assume we were | ||
867 | * loaded by an OF bootloader which did set a BAT for us. | ||
868 | * This breaks OF translate so we force phys to be 0. | ||
869 | */ | ||
870 | prom_print("(already at 0xc0000000) phys=0\n"); | ||
871 | phys = 0; | ||
872 | } else if (call_prom("getprop", 4, 1, prom_chosen, "mmu", | ||
873 | &prom_mmu, sizeof(prom_mmu)) <= 0) { | ||
874 | prom_print(" no MMU found\n"); | ||
875 | } else if (call_prom_ret("call-method", 4, 4, result, "translate", | ||
876 | prom_mmu, &_stext, 1) != 0) { | ||
877 | prom_print(" (translate failed)\n"); | ||
878 | } else { | ||
879 | /* We assume the phys. address size is 3 cells */ | ||
880 | phys = result[2]; | ||
881 | } | ||
882 | |||
883 | if (prom_disp_node != 0) | ||
884 | setup_disp_fake_bi(prom_disp_node); | ||
885 | |||
886 | /* Use quiesce call to get OF to shut down any devices it's using */ | ||
887 | prom_print("Calling quiesce ...\n"); | ||
888 | call_prom("quiesce", 0, 0); | ||
889 | |||
890 | /* Relocate various pointers which will be used once the | ||
891 | kernel is running at the address it was linked at. */ | ||
892 | for (i = 0; i < prom_num_displays; ++i) | ||
893 | prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]); | ||
894 | |||
895 | #ifdef CONFIG_SERIAL_CORE_CONSOLE | ||
896 | /* Relocate the of stdout for console autodetection */ | ||
897 | of_stdout_device = PTRUNRELOC(of_stdout_device); | ||
898 | #endif | ||
899 | |||
900 | prom_print("returning 0x"); | ||
901 | prom_print_hex(phys); | ||
902 | prom_print("from prom_init\n"); | ||
903 | prom_stdout = 0; | ||
904 | |||
905 | return phys; | ||
906 | } | ||
907 | |||
908 | /* | ||
909 | * early_get_property is used to access the device tree image prepared | ||
910 | * by BootX very early on, before the pointers in it have been relocated. | ||
911 | */ | ||
912 | static void * __init | ||
913 | early_get_property(unsigned long base, unsigned long node, char *prop) | ||
914 | { | ||
915 | struct device_node *np = (struct device_node *)(base + node); | ||
916 | struct property *pp; | ||
917 | |||
918 | for (pp = np->properties; pp != 0; pp = pp->next) { | ||
919 | pp = (struct property *) (base + (unsigned long)pp); | ||
920 | if (strcmp((char *)((unsigned long)pp->name + base), | ||
921 | prop) == 0) { | ||
922 | return (void *)((unsigned long)pp->value + base); | ||
923 | } | ||
924 | } | ||
925 | return NULL; | ||
926 | } | ||
927 | |||
928 | /* Is boot-info compatible ? */ | ||
929 | #define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) | ||
930 | #define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) | ||
931 | #define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) | ||
932 | |||
933 | void __init | ||
934 | bootx_init(unsigned long r4, unsigned long phys) | ||
935 | { | ||
936 | boot_infos_t *bi = (boot_infos_t *) r4; | ||
937 | unsigned long space; | ||
938 | unsigned long ptr, x; | ||
939 | char *model; | ||
940 | |||
941 | boot_infos = PTRUNRELOC(bi); | ||
942 | if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) | ||
943 | bi->logicalDisplayBase = NULL; | ||
944 | |||
945 | #ifdef CONFIG_BOOTX_TEXT | ||
946 | btext_init(bi); | ||
947 | |||
948 | /* | ||
949 | * Test if boot-info is compatible. Done only in config | ||
950 | * CONFIG_BOOTX_TEXT since there is nothing much we can do | ||
951 | * with an incompatible version, except display a message | ||
952 | * and eventually hang the processor... | ||
953 | * | ||
954 | * I'll try to keep enough of boot-info compatible in the | ||
955 | * future to always allow display of this message; | ||
956 | */ | ||
957 | if (!BOOT_INFO_IS_COMPATIBLE(bi)) { | ||
958 | btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"); | ||
959 | btext_flushscreen(); | ||
960 | } | ||
961 | #endif /* CONFIG_BOOTX_TEXT */ | ||
962 | |||
963 | /* New BootX enters kernel with MMU off, i/os are not allowed | ||
964 | here. This hack will have been done by the boostrap anyway. | ||
965 | */ | ||
966 | if (bi->version < 4) { | ||
967 | /* | ||
968 | * XXX If this is an iMac, turn off the USB controller. | ||
969 | */ | ||
970 | model = (char *) early_get_property | ||
971 | (r4 + bi->deviceTreeOffset, 4, "model"); | ||
972 | if (model | ||
973 | && (strcmp(model, "iMac,1") == 0 | ||
974 | || strcmp(model, "PowerMac1,1") == 0)) { | ||
975 | out_le32((unsigned *)0x80880008, 1); /* XXX */ | ||
976 | } | ||
977 | } | ||
978 | |||
979 | /* Move klimit to enclose device tree, args, ramdisk, etc... */ | ||
980 | if (bi->version < 5) { | ||
981 | space = bi->deviceTreeOffset + bi->deviceTreeSize; | ||
982 | if (bi->ramDisk) | ||
983 | space = bi->ramDisk + bi->ramDiskSize; | ||
984 | } else | ||
985 | space = bi->totalParamsSize; | ||
986 | klimit = PTRUNRELOC((char *) bi + space); | ||
987 | |||
988 | /* New BootX will have flushed all TLBs and enters kernel with | ||
989 | MMU switched OFF, so this should not be useful anymore. | ||
990 | */ | ||
991 | if (bi->version < 4) { | ||
992 | /* | ||
993 | * Touch each page to make sure the PTEs for them | ||
994 | * are in the hash table - the aim is to try to avoid | ||
995 | * getting DSI exceptions while copying the kernel image. | ||
996 | */ | ||
997 | for (ptr = ((unsigned long) &_stext) & PAGE_MASK; | ||
998 | ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) | ||
999 | x = *(volatile unsigned long *)ptr; | ||
1000 | } | ||
1001 | |||
1002 | #ifdef CONFIG_BOOTX_TEXT | ||
1003 | /* | ||
1004 | * Note that after we call btext_prepare_BAT, we can't do | ||
1005 | * prom_draw*, flushscreen or clearscreen until we turn the MMU | ||
1006 | * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase | ||
1007 | * to a virtual address. | ||
1008 | */ | ||
1009 | btext_prepare_BAT(); | ||
1010 | #endif | ||
1011 | } | ||