]> err.no Git - linux-2.6/blobdiff - arch/powerpc/kernel/prom_init.c
[PATCH] powerpc: Fixed memory reserve map layout
[linux-2.6] / arch / powerpc / kernel / prom_init.c
index 9b1baaa9eda0a9b7e4f261914b20c51ba913cf7c..d963a12ec640007269d5be45a63a64a73f04cb1b 100644 (file)
@@ -41,7 +41,6 @@
 #include <asm/pgtable.h>
 #include <asm/pci.h>
 #include <asm/iommu.h>
-#include <asm/bootinfo.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
 #include <asm/machdep.h>
@@ -95,11 +94,17 @@ extern const struct linux_logo logo_linux_clut224;
 #ifdef CONFIG_PPC64
 #define RELOC(x)        (*PTRRELOC(&(x)))
 #define ADDR(x)                (u32) add_reloc_offset((unsigned long)(x))
+#define OF_WORKAROUNDS 0
 #else
 #define RELOC(x)       (x)
 #define ADDR(x)                (u32) (x)
+#define OF_WORKAROUNDS of_workarounds
+int of_workarounds;
 #endif
 
+#define OF_WA_CLAIM    1       /* do phys/virt claim separately, then map */
+#define OF_WA_LONGTRAIL        2       /* work around longtrail bugs */
+
 #define PROM_BUG() do {                                                \
         prom_printf("kernel BUG at %s line 0x%x!\n",           \
                    RELOC(__FILE__), __LINE__);                 \
@@ -112,11 +117,6 @@ extern const struct linux_logo logo_linux_clut224;
 #define prom_debug(x...)
 #endif
 
-#ifdef CONFIG_PPC32
-#define PLATFORM_POWERMAC      _MACH_Pmac
-#define PLATFORM_CHRP          _MACH_chrp
-#endif
-
 
 typedef u32 prom_arg_t;
 
@@ -129,14 +129,16 @@ struct prom_args {
 
 struct prom_t {
        ihandle root;
-       ihandle chosen;
+       phandle chosen;
        int cpu;
        ihandle stdout;
+       ihandle mmumap;
+       ihandle memory;
 };
 
 struct mem_map_entry {
-       unsigned long   base;
-       unsigned long   size;
+       u64     base;
+       u64     size;
 };
 
 typedef u32 cell_t;
@@ -144,11 +146,11 @@ typedef u32 cell_t;
 extern void __start(unsigned long r3, unsigned long r4, unsigned long r5);
 
 #ifdef CONFIG_PPC64
-extern void enter_prom(struct prom_args *args, unsigned long entry);
+extern int enter_prom(struct prom_args *args, unsigned long entry);
 #else
-static inline void enter_prom(struct prom_args *args, unsigned long entry)
+static inline int enter_prom(struct prom_args *args, unsigned long entry)
 {
-       ((void (*)(struct prom_args *))entry)(args);
+       return ((int (*)(struct prom_args *))entry)(args);
 }
 #endif
 
@@ -190,6 +192,11 @@ static unsigned long __initdata alloc_bottom;
 static unsigned long __initdata rmo_top;
 static unsigned long __initdata ram_top;
 
+#ifdef CONFIG_KEXEC
+static unsigned long __initdata prom_crashk_base;
+static unsigned long __initdata prom_crashk_size;
+#endif
+
 static struct mem_map_entry __initdata mem_reserve_map[MEM_RESERVE_MAP_SIZE];
 static int __initdata mem_reserve_cnt;
 
@@ -240,7 +247,8 @@ static int __init call_prom(const char *service, int nargs, int nret, ...)
        for (i = 0; i < nret; i++)
                args.args[nargs+i] = 0;
 
-       enter_prom(&args, RELOC(prom_entry));
+       if (enter_prom(&args, RELOC(prom_entry)) < 0)
+               return PROM_ERROR;
 
        return (nret > 0) ? args.args[nargs] : 0;
 }
