Since memblock is used, initrd memory region can be easily verified and reserved if looks ok. Verification method will be useful for other reservation methods. Signed-off-by: Serge Semin <fancer.lancer@xxxxxxxxx> --- arch/mips/kernel/setup.c | 157 ++++++++++++++++------------- 1 file changed, 87 insertions(+), 70 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 789aafe..d2f38ac 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -82,6 +82,8 @@ static struct resource data_resource = { .name = "Kernel data", }; static void *detect_magic __initdata = detect_memory_region; +static phys_addr_t __initdata mips_lowmem_limit; + /* * General method to add RAM regions to the system * @@ -266,6 +268,38 @@ static int __init early_parse_mem(char *p) early_param("mem", early_parse_mem); /* + * Helper method checking whether passed lowmem region is valid + */ +static bool __init is_lowmem_and_valid(const char *name, phys_addr_t base, + phys_addr_t size) +{ + phys_addr_t end = base + size; + + /* Check whether region belongs to actual memory */ + if (!memblock_is_region_memory(base, size)) { + pr_err("%s %08zx @ %pa is not a memory region", name, + (size_t)size, &base); + return false; + } + + /* Check whether region belongs to low memory */ + if (end > mips_lowmem_limit) { + pr_err("%s %08zx @ %pa is out of low memory", name, + (size_t)size, &base); + return false; + } + + /* Check whether region is free */ + if (memblock_is_region_reserved(base, size)) { + pr_err("%s %08zx @ %pa overlaps in-use memory", name, + (size_t)size, &base); + return false; + } + + return true; +} + +/* * Manage initrd */ #ifdef CONFIG_BLK_DEV_INITRD @@ -292,47 +326,6 @@ static int __init rd_size_early(char *p) } early_param("rd_size", rd_size_early); -/* it returns the next free pfn after initrd */ -static unsigned long __init init_initrd(void) -{ - unsigned long end; - - /* - * Board specific code or command line parser should have - * already set up initrd_start and initrd_end. In these cases - * perfom sanity checks and use them if all looks good. - */ - if (!initrd_start || initrd_end <= initrd_start) - goto disable; - - if (initrd_start & ~PAGE_MASK) { - pr_err("initrd start must be page aligned\n"); - goto disable; - } - if (initrd_start < PAGE_OFFSET) { - pr_err("initrd start < PAGE_OFFSET\n"); - goto disable; - } - - /* - * Sanitize initrd addresses. For example firmware - * can't guess if they need to pass them through - * 64-bits values if the kernel has been built in pure - * 32-bit. We need also to switch from KSEG0 to XKPHYS - * addresses now, so the code can now safely use __pa(). - */ - end = __pa(initrd_end); - initrd_end = (unsigned long)__va(end); - initrd_start = (unsigned long)__va(__pa(initrd_start)); - - ROOT_DEV = Root_RAM0; - return PFN_UP(end); -disable: - initrd_start = 0; - initrd_end = 0; - return 0; -} - /* In some conditions (e.g. big endian bootloader with a little endian kernel), the initrd might appear byte swapped. Try to detect this and byte swap it if needed. */ @@ -362,26 +355,64 @@ static void __init maybe_bswap_initrd(void) #endif } -static void __init finalize_initrd(void) +/* + * Check and reserve memory occupied by initrd + */ +static void __init mips_reserve_initrd_mem(void) { - unsigned long size = initrd_end - initrd_start; + phys_addr_t phys_initrd_start, phys_initrd_end, phys_initrd_size; - if (size == 0) { - printk(KERN_INFO "Initrd not found or empty"); + /* + * Board specific code or command line parser should have already set + * up initrd_start and initrd_end. In these cases perform sanity checks + * and use them if all looks good. + */ + if (!initrd_start || initrd_end <= initrd_start) { + pr_info("No initrd found"); goto disable; } - if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) { - printk(KERN_ERR "Initrd extends beyond end of memory"); + if (initrd_start & ~PAGE_MASK) { + pr_err("Initrd start must be page aligned"); goto disable; } + if (initrd_start < PAGE_OFFSET) { + pr_err("Initrd start < PAGE_OFFSET"); + goto disable; + } + + /* + * Sanitize initrd addresses. For example firmware can't guess if they + * need to pass them through 64-bits values if the kernel has been + * built in pure 32-bit. We need also to switch from KSEG0 to XKPHYS + * addresses now, so the code can now safely use __pa(). + */ + phys_initrd_start = __pa(initrd_start); + phys_initrd_end = __pa(initrd_end); + phys_initrd_size = phys_initrd_end - phys_initrd_start; + /* Check whether initrd region is within available lowmem and free */ + if (!is_lowmem_and_valid("Initrd", phys_initrd_start, phys_initrd_size)) + goto disable; + + /* Initrd may be byteswapped in Octeon */ maybe_bswap_initrd(); - reserve_bootmem(__pa(initrd_start), size, BOOTMEM_DEFAULT); + /* Memory for initrd can be reserved now */ + memblock_reserve(phys_initrd_start, phys_initrd_size); + + /* Convert initrd to virtual addresses back (needed for x32 -> x64) */ + initrd_start = (unsigned long)__va(phys_initrd_start); + initrd_end = (unsigned long)__va(phys_initrd_end); + + /* It's OK to have initrd below actual memory start. Really? */ initrd_below_start_ok = 1; - pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n", - initrd_start, size); + pr_info("Initial ramdisk at: 0x%lx (%zu bytes)\n", + initrd_start, (size_t)phys_initrd_size); + + /* Set root device to be first ram disk */ + ROOT_DEV = Root_RAM0; + return; disable: printk(KERN_CONT " - disabling initrd\n"); @@ -391,12 +422,7 @@ disable: #else /* !CONFIG_BLK_DEV_INITRD */ -static unsigned long __init init_initrd(void) -{ - return 0; -} - -#define finalize_initrd() do {} while (0) +static void __init mips_reserve_initrd_mem(void) { } #endif @@ -408,8 +434,8 @@ static unsigned long __init init_initrd(void) static void __init bootmem_init(void) { - init_initrd(); - finalize_initrd(); + /* Check and reserve memory occupied by initrd */ + mips_reserve_initrd_mem(); } #else /* !CONFIG_SGI_IP27 */ @@ -421,13 +447,9 @@ static void __init bootmem_init(void) unsigned long bootmap_size; int i; - /* - * Sanity check any INITRD first. We don't take it into account - * for bootmem setup initially, rely on the end-of-kernel-code - * as our memory range starting point. Once bootmem is inited we - * will reserve the area used for the initrd. - */ - init_initrd(); + /* Check and reserve memory occupied by initrd */ + mips_reserve_initrd_mem(); + reserved_end = (unsigned long) PFN_UP(__pa_symbol(&_end)); /* @@ -618,11 +640,6 @@ static void __init bootmem_init(void) #endif } #endif - - /* - * Reserve initrd memory if needed. - */ - finalize_initrd(); } #endif /* CONFIG_SGI_IP27 */ -- 2.6.6