Revert to Fedora kernel-2.6.17-1.2187_FC5 patched with vs2.0.2.1; there are too many...
[linux-2.6.git] / arch / powerpc / kernel / prom_init.c
index 59465e5..2e9c953 100644 (file)
@@ -16,6 +16,7 @@
 #undef DEBUG_PROM
 
 #include <stdarg.h>
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/init.h>
@@ -193,12 +194,19 @@ static int __initdata of_platform;
 
 static char __initdata prom_cmd_line[COMMAND_LINE_SIZE];
 
+static unsigned long __initdata prom_memory_limit;
+
 static unsigned long __initdata alloc_top;
 static unsigned long __initdata alloc_top_high;
 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;
 
@@ -557,9 +565,7 @@ unsigned long prom_memparse(const char *ptr, const char **retptr)
 static void __init early_cmdline_parse(void)
 {
        struct prom_t *_prom = &RELOC(prom);
-#ifdef CONFIG_PPC64
        const char *opt;
-#endif
        char *p;
        int l = 0;
 
@@ -568,7 +574,7 @@ static void __init early_cmdline_parse(void)
        if ((long)_prom->chosen > 0)
                l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1);
 #ifdef CONFIG_CMDLINE
-       if (l <= 0 || p[0] == '\0') /* dbl check */
+       if (l == 0) /* dbl check */
                strlcpy(RELOC(prom_cmd_line),
                        RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line));
 #endif /* CONFIG_CMDLINE */
@@ -587,6 +593,45 @@ static void __init early_cmdline_parse(void)
                        RELOC(iommu_force_on) = 1;
        }
 #endif
+
+       opt = strstr(RELOC(prom_cmd_line), RELOC("mem="));
+       if (opt) {
+               opt += 4;
+               RELOC(prom_memory_limit) = prom_memparse(opt, (const char **)&opt);
+#ifdef CONFIG_PPC64
+               /* Align to 16 MB == size of ppc64 large page */
+               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, (const char **)&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
@@ -646,13 +691,13 @@ static unsigned char ibm_architecture_vec[] = {
        5 - 1,                          /* 5 option vectors */
 
        /* option vector 1: processor architectures supported */
-       3 - 2,                          /* length */
+       3 - 1,                          /* length */
        0,                              /* don't ignore, don't halt */
        OV1_PPC_2_00 | OV1_PPC_2_01 | OV1_PPC_2_02 | OV1_PPC_2_03 |
        OV1_PPC_2_04 | OV1_PPC_2_05,
 
        /* option vector 2: Open Firmware options supported */
-       34 - 2,                         /* length */
+       34 - 1,                         /* length */
        OV2_REAL_MODE,
        0, 0,
        W(0xffffffff),                  /* real_base */
@@ -666,16 +711,16 @@ static unsigned char ibm_architecture_vec[] = {
        48,                             /* max log_2(hash table size) */
 
        /* option vector 3: processor options supported */
-       3 - 2,                          /* length */
+       3 - 1,                          /* length */
        0,                              /* don't ignore, don't halt */
        OV3_FP | OV3_VMX,
 
        /* option vector 4: IBM PAPR implementation */
-       2 - 2,                          /* length */
+       2 - 1,                          /* length */
        0,                              /* don't halt */
 
        /* option vector 5: PAPR/OF options */
-       3 - 2,                          /* length */
+       3 - 1,                          /* length */
        0,                              /* don't ignore, don't halt */
        OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES,
 };
@@ -989,7 +1034,7 @@ static void reserve_mem(u64 base, u64 size)
 }
 
 /*
- * Initialize memory allocation mechanism, parse "memory" nodes and
+ * Initialize memory allocation mecanism, parse "memory" nodes and
  * obtain that way the top of memory and RMO to setup out local allocator
  */
 static void __init prom_init_mem(void)
@@ -1070,6 +1115,29 @@ static void __init prom_init_mem(void)
                        RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end));
        }
 