@@ -262,9 +270,10 @@ static int __init call_prom_ret(const char *service, int nargs, int nret,
        va_end(list);
 
        for (i = 0; i < nret; i++)
-               rets[nargs+i] = 0;
+               args.args[nargs+i] = 0;
 
-       enter_prom(&args, RELOC(prom_entry));
+       if (enter_prom(&args, RELOC(prom_entry)) < 0)
+               return PROM_ERROR;
 
        if (rets != NULL)
                for (i = 1; i < nret; ++i)
@@ -274,14 +283,6 @@ static int __init call_prom_ret(const char *service, int nargs, int nret,
 }
 
 
-static unsigned int __init prom_claim(unsigned long virt, unsigned long size,
-                               unsigned long align)
-{
-       return (unsigned int)call_prom("claim", 3, 1,
-                                      (prom_arg_t)virt, (prom_arg_t)size,
-                                      (prom_arg_t)align);
-}
-
 static void __init prom_print(const char *msg)
 {
        const char *p, *q;
@@ -363,6 +364,41 @@ static void __init prom_printf(const char *format, ...)
 }
 
 
+static unsigned int __init prom_claim(unsigned long virt, unsigned long size,
+                               unsigned long align)
+{
+       struct prom_t *_prom = &RELOC(prom);
+
+       if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) {
+               /*
+                * Old OF requires we claim physical and virtual separately
+                * and then map explicitly (assuming virtual mode)
+                */
+               int ret;
+               prom_arg_t result;
+
+               ret = call_prom_ret("call-method", 5, 2, &result,
+                                   ADDR("claim"), _prom->memory,
+                                   align, size, virt);
+               if (ret != 0 || result == -1)
+                       return -1;
+               ret = call_prom_ret("call-method", 5, 2, &result,
+                                   ADDR("claim"), _prom->mmumap,
+                                   align, size, virt);
+               if (ret != 0) {
+                       call_prom("call-method", 4, 1, ADDR("release"),
+                                 _prom->memory, size, virt);
+                       return -1;
+               }
+               /* the 0x12 is M (coherence) + PP == read/write */
+               call_prom("call-method", 6, 1,
+                         ADDR("map"), _prom->mmumap, 0x12, size, virt, virt);
+               return virt;
+       }
+       return call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size,
+                        (prom_arg_t)align);
+}
+
 static void __init __attribute__((noreturn)) prom_panic(const char *reason)
 {
 #ifdef CONFIG_PPC64
@@ -394,23 +430,64 @@ static int __init prom_next_node(phandle *nodep)
        }
 }
 
-static int __init prom_getprop(phandle node, const char *pname,
+static int inline prom_getprop(phandle node, const char *pname,
                               void *value, size_t valuelen)
 {
        return call_prom("getprop", 4, 1, node, ADDR(pname),
                         (u32)(unsigned long) value, (u32) valuelen);
 }
 
-static int __init prom_getproplen(phandle node, const char *pname)
+static int inline prom_getproplen(phandle node, const char *pname)
 {
        return call_prom("getproplen", 2, 1, node, ADDR(pname));
 }
 
-static int __init prom_setprop(phandle node, const char *pname,
-                              void *value, size_t valuelen)
+static void add_string(char **str, const char *q)
 {
-       return call_prom("setprop", 4, 1, node, ADDR(pname),
-                        (u32)(unsigned long) value, (u32) valuelen);
+       char *p = *str;
+
+       while (*q)
+               *p++ = *q++;
+       *p++ = ' ';
+       *str = p;
+}
+
+static char *tohex(unsigned int x)
+{
+       static char digits[] = "0123456789abcdef";
+       static char result[9];
+       int i;
+
+       result[8] = 0;
+       i = 8;
+       do {
+               --i;
+               result[i] = digits[x & 0xf];
+               x >>= 4;
+       } while (x != 0 && i > 0);
+       return &result[i];
+}
+
+static int __init prom_setprop(phandle node, const char *nodename,
+                              const char *pname, void *value, size_t valuelen)
+{
+       char cmd[256], *p;
+
+       if (!(OF_WORKAROUNDS & OF_WA_LONGTRAIL))
+               return call_prom("setprop", 4, 1, node, ADDR(pname),
+                                (u32)(unsigned long) value, (u32) valuelen);
+
+       /* gah... setprop doesn't work on longtrail, have to use interpret */
+       p = cmd;
+       add_string(&p, "dev");
+       add_string(&p, nodename);
+       add_string(&p, tohex((u32)(unsigned long) value));
+       add_string(&p, tohex(valuelen));
+       add_string(&p, tohex(ADDR(pname)));
+       add_string(&p, tohex(strlen(RELOC(pname))));
+       add_string(&p, "property");
+       *p = 0;
+       return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd);
 }
 
 /* We can't use the standard versions because of RELOC headaches. */
