diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2012-11-15 12:53:59 +0100 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-11-23 18:44:37 +0100 |
commit | 0ec7ec75f674dbf48010081383263771f2611e72 (patch) | |
tree | b2d3e71922151ad92074ff7e2f94ae33de5ed185 | |
parent | f4a75d2eb7b1e2206094b901be09adb31ba63681 (diff) | |
download | blackbird-op-linux-0ec7ec75f674dbf48010081383263771f2611e72.tar.gz blackbird-op-linux-0ec7ec75f674dbf48010081383263771f2611e72.zip |
MIPS: Merge overlapping bootmem ranges
Without this, we may end up with something like this in /proc/iomem:
01100000-014fffff : System RAM
01100000-013bf48f : Kernel code
013bf490-0149e01f : Kernel data
01500000-0c0fffff : System RAM
but the two System RAM ranges should be one single range. This particular
case will result in kexec failure on Octeon systems if the kernel being
loaded by kexec is bigger than the already running kernel.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/kernel/setup.c | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index a53f8ec37aac..290dc6a1d7a3 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -79,7 +79,7 @@ static struct resource data_resource = { .name = "Kernel data", }; void __init add_memory_region(phys_t start, phys_t size, long type) { int x = boot_mem_map.nr_map; - struct boot_mem_map_entry *prev = boot_mem_map.map + x - 1; + int i; /* Sanity check */ if (start + size < start) { @@ -88,15 +88,29 @@ void __init add_memory_region(phys_t start, phys_t size, long type) } /* - * Try to merge with previous entry if any. This is far less than - * perfect but is sufficient for most real world cases. + * Try to merge with existing entry, if any. */ - if (x && prev->addr + prev->size == start && prev->type == type) { - prev->size += size; + for (i = 0; i < boot_mem_map.nr_map; i++) { + struct boot_mem_map_entry *entry = boot_mem_map.map + i; + unsigned long top; + + if (entry->type != type) + continue; + + if (start + size < entry->addr) + continue; /* no overlap */ + + if (entry->addr + entry->size < start) + continue; /* no overlap */ + + top = max(entry->addr + entry->size, start + size); + entry->addr = min(entry->addr, start); + entry->size = top - entry->addr; + return; } - if (x == BOOT_MEM_MAP_MAX) { + if (boot_mem_map.nr_map == BOOT_MEM_MAP_MAX) { pr_err("Ooops! Too many entries in the memory map!\n"); return; } |