+       /*
+        * If prom_memory_limit is set we reduce the upper limits *except* for
+        * alloc_top_high. This must be the real top of RAM so we can put
+        * TCE's up there.
+        */
+
+       RELOC(alloc_top_high) = RELOC(ram_top);
+
+       if (RELOC(prom_memory_limit)) {
+               if (RELOC(prom_memory_limit) <= RELOC(alloc_bottom)) {
+                       prom_printf("Ignoring mem=%x <= alloc_bottom.\n",
+                               RELOC(prom_memory_limit));
+                       RELOC(prom_memory_limit) = 0;
+               } else if (RELOC(prom_memory_limit) >= RELOC(ram_top)) {
+                       prom_printf("Ignoring mem=%x >= ram_top.\n",
+                               RELOC(prom_memory_limit));
+                       RELOC(prom_memory_limit) = 0;
+               } else {
+                       RELOC(ram_top) = RELOC(prom_memory_limit);
+                       RELOC(rmo_top) = min(RELOC(rmo_top), RELOC(prom_memory_limit));
+               }
+       }
+
        /*
         * Setup our top alloc point, that is top of RMO or top of
         * segment 0 when running non-LPAR.
@@ -1082,14 +1150,20 @@ static void __init prom_init_mem(void)
                RELOC(rmo_top) = RELOC(ram_top);
        RELOC(rmo_top) = min(0x30000000ul, RELOC(rmo_top));
        RELOC(alloc_top) = RELOC(rmo_top);
-       RELOC(alloc_top_high) = RELOC(ram_top);
 
        prom_printf("memory layout at init:\n");
+       prom_printf("  memory_limit : %x (16 MB aligned)\n", RELOC(prom_memory_limit));
        prom_printf("  alloc_bottom : %x\n", RELOC(alloc_bottom));
        prom_printf("  alloc_top    : %x\n", RELOC(alloc_top));
        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
 }
 
 
@@ -1275,10 +1349,16 @@ static void __init prom_initialize_tce_table(void)
 
        reserve_mem(local_alloc_bottom, local_alloc_top - local_alloc_bottom);
 
-       /* These are only really needed if there is a memory limit in
-        * effect, but we don't know so export them always. */
-       RELOC(prom_tce_alloc_start) = local_alloc_bottom;
-       RELOC(prom_tce_alloc_end) = local_alloc_top;
+       if (RELOC(prom_memory_limit)) {
+               /*
+                * We align the start to a 16MB boundary so we can map
+                * the TCE area using large pages if possible.
+                * The end should be the top of RAM so no need to align it.
+                */
+               RELOC(prom_tce_alloc_start) = _ALIGN_DOWN(local_alloc_bottom,
+                                                         0x1000000);
+               RELOC(prom_tce_alloc_end) = local_alloc_top;
+       }
 
        /* Flag the first invalid entry */
        prom_debug("ending prom_initialize_tce_table\n");
@@ -1961,7 +2041,11 @@ static void __init flatten_device_tree(void)
        /* Version 16 is not backward compatible */
        hdr->last_comp_version = 0x10;
 
-       /* Copy the reserve map in */
+       /* Reserve the whole thing and copy the reserve map in, we
+        * also bump mem_reserve_cnt to cause further reservations to
+        * fail since it's too late.
+        */
+       reserve_mem(RELOC(dt_header_start), hdr->totalsize);
        memcpy(rsvmap, RELOC(mem_reserve_map), sizeof(mem_reserve_map));
 
 #ifdef DEBUG_PROM
@@ -1974,9 +2058,6 @@ static void __init flatten_device_tree(void)
                                    RELOC(mem_reserve_map)[i].size);
        }
 #endif