@@ -481,7 +558,8 @@ unsigned long prom_memparse(const char *ptr, const char **retptr)
 static void __init early_cmdline_parse(void)
 {
        struct prom_t *_prom = &RELOC(prom);
-       char *opt, *p;
+       const char *opt;
+       char *p;
        int l = 0;
 
        RELOC(prom_cmd_line[0]) = 0;
@@ -518,6 +596,34 @@ static void __init early_cmdline_parse(void)
                RELOC(prom_memory_limit) = ALIGN(RELOC(prom_memory_limit), 0x1000000);
 #endif
        }
+
+#ifdef CONFIG_KEXEC
+       /*
+        * crashkernel=size@addr specifies the location to reserve for
+        * crash kernel.
+        */
+       opt = strstr(RELOC(prom_cmd_line), RELOC("crashkernel="));
+       if (opt) {
+               opt += 12;
+               RELOC(prom_crashk_size) = prom_memparse(opt, &opt);
+
+               if (ALIGN(RELOC(prom_crashk_size), 0x1000000) !=
+                       RELOC(prom_crashk_size)) {
+                       prom_printf("Warning: crashkernel size is not "
+                                       "aligned to 16MB\n");
+               }
+
+               /*
+                * At present, the crash kernel always run at 32MB.
+                * Just ignore whatever user passed.
+                */
+               RELOC(prom_crashk_base) = 0x2000000;
+               if (*opt == '@') {
+                       prom_printf("Warning: PPC64 kdump kernel always runs "
+                                       "at 32 MB\n");
+               }
+       }
+#endif
 }
 
 #ifdef CONFIG_PPC_PSERIES
@@ -662,9 +768,11 @@ static void __init prom_send_capabilities(void)
  */
 static unsigned long __init alloc_up(unsigned long size, unsigned long align)
 {
-       unsigned long base = _ALIGN_UP(RELOC(alloc_bottom), align);
+       unsigned long base = RELOC(alloc_bottom);
        unsigned long addr = 0;
 
+       if (align)
+               base = _ALIGN_UP(base, align);
        prom_debug("alloc_up(%x, %x)\n", size, align);
        if (RELOC(ram_top) == 0)
                prom_panic("alloc_up() called with mem not initialized\n");
@@ -678,7 +786,7 @@ static unsigned long __init alloc_up(unsigned long size, unsigned long align)
            base = _ALIGN_UP(base + 0x100000, align)) {
                prom_debug("    trying: 0x%x\n\r", base);
                addr = (unsigned long)prom_claim(base, size, 0);
-               if (addr != PROM_ERROR)
+               if (addr != PROM_ERROR && addr != 0)
                        break;
                addr = 0;
                if (align == 0)
@@ -738,7 +846,7 @@ static unsigned long __init alloc_down(unsigned long size, unsigned long align,
             base = _ALIGN_DOWN(base - 0x100000, align))  {
                prom_debug("    trying: 0x%x\n\r", base);
                addr = (unsigned long)prom_claim(base, size, 0);
-               if (addr != PROM_ERROR)
+               if (addr != PROM_ERROR && addr != 0)
                        break;
                addr = 0;
        }
@@ -772,7 +880,7 @@ static unsigned long __init prom_next_cell(int s, cell_t **cellp)
        }
        r = *p++;
 #ifdef CONFIG_PPC64
