#undef DEBUG_PROM
#include <stdarg.h>
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
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;
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;
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 */
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
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 */
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,
};
}
/*
- * 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)
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.
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
}
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");
/* 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
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",
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;
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)
{
static void __init fixup_device_tree(void)
{
fixup_device_tree_maple();
- fixup_device_tree_chrp();
fixup_device_tree_pmac();
}
*/
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_
*/
/*
* 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",
}
#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
*/