@@ -68,6 +68,15 @@ static void __init reserve_crashkernel(void)
}
#endif
+/*
+ * The main usage of linux,usable-memory-range is for crash dump kernel.
+ * Originally, the number of usable-memory regions is one. Now there may
+ * be two regions, low region and high region.
+ * To make compatibility with existing user-space and older kdump, the low
+ * region is always the last range of linux,usable-memory-range if exist.
+ */
+#define MAX_USABLE_RANGES 2
+
#ifdef CONFIG_CRASH_DUMP
static int __init early_init_dt_scan_elfcorehdr(unsigned long node,
const char *uname, int depth, void *data)
@@ -201,9 +210,9 @@ early_param("mem", early_mem);
static int __init early_init_dt_scan_usablemem(unsigned long node,
const char *uname, int depth, void *data)
{
- struct memblock_region *usablemem = data;
- const __be32 *reg;
- int len;
+ struct memblock_region *usable_rgns = data;
+ const __be32 *reg, *endp;
+ int len, nr = 0;
if (depth != 1 || strcmp(uname, "chosen") != 0)
return 0;
@@ -212,22 +221,36 @@ static int __init early_init_dt_scan_usablemem(unsigned long node,
if (!reg || (len < (dt_root_addr_cells + dt_root_size_cells)))
return 1;
- usablemem->base = dt_mem_next_cell(dt_root_addr_cells, ®);
- usablemem->size = dt_mem_next_cell(dt_root_size_cells, ®);
+ endp = reg + (len / sizeof(__be32));
+ while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+ usable_rgns[nr].base = dt_mem_next_cell(dt_root_addr_cells, ®);
+ usable_rgns[nr].size = dt_mem_next_cell(dt_root_size_cells, ®);
+
+ if (++nr >= MAX_USABLE_RANGES)
+ break;
+ }
return 1;
}
static void __init fdt_enforce_memory_region(void)
{
- struct memblock_region reg = {
- .size = 0,
+ struct memblock_region usable_rgns[MAX_USABLE_RANGES] = {
+ { .size = 0 },
+ { .size = 0 }
};
- of_scan_flat_dt(early_init_dt_scan_usablemem, ®);
+ of_scan_flat_dt(early_init_dt_scan_usablemem, &usable_rgns);
- if (reg.size)
- memblock_cap_memory_range(reg.base, reg.size);
+ /*
+ * The first range of usable-memory regions is for crash dump
+ * kernel with only one region or for high region with two regions,
+ * the second range is dedicated for low region if exist.
+ */
+ if (usable_rgns[0].size)
+ memblock_cap_memory_range(usable_rgns[0].base, usable_rgns[0].size);
+ if (usable_rgns[1].size)
+ memblock_add(usable_rgns[1].base, usable_rgns[1].size);
}
void __init arm64_memblock_init(void)