-       if (s) {
+       if (s > 1) {
                r <<= 32;
                r |= *(p++);
        }
@@ -789,9 +897,9 @@ static unsigned long __init prom_next_cell(int s, cell_t **cellp)
  * If problems seem to show up, it would be a good start to track
  * them down.
  */
-static void reserve_mem(unsigned long base, unsigned long size)
+static void reserve_mem(u64 base, u64 size)
 {
-       unsigned long top = base + size;
+       u64 top = base + size;
        unsigned long cnt = RELOC(mem_reserve_cnt);
 
        if (size == 0)
@@ -844,9 +952,16 @@ static void __init prom_init_mem(void)
                type[0] = 0;
                prom_getprop(node, "device_type", type, sizeof(type));
 
+               if (type[0] == 0) {
+                       /*
+                        * CHRP Longtrail machines have no device_type
+                        * on the memory node, so check the name instead...
+                        */
+                       prom_getprop(node, "name", type, sizeof(type));
+               }
                if (strcmp(type, RELOC("memory")))
                        continue;
-       
+
                plen = prom_getprop(node, "reg", RELOC(regbuf), sizeof(regbuf));
                if (plen > sizeof(regbuf)) {
                        prom_printf("memory node too large for buffer !\n");
@@ -930,6 +1045,12 @@ static void __init prom_init_mem(void)
        prom_printf("  alloc_top_hi : %x\n", RELOC(alloc_top_high));
        prom_printf("  rmo_top      : %x\n", RELOC(rmo_top));
        prom_printf("  ram_top      : %x\n", RELOC(ram_top));
+#ifdef CONFIG_KEXEC
+       if (RELOC(prom_crashk_base)) {
+               prom_printf("  crashk_base  : %x\n",  RELOC(prom_crashk_base));
+               prom_printf("  crashk_size  : %x\n", RELOC(prom_crashk_size));
+       }
+#endif
 }
 
 
@@ -962,7 +1083,7 @@ static void __init prom_instantiate_rtas(void)
 
        rtas_inst = call_prom("open", 1, 1, ADDR("/rtas"));
        if (!IHANDLE_VALID(rtas_inst)) {
-               prom_printf("opening rtas package failed");
+               prom_printf("opening rtas package failed (%x)\n", rtas_inst);
                return;
        }
 
@@ -970,7 +1091,7 @@ static void __init prom_instantiate_rtas(void)
 
        if (call_prom_ret("call-method", 3, 2, &entry,
                          ADDR("instantiate-rtas"),
-                         rtas_inst, base) == PROM_ERROR
+                         rtas_inst, base) != 0
            || entry == 0) {
                prom_printf(" failed\n");
                return;
@@ -979,8 +1100,10 @@ static void __init prom_instantiate_rtas(void)
 
        reserve_mem(base, size);
 
-       prom_setprop(rtas_node, "linux,rtas-base", &base, sizeof(base));
-       prom_setprop(rtas_node, "linux,rtas-entry", &entry, sizeof(entry));
+       prom_setprop(rtas_node, "/rtas", "linux,rtas-base",
+                    &base, sizeof(base));
+       prom_setprop(rtas_node, "/rtas", "linux,rtas-entry",
+                    &entry, sizeof(entry));
 
        prom_debug("rtas base     = 0x%x\n", base);
        prom_debug("rtas entry    = 0x%x\n", entry);
@@ -1071,10 +1194,6 @@ static void __init prom_initialize_tce_table(void)
                if (base < local_alloc_bottom)
                        local_alloc_bottom = base;
 
-               /* Save away the TCE table attributes for later use. */
-               prom_setprop(node, "linux,tce-base", &base, sizeof(base));
-               prom_setprop(node, "linux,tce-size", &minsize, sizeof(minsize));
-
                /* It seems OF doesn't null-terminate the path :-( */
                memset(path, 0, sizeof(path));
                /* Call OF to setup the TCE hardware */
@@ -1083,6 +1202,10 @@ static void __init prom_initialize_tce_table(void)
                        prom_printf("package-to-path failed\n");
                }
 
+               /* Save away the TCE table attributes for later use. */
+               prom_setprop(node, path, "linux,tce-base", &base, sizeof(base));
+               prom_setprop(node, path, "linux,tce-size", &minsize, sizeof(minsize));
+
                prom_debug("TCE table: %s\n", path);
                prom_debug("\tnode = 0x%x\n", node);
                prom_debug("\tbase = 0x%x\n", base);
@@ -1147,9 +1270,18 @@ static void __init prom_initialize_tce_table(void)
  *
  * -- Cort
  */
+extern void __secondary_hold(void);
+extern unsigned long __secondary_hold_spinloop;
+extern unsigned long __secondary_hold_acknowledge;
+
+/*
+ * We want to reference the copy of __secondary_hold_* in the
+ * 0 - 0x100 address range
+ */
+#define LOW_ADDR(x)    (((unsigned long) &(x)) & 0xff)
+
 static void __init prom_hold_cpus(void)
 {
-#ifdef CONFIG_PPC64
        unsigned long i;
        unsigned int reg;
        phandle node;
@@ -1158,20 +1290,18 @@ static void __init prom_hold_cpus(void)
        unsigned int interrupt_server[MAX_CPU_THREADS];
        unsigned int cpu_threads, hw_cpu_num;
        int propsize;
-       extern void __secondary_hold(void);
-       extern unsigned long __secondary_hold_spinloop;
-       extern unsigned long __secondary_hold_acknowledge;
+       struct prom_t *_prom = &RELOC(prom);
        unsigned long *spinloop
-               = (void *) __pa(&__secondary_hold_spinloop);
+               = (void *) LOW_ADDR(__secondary_hold_spinloop);
        unsigned long *acknowledge
-               = (void *) __pa(&__secondary_hold_acknowledge);
+               = (void *) LOW_ADDR(__secondary_hold_acknowledge);
 #ifdef CONFIG_PPC64
+       /* __secondary_hold is actually a descriptor, not the text address */
        unsigned long secondary_hold
                = __pa(*PTRRELOC((unsigned long *)__secondary_hold));
 #else
-       unsigned long secondary_hold = __pa(&__secondary_hold);
+       unsigned long secondary_hold = LOW_ADDR(__secondary_hold);
 #endif
-       struct prom_t *_prom = &RELOC(prom);
 
        prom_debug("prom_hold_cpus: start...\n");
        prom_debug("    1) spinloop       = 0x%x\n", (unsigned long)spinloop);
@@ -1189,9 +1319,8 @@ static void __init prom_hold_cpus(void)
        *spinloop = 0;
 
 #ifdef CONFIG_HMT
-       for (i = 0; i < NR_CPUS; i++) {
+       for (i = 0; i < NR_CPUS; i++)
                RELOC(hmt_thread_data)[i].pir = 0xdeadbeef;
-       }
 #endif
        /* look for cpus */
        for (node = 0; prom_next_node(&node); ) {
@@ -1242,34 +1371,22 @@ static void __init prom_hold_cpus(void)
                        call_prom("start-cpu", 3, 0, node,
                                  secondary_hold, reg);
 
-                       for ( i = 0 ; (i < 100000000) && 
-                             (*acknowledge == ((unsigned long)-1)); i++ )
+                       for (i = 0; (i < 100000000) && 
+                            (*acknowledge == ((unsigned long)-1)); i++ )
                                mb();
 
-                       if (*acknowledge == reg) {
+                       if (*acknowledge == reg)
                                prom_printf("done\n");
-                               /* We have to get every CPU out of OF,
-                                * even if we never start it. */
-                               if (cpuid >= NR_CPUS)
-                                       goto next;
-                       } else {
+                       else
                                prom_printf("failed: %x\n", *acknowledge);
-                       }
                }
 #ifdef CONFIG_SMP
                else
                        prom_printf("%x : boot cpu     %x\n", cpuid, reg);
-#endif
-next:
-#ifdef CONFIG_SMP
-               /* Init paca for secondary threads.   They start later. */
-               for (i=1; i < cpu_threads; i++) {
-                       cpuid++;
-                       if (cpuid >= NR_CPUS)
-                               continue;
-               }
 #endif /* CONFIG_SMP */
-               cpuid++;
+
+               /* Reserve cpu #s for secondary threads.   They start later. */
+               cpuid += cpu_threads;
        }
 #ifdef CONFIG_HMT
        /* Only enable HMT on processors that provide support. */
@@ -1303,7 +1420,6 @@ next:
                            ") exceeded: ignoring extras\n");
 
        prom_debug("prom_hold_cpus: end...\n");
