diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index e31fabed378a59..1d65320c6ba484 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) nr_ranges = 2; /* for exclusion of crashkernel region */ for_each_mem_range(i, &start, &end) nr_ranges++; + nr_ranges += of_reserved_mem_kdump_nr_ranges(); cmem = kmalloc_flex(*cmem, ranges, nr_ranges); if (!cmem) @@ -75,6 +77,10 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) goto out; } + ret = of_reserved_mem_kdump_exclude(cmem); + if (ret) + goto out; + ret = crash_prepare_elf64_headers(cmem, true, addr, sz); out: diff --git a/arch/loongarch/kernel/machine_kexec_file.c b/arch/loongarch/kernel/machine_kexec_file.c index 5584b798ba4645..4b918c3d4a2851 100644 --- a/arch/loongarch/kernel/machine_kexec_file.c +++ b/arch/loongarch/kernel/machine_kexec_file.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) nr_ranges = 2; /* for exclusion of crashkernel region */ for_each_mem_range(i, &start, &end) nr_ranges++; + nr_ranges += of_reserved_mem_kdump_nr_ranges(); cmem = kmalloc_flex(*cmem, ranges, nr_ranges); if (!cmem) @@ -91,6 +93,10 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) goto out; } + ret = of_reserved_mem_kdump_exclude(cmem); + if (ret) + goto out; + ret = crash_prepare_elf64_headers(cmem, true, addr, sz); out: diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c index 59d4bbc848a896..25359d583bc3b2 100644 --- a/arch/riscv/kernel/machine_kexec_file.c +++ b/arch/riscv/kernel/machine_kexec_file.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,7 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) nr_ranges = 1; /* For exclusion of crashkernel region */ walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback); + nr_ranges += of_reserved_mem_kdump_nr_ranges(); cmem = kmalloc_flex(*cmem, ranges, nr_ranges); if (!cmem) @@ -77,6 +79,8 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) /* Exclude crashkernel region */ ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end); + if (!ret) + ret = of_reserved_mem_kdump_exclude(cmem); if (!ret) ret = crash_prepare_elf64_headers(cmem, true, addr, sz); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 82f7327c59ea90..745e53b1c564d9 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -499,6 +499,7 @@ void __init early_init_fdt_scan_reserved_mem(void) int n; int res; u64 base, size; + int nr_memreserve = 0; if (!initial_boot_params) return; @@ -516,7 +517,9 @@ void __init early_init_fdt_scan_reserved_mem(void) if (!size) break; memblock_reserve(base, size); + nr_memreserve++; } + fdt_reserved_mem_account_memreserve(nr_memreserve); } /** @@ -1284,8 +1287,12 @@ void __init unflatten_device_tree(void) { void *fdt = initial_boot_params; - /* Save the statically-placed regions in the reserved_mem array */ - fdt_scan_reserved_mem_late(); + /* Attempt dynamic allocation of a new reserved_mem array */ + if (fdt && alloc_reserved_mem_array()) { + /* Save the statically-placed regions in the reserved_mem array */ + fdt_scan_reserved_mem_late(); + fdt_reserved_mem_save_memreserve_entries(); + } /* Populate an empty root node when bootloader doesn't provide one */ if (!fdt) { diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 0ae16da066e2c7..30954e85968966 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -187,6 +187,9 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node * int fdt_scan_reserved_mem(void); void __init fdt_scan_reserved_mem_late(void); +bool __init alloc_reserved_mem_array(void); +void __init fdt_reserved_mem_account_memreserve(int n); +void __init fdt_reserved_mem_save_memreserve_entries(void); bool of_fdt_device_is_available(const void *blob, unsigned long node); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 8d5777cb5d1b3d..1d9aa332325b74 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "of_private.h" @@ -69,29 +70,31 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, * the initial static array is copied over to this new array and * the new array is used from this point on. */ -static void __init alloc_reserved_mem_array(void) +bool __init alloc_reserved_mem_array(void) { struct reserved_mem *new_array; size_t alloc_size, copy_size, memset_size; + if (!total_reserved_mem_cnt) + return true; + alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array)); if (alloc_size == SIZE_MAX) { pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + goto fail; } new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES); if (!new_array) { pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM); - return; + goto fail; } copy_size = array_size(reserved_mem_count, sizeof(*new_array)); if (copy_size == SIZE_MAX) { memblock_free(new_array, alloc_size); - total_reserved_mem_cnt = MAX_RESERVED_REGIONS; pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + goto fail; } memset_size = alloc_size - copy_size; @@ -100,6 +103,11 @@ static void __init alloc_reserved_mem_array(void) memset(new_array + reserved_mem_count, 0, memset_size); reserved_mem = new_array; + return true; + +fail: + reserved_mem_count = 0; + return false; } static void fdt_init_reserved_mem_node(unsigned long node, const char *uname, @@ -241,6 +249,43 @@ static void __init __rmem_check_for_overlap(void) } } +static void __init fdt_reserved_mem_add_memreserve(phys_addr_t base, + phys_addr_t size) +{ + struct reserved_mem *rmem; + bool dumpable = false; + int i; + + if (reserved_mem_count == total_reserved_mem_cnt) { + pr_err("not enough space for memreserve regions.\n"); + return; + } + + for (i = 0; i < reserved_mem_count; i++) { + rmem = &reserved_mem[i]; + + if (!rmem->dumpable) + continue; + + if (base < rmem->base + rmem->size && rmem->base < base + size) { + dumpable = true; + break; + } + } + + rmem = &reserved_mem[reserved_mem_count]; + rmem->base = base; + rmem->size = size; + rmem->dumpable = dumpable; + + reserved_mem_count++; +} + +void __init fdt_reserved_mem_account_memreserve(int n) +{ + total_reserved_mem_cnt += n; +} + /** * fdt_scan_reserved_mem_late() - Scan FDT and initialize remaining reserved * memory regions. @@ -265,9 +310,6 @@ void __init fdt_scan_reserved_mem_late(void) return; } - /* Attempt dynamic allocation of a new reserved_mem array */ - alloc_reserved_mem_array(); - if (__reserved_mem_check_root(node)) { pr_err("Reserved memory: unsupported node format, ignoring\n"); return; @@ -301,6 +343,24 @@ void __init fdt_scan_reserved_mem_late(void) __rmem_check_for_overlap(); } +void __init fdt_reserved_mem_save_memreserve_entries(void) +{ + const void *fdt = initial_boot_params; + u64 base, size; + int n; + + if (!fdt) + return; + + for (n = 0; ; n++) { + if (fdt_get_mem_rsv(fdt, n, &base, &size)) + break; + if (!size) + break; + fdt_reserved_mem_add_memreserve(base, size); + } +} + static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname); /* @@ -321,11 +381,14 @@ int __init fdt_scan_reserved_mem(void) const void *fdt = initial_boot_params; node = fdt_path_offset(fdt, "/reserved-memory"); - if (node < 0) + if (node < 0) { + total_reserved_mem_cnt = 0; return -ENODEV; + } if (__reserved_mem_check_root(node) != 0) { pr_err("Reserved memory: unsupported node format, ignoring\n"); + total_reserved_mem_cnt = 0; return -EINVAL; } @@ -788,13 +851,47 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np) name = kbasename(np->full_name); for (i = 0; i < reserved_mem_count; i++) - if (!strcmp(reserved_mem[i].name, name)) + if (reserved_mem[i].name && + !strcmp(reserved_mem[i].name, name)) return &reserved_mem[i]; return NULL; } EXPORT_SYMBOL_GPL(of_reserved_mem_lookup); +/* + * Count non-dumpable reserved regions. Excluding each one may split a + * crash_mem range in two, callers use this to size the allocation. + */ +unsigned int of_reserved_mem_kdump_nr_ranges(void) +{ + unsigned int i, n = 0; + + for (i = 0; i < reserved_mem_count; i++) + if (reserved_mem[i].size && !reserved_mem[i].dumpable) + n++; + return n; +} + +/* Exclude non-dumpable reserved regions from @cmem. */ +int of_reserved_mem_kdump_exclude(struct crash_mem *cmem) +{ + unsigned int i; + int ret; + + for (i = 0; i < reserved_mem_count; i++) { + struct reserved_mem *r = &reserved_mem[i]; + + if (!r->size || r->dumpable) + continue; + ret = crash_exclude_mem_range(cmem, r->base, + r->base + r->size - 1); + if (ret) + return ret; + } + return 0; +} + /** * of_reserved_mem_region_to_resource() - Get a reserved memory region as a resource * @np: node containing 'memory-region' property diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h index c1dee3f971a918..0033d477764873 100644 --- a/include/linux/crash_core.h +++ b/include/linux/crash_core.h @@ -87,6 +87,12 @@ static inline int kexec_should_crash(struct task_struct *p) { return 0; } static inline int kexec_crash_loaded(void) { return 0; } static inline void crash_save_cpu(struct pt_regs *regs, int cpu) {}; static inline int kimage_crash_copy_vmcoreinfo(struct kimage *image) { return 0; }; +static inline int crash_exclude_mem_range(struct crash_mem *mem, + unsigned long long mstart, + unsigned long long mend) +{ + return 0; +} #endif /* CONFIG_CRASH_DUMP*/ #ifdef CONFIG_CRASH_DM_CRYPT diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index e8b20b29fa68c2..70db99f1fbff08 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -8,6 +8,7 @@ struct of_phandle_args; struct reserved_mem_ops; struct resource; +struct crash_mem; struct reserved_mem { const char *name; @@ -15,6 +16,7 @@ struct reserved_mem { phys_addr_t base; phys_addr_t size; void *priv; + bool dumpable; }; struct reserved_mem_ops { @@ -47,6 +49,9 @@ int of_reserved_mem_region_to_resource_byname(const struct device_node *np, const char *name, struct resource *res); int of_reserved_mem_region_count(const struct device_node *np); +unsigned int of_reserved_mem_kdump_nr_ranges(void); +int of_reserved_mem_kdump_exclude(struct crash_mem *cmem); + #else #define RESERVEDMEM_OF_DECLARE(name, compat, ops) \ @@ -91,6 +96,16 @@ static inline int of_reserved_mem_region_count(const struct device_node *np) { return 0; } + +static inline unsigned int of_reserved_mem_kdump_nr_ranges(void) +{ + return 0; +} + +static inline int of_reserved_mem_kdump_exclude(struct crash_mem *cmem) +{ + return 0; +} #endif /** diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index 03f52bd1712062..eddec89eb41465 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -579,6 +579,7 @@ static int __init rmem_cma_setup(unsigned long node, struct reserved_mem *rmem) dma_contiguous_default_area = cma; rmem->priv = cma; + rmem->dumpable = true; pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M);