-       /* Bump mem_reserve_cnt to cause further reservations to fail
-        * since it's too late.
-        */
        RELOC(mem_reserve_cnt) = MEM_RESERVE_MAP_SIZE;
 
        prom_printf("Device tree strings 0x%x -> 0x%x\n",
@@ -1992,22 +2073,12 @@ static void __init flatten_device_tree(void)
 static void __init fixup_device_tree_maple(void)
 {
        phandle isa;
-       u32 rloc = 0x01002000; /* IO space; PCI device = 4 */
        u32 isa_ranges[6];
-       char *name;
-
-       name = "/ht@0/isa@4";
-       isa = call_prom("finddevice", 1, 1, ADDR(name));
-       if (!PHANDLE_VALID(isa)) {
-               name = "/ht@0/isa@6";
-               isa = call_prom("finddevice", 1, 1, ADDR(name));
-               rloc = 0x01003000; /* IO space; PCI device = 6 */
-       }
+
+       isa = call_prom("finddevice", 1, 1, ADDR("/ht@0/isa@4"));
        if (!PHANDLE_VALID(isa))
                return;
 
-       if (prom_getproplen(isa, "ranges") != 12)
-               return;
        if (prom_getprop(isa, "ranges", isa_ranges, sizeof(isa_ranges))
                == PROM_ERROR)
                return;
@@ -2017,54 +2088,21 @@ static void __init fixup_device_tree_maple(void)
                isa_ranges[2] != 0x00010000)
                return;
 
-       prom_printf("Fixing up bogus ISA range on Maple/Apache...\n");
+       prom_printf("fixing up bogus ISA range on Maple...\n");
 
        isa_ranges[0] = 0x1;
        isa_ranges[1] = 0x0;
-       isa_ranges[2] = rloc;
+       isa_ranges[2] = 0x01002000; /* IO space; PCI device = 4 */
        isa_ranges[3] = 0x0;
        isa_ranges[4] = 0x0;
        isa_ranges[5] = 0x00010000;
-       prom_setprop(isa, name, "ranges",
+       prom_setprop(isa, "/ht@0/isa@4", "ranges",
                        isa_ranges, sizeof(isa_ranges));
 }
 #else
 #define fixup_device_tree_maple()
 #endif
 
-#ifdef CONFIG_PPC_CHRP
-/* Pegasos lacks the "ranges" property in the isa node */
-static void __init fixup_device_tree_chrp(void)
-{
-       phandle isa;
-       u32 isa_ranges[6];
-       char *name;
-       int rc;
-
-       name = "/pci@80000000/isa@c";
-       isa = call_prom("finddevice", 1, 1, ADDR(name));
-       if (!PHANDLE_VALID(isa))
-               return;
-
-       rc = prom_getproplen(isa, "ranges");
-       if (rc != 0 && rc != PROM_ERROR)
-               return;
-
-       prom_printf("Fixing up missing ISA range on Pegasos...\n");
-
-       isa_ranges[0] = 0x1;
-       isa_ranges[1] = 0x0;
-       isa_ranges[2] = 0x01006000;
-       isa_ranges[3] = 0x0;
-       isa_ranges[4] = 0x0;
-       isa_ranges[5] = 0x00010000;
-       prom_setprop(isa, name, "ranges",
-                       isa_ranges, sizeof(isa_ranges));
-}
-#else
-#define fixup_device_tree_chrp()
-#endif
-
 #if defined(CONFIG_PPC64) && defined(CONFIG_PPC_PMAC)
 static void __init fixup_device_tree_pmac(void)
 {
@@ -2112,7 +2150,6 @@ static void __init fixup_device_tree_pmac(void)
 static void __init fixup_device_tree(void)
 {
        fixup_device_tree_maple();
-       fixup_device_tree_chrp();
        fixup_device_tree_pmac();
 }
 
@@ -2243,6 +2280,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_
         */
@@ -2276,6 +2317,10 @@ 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, "/chosen", "linux,memory-limit",
+                            &RELOC(prom_memory_limit),
+                            sizeof(prom_memory_limit));
 #ifdef CONFIG_PPC64
        if (RELOC(ppc64_iommu_off))
                prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off",
@@ -2295,6 +2340,16 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
        }
 #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
         */