-#endif
 }
 
 
@@ -1323,7 +1439,45 @@ static void __init prom_init_client_services(unsigned long pp)
        _prom->root = call_prom("finddevice", 1, 1, ADDR("/"));
        if (!PHANDLE_VALID(_prom->root))
                prom_panic("cannot find device tree root"); /* msg won't be printed :( */
+
+       _prom->mmumap = 0;
+}
+
+#ifdef CONFIG_PPC32
+/*
+ * For really old powermacs, we need to map things we claim.
+ * For that, we need the ihandle of the mmu.
+ * Also, on the longtrail, we need to work around other bugs.
+ */
+static void __init prom_find_mmu(void)
+{
+       struct prom_t *_prom = &RELOC(prom);
+       phandle oprom;
+       char version[64];
+
+       oprom = call_prom("finddevice", 1, 1, ADDR("/openprom"));
+       if (!PHANDLE_VALID(oprom))
+               return;
+       if (prom_getprop(oprom, "model", version, sizeof(version)) <= 0)
+               return;
+       version[sizeof(version) - 1] = 0;
+       /* XXX might need to add other versions here */
+       if (strcmp(version, "Open Firmware, 1.0.5") == 0)
+               of_workarounds = OF_WA_CLAIM;
+       else if (strncmp(version, "FirmWorks,3.", 12) == 0) {
+               of_workarounds = OF_WA_CLAIM | OF_WA_LONGTRAIL;
+               call_prom("interpret", 1, 1, "dev /memory 0 to allow-reclaim");
+       } else
+               return;
+       _prom->memory = call_prom("open", 1, 1, ADDR("/memory"));
+       prom_getprop(_prom->chosen, "mmu", &_prom->mmumap,
+                    sizeof(_prom->mmumap));
+       if (!IHANDLE_VALID(_prom->memory) || !IHANDLE_VALID(_prom->mmumap))
+               of_workarounds &= ~OF_WA_CLAIM;         /* hmmm */
 }
