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