[PATCH 05/21] MIPS memblock: Alter initrd memory reservation method

From: Serge Semin
Date: Sun Dec 18 2016 - 21:18:16 EST


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