+#else
+#define prom_find_mmu()
+#endif
 
 static void __init prom_init_stdout(void)
 {
@@ -1341,16 +1495,17 @@ static void __init prom_init_stdout(void)
        memset(path, 0, 256);
        call_prom("instance-to-path", 3, 1, _prom->stdout, path, 255);
        val = call_prom("instance-to-package", 1, 1, _prom->stdout);
-       prom_setprop(_prom->chosen, "linux,stdout-package", &val, sizeof(val));
+       prom_setprop(_prom->chosen, "/chosen", "linux,stdout-package",
+                    &val, sizeof(val));
        prom_printf("OF stdout device is: %s\n", RELOC(of_stdout_device));
-       prom_setprop(_prom->chosen, "linux,stdout-path",
-                    RELOC(of_stdout_device), strlen(RELOC(of_stdout_device))+1);
+       prom_setprop(_prom->chosen, "/chosen", "linux,stdout-path",
+                    path, strlen(path) + 1);
 
        /* If it's a display, note it */
        memset(type, 0, sizeof(type));
        prom_getprop(val, "device_type", type, sizeof(type));
        if (strcmp(type, RELOC("display")) == 0)
-               prom_setprop(val, "linux,boot-display", NULL, 0);
+               prom_setprop(val, path, "linux,boot-display", NULL, 0);
 }
 
 static void __init prom_close_stdin(void)
@@ -1367,8 +1522,9 @@ static int __init prom_find_machine_type(void)
        struct prom_t *_prom = &RELOC(prom);
        char compat[256];
        int len, i = 0;
+#ifdef CONFIG_PPC64
        phandle rtas;
-
+#endif
        len = prom_getprop(_prom->root, "compatible",
                           compat, sizeof(compat)-1);
        if (len > 0) {
@@ -1379,11 +1535,13 @@ static int __init prom_find_machine_type(void)
                        if (sl == 0)
                                break;
                        if (strstr(p, RELOC("Power Macintosh")) ||
-                           strstr(p, RELOC("MacRISC4")))
+                           strstr(p, RELOC("MacRISC")))
                                return PLATFORM_POWERMAC;
 #ifdef CONFIG_PPC64
                        if (strstr(p, RELOC("Momentum,Maple")))
                                return PLATFORM_MAPLE;
+                       if (strstr(p, RELOC("IBM,CPB")))
+                               return PLATFORM_CELL;
 #endif
                        i += sl + 1;
                }
@@ -1472,7 +1630,7 @@ static void __init prom_check_displays(void)
 
                /* Success */
                prom_printf("done\n");
-               prom_setprop(node, "linux,opened", NULL, 0);
+               prom_setprop(node, path, "linux,opened", NULL, 0);
 
                /* Setup a usable color table when the appropriate
                 * method is available. Should update this to set-colors */
@@ -1601,39 +1759,37 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
        unsigned long soff;
        unsigned char *valp;
        static char pname[MAX_PROPERTY_NAME];
-       int l;
+       int l, room;
 
        dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end);
 
        /* get the node's full name */
        namep = (char *)*mem_start;
