Commit cf68787b authored by Benjamin Krill's avatar Benjamin Krill Committed by Benjamin Herrenschmidt

powerpc/prom_init: Evaluate mem kernel parameter for early allocation

Evaluate mem kernel parameter for early memory allocations. If mem is set
no allocation in the region above the given boundary is allowed. The current
code doesn't take care about this and allocate memory above the given mem
boundary.
Signed-off-by: default avatarBenjamin Krill <ben@codiert.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent a888ad45
...@@ -190,6 +190,8 @@ static int __initdata of_platform; ...@@ -190,6 +190,8 @@ static int __initdata of_platform;
static char __initdata prom_cmd_line[COMMAND_LINE_SIZE]; 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;
static unsigned long __initdata alloc_top_high; static unsigned long __initdata alloc_top_high;
static unsigned long __initdata alloc_bottom; static unsigned long __initdata alloc_bottom;
...@@ -484,6 +486,67 @@ static int __init prom_setprop(phandle node, const char *nodename, ...@@ -484,6 +486,67 @@ static int __init prom_setprop(phandle node, const char *nodename,
return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd); return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd);
} }
/* We can't use the standard versions because of RELOC headaches. */
#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
|| ('a' <= (c) && (c) <= 'f') \
|| ('A' <= (c) && (c) <= 'F'))
#define isdigit(c) ('0' <= (c) && (c) <= '9')
#define islower(c) ('a' <= (c) && (c) <= 'z')
#define toupper(c) (islower(c) ? ((c) - 'a' + 'A') : (c))
unsigned long prom_strtoul(const char *cp, const char **endp)
{
unsigned long result = 0, base = 10, value;
if (*cp == '0') {
base = 8;
cp++;
if (toupper(*cp) == 'X') {
cp++;
base = 16;
}
}
while (isxdigit(*cp) &&
(value = isdigit(*cp) ? *cp - '0' : toupper(*cp) - 'A' + 10) < base) {
result = result * base + value;
cp++;
}
if (endp)
*endp = cp;
return result;
}
unsigned long prom_memparse(const char *ptr, const char **retptr)
{
unsigned long ret = prom_strtoul(ptr, retptr);
int shift = 0;
/*
* We can't use a switch here because GCC *may* generate a
* jump table which won't work, because we're not running at
* the address we're linked at.
*/
if ('G' == **retptr || 'g' == **retptr)
shift = 30;
if ('M' == **retptr || 'm' == **retptr)
shift = 20;
if ('K' == **retptr || 'k' == **retptr)
shift = 10;
if (shift) {
ret <<= shift;
(*retptr)++;
}
return ret;
}
/* /*
* Early parsing of the command line passed to the kernel, used for * Early parsing of the command line passed to the kernel, used for
* "mem=x" and the options that affect the iommu * "mem=x" and the options that affect the iommu
...@@ -491,9 +554,8 @@ static int __init prom_setprop(phandle node, const char *nodename, ...@@ -491,9 +554,8 @@ static int __init prom_setprop(phandle node, const char *nodename,
static void __init early_cmdline_parse(void) static void __init early_cmdline_parse(void)
{ {
struct prom_t *_prom = &RELOC(prom); struct prom_t *_prom = &RELOC(prom);
#ifdef CONFIG_PPC64
const char *opt; const char *opt;
#endif
char *p; char *p;
int l = 0; int l = 0;
...@@ -521,6 +583,15 @@ static void __init early_cmdline_parse(void) ...@@ -521,6 +583,15 @@ static void __init early_cmdline_parse(void)
RELOC(prom_iommu_force_on) = 1; RELOC(prom_iommu_force_on) = 1;
} }
#endif #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_PPC_PSERIES #ifdef CONFIG_PPC_PSERIES
...@@ -1026,6 +1097,29 @@ static void __init prom_init_mem(void) ...@@ -1026,6 +1097,29 @@ static void __init prom_init_mem(void)
RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end)); 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 * Setup our top alloc point, that is top of RMO or top of
* segment 0 when running non-LPAR. * segment 0 when running non-LPAR.
...@@ -1041,6 +1135,7 @@ static void __init prom_init_mem(void) ...@@ -1041,6 +1135,7 @@ static void __init prom_init_mem(void)
RELOC(alloc_top_high) = RELOC(ram_top); RELOC(alloc_top_high) = RELOC(ram_top);
prom_printf("memory layout at init:\n"); 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_bottom : %x\n", RELOC(alloc_bottom));
prom_printf(" alloc_top : %x\n", RELOC(alloc_top)); prom_printf(" alloc_top : %x\n", RELOC(alloc_top));
prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high));
...@@ -2395,6 +2490,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, ...@@ -2395,6 +2490,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
/* /*
* Fill in some infos for use by the kernel later on * 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 #ifdef CONFIG_PPC64
if (RELOC(prom_iommu_off)) if (RELOC(prom_iommu_off))
prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off", prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment