/* * Note that prom_init() and anything called from prom_init() * may be running at an address that is different from the address * that it was linked at. References to static data items are * handled by compiling this file with -mrelocatable-lib. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_LOGO_LINUX_CLUT224 #include extern const struct linux_logo logo_linux_clut224; #endif /* * Properties whose value is longer than this get excluded from our * copy of the device tree. This way we don't waste space storing * things like "driver,AAPL,MacOS,PowerPC" properties. But this value * does need to be big enough to ensure that we don't lose things * like the interrupt-map property on a PCI-PCI bridge. */ #define MAX_PROPERTY_LENGTH 4096 #ifndef FB_MAX /* avoid pulling in all of the fb stuff */ #define FB_MAX 8 #endif #define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) typedef u32 prom_arg_t; struct prom_args { const char *service; int nargs; int nret; prom_arg_t args[10]; }; struct pci_address { unsigned a_hi; unsigned a_mid; unsigned a_lo; }; struct pci_reg_property { struct pci_address addr; unsigned size_hi; unsigned size_lo; }; struct pci_range { struct pci_address addr; unsigned phys; unsigned size_hi; unsigned size_lo; }; struct isa_reg_property { unsigned space; unsigned address; unsigned size; }; struct pci_intr_map { struct pci_address addr; unsigned dunno; phandle int_ctrler; unsigned intr; }; static void prom_exit(void); static int call_prom(const char *service, int nargs, int nret, ...); static int call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...); static void prom_print_hex(unsigned int v); static int prom_set_color(ihandle ih, int i, int r, int g, int b); static int prom_next_node(phandle *nodep); static unsigned long check_display(unsigned long mem); static void setup_disp_fake_bi(ihandle dp); static unsigned long copy_device_tree(unsigned long mem_start, unsigned long mem_end); static unsigned long inspect_node(phandle node, struct device_node *dad, unsigned long mem_start, unsigned long mem_end, struct device_node ***allnextpp); static void prom_hold_cpus(unsigned long mem); static void prom_instantiate_rtas(void); static void * early_get_property(unsigned long base, unsigned long node, char *prop); prom_entry prom __initdata; ihandle prom_chosen __initdata; ihandle prom_stdout __initdata; static char *prom_display_paths[FB_MAX] __initdata; static phandle prom_display_nodes[FB_MAX] __initdata; static unsigned int prom_num_displays __initdata; static ihandle prom_disp_node __initdata; char *of_stdout_device __initdata; unsigned int rtas_data; /* physical pointer */ unsigned int rtas_entry; /* physical pointer */ unsigned int rtas_size; unsigned int old_rtas; boot_infos_t *boot_infos; char *bootpath; char *bootdevice; struct device_node *allnodes; extern char *klimit; static void __init prom_exit(void) { struct prom_args args; args.service = "exit"; args.nargs = 0; args.nret = 0; prom(&args); for (;;) /* should never get here */ ; } static int __init call_prom(const char *service, int nargs, int nret, ...) { va_list list; int i; struct prom_args prom_args; prom_args.service = service; prom_args.nargs = nargs; prom_args.nret = nret; va_start(list, nret); for (i = 0; i < nargs; ++i) prom_args.args[i] = va_arg(list, prom_arg_t); va_end(list); for (i = 0; i < nret; ++i) prom_args.args[i + nargs] = 0; prom(&prom_args); return prom_args.args[nargs]; } static int __init call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...) { va_list list; int i; struct prom_args prom_args; prom_args.service = service; prom_args.nargs = nargs; prom_args.nret = nret; va_start(list, rets); for (i = 0; i < nargs; ++i) prom_args.args[i] = va_arg(list, int); va_end(list); for (i = 0; i < nret; ++i) prom_args.args[i + nargs] = 0; prom(&prom_args); for (i = 1; i < nret; ++i) rets[i-1] = prom_args.args[nargs + i]; return prom_args.args[nargs]; } void __init prom_print(const char *msg) { const char *p, *q; if (prom_stdout == 0) return; for (p = msg; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n'; ++q) ; if (q > p) call_prom("write", 3, 1, prom_stdout, p, q - p); if (*q != 0) { ++q; call_prom("write", 3, 1, prom_stdout, "\r\n", 2); } } } static void __init prom_print_hex(unsigned int v) { char buf[16]; int i, c; for (i = 0; i < 8; ++i) { c = (v >> ((7-i)*4)) & 0xf; c += (c >= 10)? ('a' - 10): '0'; buf[i] = c; } buf[i] = ' '; buf[i+1] = 0; prom_print(buf); } static int __init prom_set_color(ihandle ih, int i, int r, int g, int b) { return call_prom("call-method", 6, 1, "color!", ih, i, b, g, r); } static int __init prom_next_node(phandle *nodep) { phandle node; if ((node = *nodep) != 0 && (*nodep = call_prom("child", 1, 1, node)) != 0) return 1; if ((*nodep = call_prom("peer", 1, 1, node)) != 0) return 1; for (;;) { if ((node = call_prom("parent", 1, 1, node)) == 0) return 0; if ((*nodep = call_prom("peer", 1, 1, node)) != 0) return 1; } } #ifdef CONFIG_POWER4 /* * Set up a hash table with a set of entries in it to map the * first 64MB of RAM. This is used on 64-bit machines since * some of them don't have BATs. */ static inline void make_pte(unsigned long htab, unsigned int hsize, unsigned int va, unsigned int pa, int mode) { unsigned int *pteg; unsigned int hash, i, vsid; vsid = ((va >> 28) * 0x111) << 12; hash = ((va ^ vsid) >> 5) & 0x7fff80; pteg = (unsigned int *)(htab + (hash & (hsize - 1))); for (i = 0; i < 8; ++i, pteg += 4) { if ((pteg[1] & 1) == 0) { pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; pteg[3] = pa | mode; break; } } } extern unsigned long _SDR1; extern PTE *Hash; extern unsigned long Hash_size; static void __init prom_alloc_htab(void) { unsigned int hsize; unsigned long htab; unsigned int addr; /* * Because of OF bugs we can't use the "claim" client * interface to allocate memory for the hash table. * This code is only used on 64-bit PPCs, and the only * 64-bit PPCs at the moment are RS/6000s, and their * OF is based at 0xc00000 (the 12M point), so we just * arbitrarily use the 0x800000 - 0xc00000 region for the * hash table. * -- paulus. */ hsize = 4 << 20; /* POWER4 has no BATs */ htab = (8 << 20); call_prom("claim", 3, 1, htab, hsize, 0); Hash = (void *)(htab + KERNELBASE); Hash_size = hsize; _SDR1 = htab + __ilog2(hsize) - 18; /* * Put in PTEs for the first 64MB of RAM */ memset((void *)htab, 0, hsize); for (addr = 0; addr < 0x4000000; addr += 0x1000) make_pte(htab, hsize, addr + KERNELBASE, addr, _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); #if 0 /* DEBUG stuff mapping the SCC */ make_pte(htab, hsize, 0x80013000, 0x80013000, _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); #endif } #endif /* CONFIG_POWER4 */ /* * If we have a display that we don't know how to drive, * we will want to try to execute OF's open method for it * later. However, OF will probably fall over if we do that * we've taken over the MMU. * So we check whether we will need to open the display, * and if so, open it now. */ static unsigned long __init check_display(unsigned long mem) { phandle node; ihandle ih; int i, j; char type[16], *path; static unsigned char default_colors[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff, 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff }; const unsigned char *clut; prom_disp_node = 0; for (node = 0; prom_next_node(&node); ) { type[0] = 0; call_prom("getprop", 4, 1, node, "device_type", type, sizeof(type)); if (strcmp(type, "display") != 0) continue; /* It seems OF doesn't null-terminate the path :-( */ path = (char *) mem; memset(path, 0, 256); if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) continue; /* * If this display is the device that OF is using for stdout, * move it to the front of the list. */ mem += strlen(path) + 1; i = prom_num_displays++; if (of_stdout_device != 0 && i > 0 && strcmp(of_stdout_device, path) == 0) { for (; i > 0; --i) { prom_display_paths[i] = prom_display_paths[i-1]; prom_display_nodes[i] = prom_display_nodes[i-1]; } } prom_display_paths[i] = path; prom_display_nodes[i] = node; if (i == 0) prom_disp_node = node; if (prom_num_displays >= FB_MAX) break; } for (j=0; j 0) { prom_disp_node = prom_display_nodes[j]; j--; } else prom_disp_node = 0; continue; } else { prom_print("... ok\n"); call_prom("setprop", 4, 1, node, "linux,opened", 0, 0); /* * Setup a usable color table when the appropriate * method is available. * Should update this to use set-colors. */ clut = default_colors; for (i = 0; i < 32; i++, clut += 3) if (prom_set_color(ih, i, clut[0], clut[1], clut[2]) != 0) break; #ifdef CONFIG_LOGO_LINUX_CLUT224 clut = PTRRELOC(logo_linux_clut224.clut); for (i = 0; i < logo_linux_clut224.clutsize; i++, clut += 3) if (prom_set_color(ih, i + 32, clut[0], clut[1], clut[2]) != 0) break; #endif /* CONFIG_LOGO_LINUX_CLUT224 */ } } if (prom_stdout) { phandle p; p = call_prom("instance-to-package", 1, 1, prom_stdout); if (p && p != -1) { type[0] = 0; call_prom("getprop", 4, 1, p, "device_type", type, sizeof(type)); if (strcmp(type, "display") == 0) call_prom("setprop", 4, 1, p, "linux,boot-display", 0, 0); } } return ALIGNUL(mem); } /* This function will enable the early boot text when doing OF booting. This * way, xmon output should work too */ static void __init setup_disp_fake_bi(ihandle dp) { #ifdef CONFIG_BOOTX_TEXT int width = 640, height = 480, depth = 8, pitch; unsigned address; struct pci_reg_property addrs[8]; int i, naddrs; char name[32]; char *getprop = "getprop"; prom_print("Initializing fake screen: "); memset(name, 0, sizeof(name)); call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); name[sizeof(name)-1] = 0; prom_print(name); prom_print("\n"); call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); pitch = width * ((depth + 7) / 8); call_prom(getprop, 4, 1, dp, "linebytes", &pitch, sizeof(pitch)); if (pitch == 1) pitch = 0x1000; /* for strange IBM display */ address = 0; call_prom(getprop, 4, 1, dp, "address", &address, sizeof(address)); if (address == 0) { /* look for an assigned address with a size of >= 1MB */ naddrs = call_prom(getprop, 4, 1, dp, "assigned-addresses", addrs, sizeof(addrs)); naddrs /= sizeof(struct pci_reg_property); for (i = 0; i < naddrs; ++i) { if (addrs[i].size_lo >= (1 << 20)) { address = addrs[i].addr.a_lo; /* use the BE aperture if possible */ if (addrs[i].size_lo >= (16 << 20)) address += (8 << 20); break; } } if (address == 0) { prom_print("Failed to get address\n"); return; } } /* kludge for valkyrie */ if (strcmp(name, "valkyrie") == 0) address += 0x1000; #ifdef CONFIG_POWER4 #if CONFIG_TASK_SIZE > 0x80000000 #error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5 #endif { extern boot_infos_t disp_bi; unsigned long va, pa, i, offset; va = 0x90000000; pa = address & 0xfffff000ul; offset = address & 0x00000fff; for (i=0; i<0x4000; i++) { make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa, _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); va += 0x1000; pa += 0x1000; } btext_setup_display(width, height, depth, pitch, 0x90000000 | offset); disp_bi.dispDeviceBase = (u8 *)address; } #else /* CONFIG_POWER4 */ btext_setup_display(width, height, depth, pitch, address); btext_prepare_BAT(); #endif /* CONFIG_POWER4 */ #endif /* CONFIG_BOOTX_TEXT */ } /* * Make a copy of the device tree from the PROM. */ static unsigned long __init copy_device_tree(unsigned long mem_start, unsigned long mem_end) { phandle root; unsigned long new_start; struct device_node **allnextp; root = call_prom("peer", 1, 1, (phandle)0); if (root == (phandle)0) { prom_print("couldn't get device tree root\n"); prom_exit(); } allnextp = &allnodes; mem_start = ALIGNUL(mem_start); new_start = inspect_node(root, NULL, mem_start, mem_end, &allnextp); *allnextp = NULL; return new_start; } static unsigned long __init inspect_node(phandle node, struct device_node *dad, unsigned long mem_start, unsigned long mem_end, struct device_node ***allnextpp) { int l; phandle child; struct device_node *np; struct property *pp, **prev_propp; char *prev_name, *namep; unsigned char *valp; np = (struct device_node *) mem_start; mem_start += sizeof(struct device_node); memset(np, 0, sizeof(*np)); np->node = node; **allnextpp = PTRUNRELOC(np); *allnextpp = &np->allnext; if (dad != 0) { np->parent = PTRUNRELOC(dad); /* we temporarily use the `next' field as `last_child'. */ if (dad->next == 0) dad->child = PTRUNRELOC(np); else dad->next->sibling = PTRUNRELOC(np); dad->next = np; } /* get and store all properties */ prev_propp = &np->properties; prev_name = ""; for (;;) { pp = (struct property *) mem_start; namep = (char *) (pp + 1); pp->name = PTRUNRELOC(namep); if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0) break; mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); prev_name = namep; valp = (unsigned char *) mem_start; pp->value = PTRUNRELOC(valp); pp->length = call_prom("getprop", 4, 1, node, namep, valp, mem_end - mem_start); if (pp->length < 0) continue; #ifdef MAX_PROPERTY_LENGTH if (pp->length > MAX_PROPERTY_LENGTH) continue; /* ignore this property */ #endif mem_start = ALIGNUL(mem_start + pp->length); *prev_propp = PTRUNRELOC(pp); prev_propp = &pp->next; } if (np->node != 0) { /* Add a "linux,phandle" property" */ pp = (struct property *) mem_start; *prev_propp = PTRUNRELOC(pp); prev_propp = &pp->next; namep = (char *) (pp + 1); pp->name = PTRUNRELOC(namep); strcpy(namep, "linux,phandle"); mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); pp->value = (unsigned char *) PTRUNRELOC(&np->node); pp->length = sizeof(np->node); } *prev_propp = NULL; /* get the node's full name */ l = call_prom("package-to-path", 3, 1, node, mem_start, mem_end - mem_start); if (l >= 0) { char *p, *ep; np->full_name = PTRUNRELOC((char *) mem_start); *(char *)(mem_start + l) = 0; /* Fixup an Apple bug where they have bogus \0 chars in the * middle of the path in some properties */ for (p = (char *)mem_start, ep = p + l; p < ep; p++) if ((*p) == '\0') { memmove(p, p+1, ep - p); ep--; } mem_start = ALIGNUL(mem_start + l + 1); } /* do all our children */ child = call_prom("child", 1, 1, node); while (child != 0) { mem_start = inspect_node(child, np, mem_start, mem_end, allnextpp); child = call_prom("peer", 1, 1, child); } return mem_start; } unsigned long smp_chrp_cpu_nr __initdata = 0; /* * With CHRP SMP we need to use the OF to start the other * processors so we can't wait until smp_boot_cpus (the OF is * trashed by then) so we have to put the processors into * a holding pattern controlled by the kernel (not OF) before * we destroy the OF. * * This uses a chunk of high memory, puts some holding pattern * code there and sends the other processors off to there until * smp_boot_cpus tells them to do something. We do that by using * physical address 0x0. The holding pattern checks that address * until its cpu # is there, when it is that cpu jumps to * __secondary_start(). smp_boot_cpus() takes care of setting those * values. * * We also use physical address 0x4 here to tell when a cpu * is in its holding pattern code. * * -- Cort * * Note that we have to do this if we have more than one CPU, * even if this is a UP kernel. Otherwise when we trash OF * the other CPUs will start executing some random instructions * and crash the system. -- paulus */ static void __init prom_hold_cpus(unsigned long mem) { extern void __secondary_hold(void); unsigned long i; int cpu; phandle node; char type[16], *path; unsigned int reg; /* * XXX: hack to make sure we're chrp, assume that if we're * chrp we have a device_type property -- Cort */ node = call_prom("finddevice", 1, 1, "/"); if (call_prom("getprop", 4, 1, node, "device_type", type, sizeof(type)) <= 0) return; /* copy the holding pattern code to someplace safe (0) */ /* the holding pattern is now within the first 0x100 bytes of the kernel image -- paulus */ memcpy((void *)0, _stext, 0x100); flush_icache_range(0, 0x100); /* look for cpus */ *(unsigned long *)(0x0) = 0; asm volatile("dcbf 0,%0": : "r" (0) : "memory"); for (node = 0; prom_next_node(&node); ) { type[0] = 0; call_prom("getprop", 4, 1, node, "device_type", type, sizeof(type)); if (strcmp(type, "cpu") != 0) continue; path = (char *) mem; memset(path, 0, 256); if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) continue; reg = -1; call_prom("getprop", 4, 1, node, "reg", ®, sizeof(reg)); cpu = smp_chrp_cpu_nr++; #ifdef CONFIG_SMP smp_hw_index[cpu] = reg; #endif /* CONFIG_SMP */ /* XXX: hack - don't start cpu 0, this cpu -- Cort */ if (cpu == 0) continue; prom_print("starting cpu "); prom_print(path); *(ulong *)(0x4) = 0; call_prom("start-cpu", 3, 0, node, (char *)__secondary_hold - _stext, cpu); prom_print("..."); for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) ; if (*(ulong *)(0x4) == cpu) prom_print("ok\n"); else { prom_print("failed: "); prom_print_hex(*(ulong *)0x4); prom_print("\n"); } } } static void __init prom_instantiate_rtas(void) { ihandle prom_rtas; prom_arg_t result; prom_rtas = call_prom("finddevice", 1, 1, "/rtas"); if (prom_rtas == -1) return; rtas_size = 0; call_prom("getprop", 4, 1, prom_rtas, "rtas-size", &rtas_size, sizeof(rtas_size)); prom_print("instantiating rtas"); if (rtas_size == 0) { rtas_data = 0; } else { /* * Ask OF for some space for RTAS. * Actually OF has bugs so we just arbitrarily * use memory at the 6MB point. */ rtas_data = 6 << 20; prom_print(" at "); prom_print_hex(rtas_data); } prom_rtas = call_prom("open", 1, 1, "/rtas"); prom_print("..."); rtas_entry = 0; if (call_prom_ret("call-method", 3, 2, &result, "instantiate-rtas", prom_rtas, rtas_data) == 0) rtas_entry = result; if ((rtas_entry == -1) || (rtas_entry == 0)) prom_print(" failed\n"); else prom_print(" done\n"); } /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. */ unsigned long __init prom_init(int r3, int r4, prom_entry pp) { unsigned long mem; ihandle prom_mmu; unsigned long offset = reloc_offset(); int i, l; char *p, *d; unsigned long phys; prom_arg_t result[3]; char model[32]; phandle node; int rc; /* Default */ phys = (unsigned long) &_stext; /* First get a handle for the stdout device */ prom = pp; prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); if (prom_chosen == -1) prom_exit(); if (call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout, sizeof(prom_stdout)) <= 0) prom_exit(); /* Get the full OF pathname of the stdout device */ mem = (unsigned long) klimit + offset; p = (char *) mem; memset(p, 0, 256); call_prom("instance-to-path", 3, 1, prom_stdout, p, 255); of_stdout_device = p; mem += strlen(p) + 1; /* Get the boot device and translate it to a full OF pathname. */ p = (char *) mem; l = call_prom("getprop", 4, 1, prom_chosen, "bootpath", p, 1<<20); if (l > 0) { p[l] = 0; /* should already be null-terminated */ bootpath = PTRUNRELOC(p); mem += l + 1; d = (char *) mem; *d = 0; call_prom("canon", 3, 1, p, d, 1<<20); bootdevice = PTRUNRELOC(d); mem = ALIGNUL(mem + strlen(d) + 1); } prom_instantiate_rtas(); #ifdef CONFIG_POWER4 /* * Find out how much memory we have and allocate a * suitably-sized hash table. */ prom_alloc_htab(); #endif mem = check_display(mem); prom_print("copying OF device tree..."); mem = copy_device_tree(mem, mem + (1<<20)); prom_print("done\n"); prom_hold_cpus(mem); klimit = (char *) (mem - offset); node = call_prom("finddevice", 1, 1, "/"); rc = call_prom("getprop", 4, 1, node, "model", model, sizeof(model)); if (rc > 0 && !strncmp (model, "Pegasos", 7) && strncmp (model, "Pegasos2", 8)) { /* Pegasos 1 has a broken translate method in the OF, * and furthermore the BATs are mapped 1:1 so the phys * address calculated above is correct, so let's use * it directly. */ } else if (offset == 0) { /* If we are already running at 0xc0000000, we assume we were * loaded by an OF bootloader which did set a BAT for us. * This breaks OF translate so we force phys to be 0. */ prom_print("(already at 0xc0000000) phys=0\n"); phys = 0; } else if (call_prom("getprop", 4, 1, prom_chosen, "mmu", &prom_mmu, sizeof(prom_mmu)) <= 0) { prom_print(" no MMU found\n"); } else if (call_prom_ret("call-method", 4, 4, result, "translate", prom_mmu, &_stext, 1) != 0) { prom_print(" (translate failed)\n"); } else { /* We assume the phys. address size is 3 cells */ phys = result[2]; } if (prom_disp_node != 0) setup_disp_fake_bi(prom_disp_node); /* Use quiesce call to get OF to shut down any devices it's using */ prom_print("Calling quiesce ...\n"); call_prom("quiesce", 0, 0); /* Relocate various pointers which will be used once the kernel is running at the address it was linked at. */ for (i = 0; i < prom_num_displays; ++i) prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]); #ifdef CONFIG_SERIAL_CORE_CONSOLE /* Relocate the of stdout for console autodetection */ of_stdout_device = PTRUNRELOC(of_stdout_device); #endif prom_print("returning 0x"); prom_print_hex(phys); prom_print("from prom_init\n"); prom_stdout = 0; return phys; } /* * early_get_property is used to access the device tree image prepared * by BootX very early on, before the pointers in it have been relocated. */ static void * __init early_get_property(unsigned long base, unsigned long node, char *prop) { struct device_node *np = (struct device_node *)(base + node); struct property *pp; for (pp = np->properties; pp != 0; pp = pp->next) { pp = (struct property *) (base + (unsigned long)pp); if (strcmp((char *)((unsigned long)pp->name + base), prop) == 0) { return (void *)((unsigned long)pp->value + base); } } return NULL; } /* Is boot-info compatible ? */ #define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) #define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) #define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) void __init bootx_init(unsigned long r4, unsigned long phys) { boot_infos_t *bi = (boot_infos_t *) r4; unsigned long space; unsigned long ptr, x; char *model; boot_infos = PTRUNRELOC(bi); if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) bi->logicalDisplayBase = NULL; #ifdef CONFIG_BOOTX_TEXT btext_init(bi); /* * Test if boot-info is compatible. Done only in config * CONFIG_BOOTX_TEXT since there is nothing much we can do * with an incompatible version, except display a message * and eventually hang the processor... * * I'll try to keep enough of boot-info compatible in the * future to always allow display of this message; */ if (!BOOT_INFO_IS_COMPATIBLE(bi)) { btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"); btext_flushscreen(); } #endif /* CONFIG_BOOTX_TEXT */ /* New BootX enters kernel with MMU off, i/os are not allowed here. This hack will have been done by the boostrap anyway. */ if (bi->version < 4) { /* * XXX If this is an iMac, turn off the USB controller. */ model = (char *) early_get_property (r4 + bi->deviceTreeOffset, 4, "model"); if (model && (strcmp(model, "iMac,1") == 0 || strcmp(model, "PowerMac1,1") == 0)) { out_le32((unsigned *)0x80880008, 1); /* XXX */ } } /* Move klimit to enclose device tree, args, ramdisk, etc... */ if (bi->version < 5) { space = bi->deviceTreeOffset + bi->deviceTreeSize; if (bi->ramDisk) space = bi->ramDisk + bi->ramDiskSize; } else space = bi->totalParamsSize; klimit = PTRUNRELOC((char *) bi + space); /* New BootX will have flushed all TLBs and enters kernel with MMU switched OFF, so this should not be useful anymore. */ if (bi->version < 4) { /* * Touch each page to make sure the PTEs for them * are in the hash table - the aim is to try to avoid * getting DSI exceptions while copying the kernel image. */ for (ptr = ((unsigned long) &_stext) & PAGE_MASK; ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) x = *(volatile unsigned long *)ptr; } #ifdef CONFIG_BOOTX_TEXT /* * Note that after we call btext_prepare_BAT, we can't do * prom_draw*, flushscreen or clearscreen until we turn the MMU * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase * to a virtual address. */ btext_prepare_BAT(); #endif }