Commit 60296c71 authored by Lennert Buytenhek's avatar Lennert Buytenhek Committed by Lennert Buytenhek

[ARM] prevent crashing when too much RAM installed

This patch will truncate and/or ignore memory banks if their kernel
direct mappings would (partially) overlap with the vmalloc area or
the mappings between the vmalloc area and the address space top, to
prevent crashing during early boot if there happens to be more RAM
installed than we are expecting.

Since the start of the vmalloc area is not at a fixed address (but
the vmalloc end address is, via the per-platform VMALLOC_END define),
a default area of 128M is reserved for vmalloc mappings, which can
be shrunk or enlarged by passing an appropriate vmalloc= command line
option as it is done on x86.

On a board with a 3:1 user:kernel split, VMALLOC_END at 0xfe000000,
two 512M RAM banks and vmalloc=128M (the default), this patch gives:

	Truncating RAM at 20000000-3fffffff to -35ffffff (vmalloc region overlap).
	Memory: 512MB 352MB = 864MB total

On a board with a 3:1 user:kernel split, VMALLOC_END at 0xfe800000,
two 256M RAM banks and vmalloc=768M, this patch gives:

	Truncating RAM at 00000000-0fffffff to -0e7fffff (vmalloc region overlap).
	Ignoring RAM at 10000000-1fffffff (vmalloc region overlap).
Signed-off-by: default avatarLennert Buytenhek <buytenh@marvell.com>
Tested-by: default avatarRiku Voipio <riku.voipio@iki.fi>
parent 09c0ed2e
...@@ -149,6 +149,14 @@ ...@@ -149,6 +149,14 @@
#define arch_adjust_zones(node,size,holes) do { } while (0) #define arch_adjust_zones(node,size,holes) do { } while (0)
#endif #endif
/*
* Amount of memory reserved for the vmalloc() area, and minimum
* address for vmalloc mappings.
*/
extern unsigned long vmalloc_reserve;
#define VMALLOC_MIN (void *)(VMALLOC_END - vmalloc_reserve)
/* /*
* PFNs are used to describe any physical page; this means * PFNs are used to describe any physical page; this means
* PFN 0 == physical address 0. * PFN 0 == physical address 0.
......
...@@ -81,6 +81,8 @@ EXPORT_SYMBOL(system_serial_high); ...@@ -81,6 +81,8 @@ EXPORT_SYMBOL(system_serial_high);
unsigned int elf_hwcap; unsigned int elf_hwcap;
EXPORT_SYMBOL(elf_hwcap); EXPORT_SYMBOL(elf_hwcap);
unsigned long __initdata vmalloc_reserve = 128 << 20;
#ifdef MULTI_CPU #ifdef MULTI_CPU
struct processor processor; struct processor processor;
...@@ -500,6 +502,17 @@ static void __init early_mem(char **p) ...@@ -500,6 +502,17 @@ static void __init early_mem(char **p)
} }
__early_param("mem=", early_mem); __early_param("mem=", early_mem);
/*
* vmalloc=size forces the vmalloc area to be exactly 'size'
* bytes. This can be used to increase (or decrease) the vmalloc
* area - the default is 128m.
*/
static void __init early_vmalloc(char **arg)
{
vmalloc_reserve = memparse(*arg, arg);
}
__early_param("vmalloc=", early_vmalloc);
/* /*
* Initial parsing of the command line. * Initial parsing of the command line.
*/ */
......
...@@ -568,6 +568,55 @@ void __init iotable_init(struct map_desc *io_desc, int nr) ...@@ -568,6 +568,55 @@ void __init iotable_init(struct map_desc *io_desc, int nr)
create_mapping(io_desc + i); create_mapping(io_desc + i);
} }
static int __init check_membank_valid(struct membank *mb)
{
/*
* Check whether this memory region has non-zero size.
*/
if (mb->size == 0)
return 0;
/*
* Check whether this memory region would entirely overlap
* the vmalloc area.
*/
if (phys_to_virt(mb->start) >= VMALLOC_MIN) {
printk(KERN_NOTICE "Ignoring RAM at %.8lx-%.8lx "
"(vmalloc region overlap).\n",
mb->start, mb->start + mb->size - 1);
return 0;
}
/*
* Check whether this memory region would partially overlap
* the vmalloc area.
*/
if (phys_to_virt(mb->start + mb->size) < phys_to_virt(mb->start) ||
phys_to_virt(mb->start + mb->size) > VMALLOC_MIN) {
unsigned long newsize = VMALLOC_MIN - phys_to_virt(mb->start);
printk(KERN_NOTICE "Truncating RAM at %.8lx-%.8lx "
"to -%.8lx (vmalloc region overlap).\n",
mb->start, mb->start + mb->size - 1,
mb->start + newsize - 1);
mb->size = newsize;
}
return 1;
}
static void __init sanity_check_meminfo(struct meminfo *mi)
{
int i;
int j;
for (i = 0, j = 0; i < mi->nr_banks; i++) {
if (check_membank_valid(&mi->bank[i]))
mi->bank[j++] = mi->bank[i];
}
mi->nr_banks = j;
}
static inline void prepare_page_table(struct meminfo *mi) static inline void prepare_page_table(struct meminfo *mi)
{ {
unsigned long addr; unsigned long addr;
...@@ -753,6 +802,7 @@ void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc) ...@@ -753,6 +802,7 @@ void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
void *zero_page; void *zero_page;
build_mem_type_table(); build_mem_type_table();
sanity_check_meminfo(mi);
prepare_page_table(mi); prepare_page_table(mi);
bootmem_init(mi); bootmem_init(mi);
devicemaps_init(mdesc); devicemaps_init(mdesc);
......
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