-       l = call_prom("package-to-path", 3, 1, node,
-                     namep, *mem_end - *mem_start);
+       room = *mem_end - *mem_start;
+       if (room > 255)
+               room = 255;
+       l = call_prom("package-to-path", 3, 1, node, namep, room);
        if (l >= 0) {
                /* Didn't fit?  Get more room. */
-               if ((l+1) > (*mem_end - *mem_start)) {
-                       namep = make_room(mem_start, mem_end, l+1, 1);
+               if (l >= room) {
+                       if (l >= *mem_end - *mem_start)
+                               namep = make_room(mem_start, mem_end, l+1, 1);
                        call_prom("package-to-path", 3, 1, node, namep, l);
                }
                namep[l] = '\0';
 
                /* Fixup an Apple bug where they have bogus \0 chars in the
-                * middle of the path in some properties
+                * middle of the path in some properties, and extract
+                * the unit name (everything after the last '/').
                 */
-               for (p = namep, ep = namep + l; p < ep; p++)
-                       if (*p == '\0') {
-                               memmove(p, p+1, ep - p);
-                               ep--; l--; p--;
-                       }
-
-               /* now try to extract the unit name in that mess */
-               for (p = namep, lp = NULL; *p; p++)
+               for (lp = p = namep, ep = namep + l; p < ep; p++) {
                        if (*p == '/')
-                               lp = p + 1;
-               if (lp != NULL)
-                       memmove(namep, lp, strlen(lp) + 1);
-               *mem_start = _ALIGN(((unsigned long) namep) +
-                                   strlen(namep) + 1, 4);
+                               lp = namep;
+                       else if (*p != 0)
+                               *lp++ = *p;
+               }
+               *lp = 0;
+               *mem_start = _ALIGN((unsigned long)lp + 1, 4);
        }
 
        /* get it again for debugging */
@@ -1833,7 +1989,7 @@ static void __init fixup_device_tree(void)
        if (prom_getprop(u3, "device-rev", &u3_rev, sizeof(u3_rev))
            == PROM_ERROR)
                return;
-       if (u3_rev != 0x35 && u3_rev != 0x37)
+       if (u3_rev < 0x35 || u3_rev > 0x39)
                return;
        /* does it need fixup ? */
        if (prom_getproplen(i2c, "interrupts") > 0)
@@ -1844,9 +2000,11 @@ static void __init fixup_device_tree(void)
        /* interrupt on this revision of u3 is number 0 and level */
        interrupts[0] = 0;
        interrupts[1] = 1;
-       prom_setprop(i2c, "interrupts", &interrupts, sizeof(interrupts));
+       prom_setprop(i2c, "/u3@0,f8000000/i2c@f8001000", "interrupts",
+                    &interrupts, sizeof(interrupts));
        parent = (u32)mpic;
-       prom_setprop(i2c, "interrupt-parent", &parent, sizeof(parent));
+       prom_setprop(i2c, "/u3@0,f8000000/i2c@f8001000", "interrupt-parent",
+                    &parent, sizeof(parent));
 #endif
 }
 
@@ -1858,8 +2016,9 @@ static void __init prom_find_boot_cpu(void)
        ihandle prom_cpu;
        phandle cpu_pkg;
 
+       _prom->cpu = 0;
        if (prom_getprop(_prom->chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0)
-               prom_panic("cannot find boot cpu");
+               return;
 
        cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu);
 
@@ -1877,15 +2036,15 @@ static void __init prom_check_initrd(unsigned long r3, unsigned long r4)
        if (r3 && r4 && r4 != 0xdeadbeef) {
                unsigned long val;
 
-               RELOC(prom_initrd_start) = (r3 >= KERNELBASE) ? __pa(r3) : r3;
+               RELOC(prom_initrd_start) = is_kernel_addr(r3) ? __pa(r3) : r3;
                RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4;
 
                val = RELOC(prom_initrd_start);
-               prom_setprop(_prom->chosen, "linux,initrd-start", &val,
-                            sizeof(val));
+               prom_setprop(_prom->chosen, "/chosen", "linux,initrd-start",
+                            &val, sizeof(val));
                val = RELOC(prom_initrd_end);
-               prom_setprop(_prom->chosen, "linux,initrd-end", &val,
-                            sizeof(val));
+               prom_setprop(_prom->chosen, "/chosen", "linux,initrd-end",
+                            &val, sizeof(val));
 
                reserve_mem(RELOC(prom_initrd_start),
                            RELOC(prom_initrd_end) - RELOC(prom_initrd_start));
@@ -1906,7 +2065,6 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
                               unsigned long r6, unsigned long r7)
 {      
                struct prom_t *_prom;
-       extern char _stext[];
        unsigned long hdr;
        u32 getprop_rval;
        unsigned long offset = reloc_offset();
@@ -1928,6 +2086,12 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         */
        prom_init_client_services(pp);
 
+       /*
+        * See if this OF is old enough that we need to do explicit maps
+        * and other workarounds
+        */
+       prom_find_mmu();
+
        /*
         * Init prom stdout device
         */
@@ -1944,19 +2108,20 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         */
        RELOC(of_platform) = prom_find_machine_type();
        getprop_rval = RELOC(of_platform);
-       prom_setprop(_prom->chosen, "linux,platform",
+       prom_setprop(_prom->chosen, "/chosen", "linux,platform",
                     &getprop_rval, sizeof(getprop_rval));
 
 #ifdef CONFIG_PPC_PSERIES
        /*
         * On pSeries, inform the firmware about our capabilities
         */
-       if (RELOC(of_platform) & PLATFORM_PSERIES)
+       if (RELOC(of_platform) == PLATFORM_PSERIES ||
+           RELOC(of_platform) == PLATFORM_PSERIES_LPAR)
                prom_send_capabilities();
 #endif
 
        /*
-        * On pSeries and BPA, copy the CPU hold code
+        * Copy the CPU hold code
         */
                if (RELOC(of_platform) != PLATFORM_POWERMAC)
                        copy_and_flush(0, KERNELBASE + offset, 0x100, 0);
@@ -1971,6 +2136,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         */
        prom_init_mem();
 
+#ifdef CONFIG_KEXEC
+       if (RELOC(prom_crashk_base))
+               reserve_mem(RELOC(prom_crashk_base), RELOC(prom_crashk_size));
+#endif
        /*
         * Determine which cpu is actually running right _now_
         */
@@ -2005,26 +2174,38 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         * Fill in some infos for use by the kernel later on
         */
        if (RELOC(prom_memory_limit))
-               prom_setprop(_prom->chosen, "linux,memory-limit",
+               prom_setprop(_prom->chosen, "/chosen", "linux,memory-limit",
                             &RELOC(prom_memory_limit),
                             sizeof(prom_memory_limit));
 #ifdef CONFIG_PPC64
        if (RELOC(ppc64_iommu_off))
-               prom_setprop(_prom->chosen, "linux,iommu-off", NULL, 0);
+               prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off",
+                            NULL, 0);
 
        if (RELOC(iommu_force_on))
-               prom_setprop(_prom->chosen, "linux,iommu-force-on", NULL, 0);
+               prom_setprop(_prom->chosen, "/chosen", "linux,iommu-force-on",
+                            NULL, 0);
 
        if (RELOC(prom_tce_alloc_start)) {
-               prom_setprop(_prom->chosen, "linux,tce-alloc-start",
+               prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-start",
                             &RELOC(prom_tce_alloc_start),
                             sizeof(prom_tce_alloc_start));
-               prom_setprop(_prom->chosen, "linux,tce-alloc-end",
+               prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-end",
                             &RELOC(prom_tce_alloc_end),
                             sizeof(prom_tce_alloc_end));
        }
 #endif
 
+#ifdef CONFIG_KEXEC
+       if (RELOC(prom_crashk_base)) {
+               prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-base",
+                       PTRRELOC(&prom_crashk_base),
+                       sizeof(RELOC(prom_crashk_base)));
+               prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-size",
+                       PTRRELOC(&prom_crashk_size),
+                       sizeof(RELOC(prom_crashk_size)));
+       }
+#endif
        /*
         * Fixup any known bugs in the device-tree
         */
@@ -2036,8 +2217,13 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
        prom_printf("copying OF device tree ...\n");
        flatten_device_tree();
 
-       /* in case stdin is USB and still active on IBM machines... */
-       prom_close_stdin();
+       /*
+        * in case stdin is USB and still active on IBM machines...
+        * Unfortunately quiesce crashes on some powermacs if we have
+        * closed stdin already (in particular the powerbook 101).
+        */
+       if (RELOC(of_platform) != PLATFORM_POWERMAC)
+               prom_close_stdin();
 
        /*
         * Call OF "quiesce" method to shut down pending DMA's from
@@ -2059,7 +2245,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
        reloc_got2(-offset);
 #endif
 
-       __start(hdr, 0, 0);
+       __start(hdr, KERNELBASE + offset, 0);
 
        return 0;
 }