Message ID | 159579222211.5790.10294144969496171475.stgit@hbathini (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | ppc64: enable kdump support for kexec_file_load syscall | expand |
Hi Hari, Some comments inline ... Hari Bathini <hbathini@linux.ibm.com> writes: > diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c > new file mode 100644 > index 000000000000..21bea1b78443 > --- /dev/null > +++ b/arch/powerpc/kexec/ranges.c > @@ -0,0 +1,417 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * powerpc code to implement the kexec_file_load syscall > + * > + * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) > + * Copyright (C) 2004 IBM Corp. > + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation > + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) > + * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) > + * Copyright (C) 2020 IBM Corporation > + * > + * Based on kexec-tools' kexec-ppc64.c, fs2dt.c. > + * Heavily modified for the kernel by > + * Hari Bathini <hbathini@linux.ibm.com>. Please just use your name, email addresses bit rot. It's in the commit log anyway. > + */ > + > +#undef DEBUG ^ Dont do that in new code please. > +#define pr_fmt(fmt) "kexec ranges: " fmt > + > +#include <linux/sort.h> > +#include <linux/kexec.h> > +#include <linux/of_device.h> > +#include <linux/slab.h> > +#include <asm/sections.h> > +#include <asm/kexec_ranges.h> > + > +/** > + * get_max_nr_ranges - Get the max no. of ranges crash_mem structure > + * could hold, given the size allocated for it. > + * @size: Allocation size of crash_mem structure. > + * > + * Returns the maximum no. of ranges. > + */ > +static inline unsigned int get_max_nr_ranges(size_t size) > +{ > + return ((size - sizeof(struct crash_mem)) / > + sizeof(struct crash_mem_range)); > +} > + > +/** > + * get_mem_rngs_size - Get the allocated size of mrngs based on > + * max_nr_ranges and chunk size. > + * @mrngs: Memory ranges. mrngs is not a great name, what about memory_ranges or ranges? Ditto everywhere else you use mrngs. > + * > + * Returns the maximum size of @mrngs. > + */ > +static inline size_t get_mem_rngs_size(struct crash_mem *mrngs) > +{ > + size_t size; > + > + if (!mrngs) > + return 0; > + > + size = (sizeof(struct crash_mem) + > + (mrngs->max_nr_ranges * sizeof(struct crash_mem_range))); > + > + /* > + * Memory is allocated in size multiple of MEM_RANGE_CHUNK_SZ. > + * So, align to get the actual length. > + */ > + return ALIGN(size, MEM_RANGE_CHUNK_SZ); > +} > + > +/** > + * __add_mem_range - add a memory range to memory ranges list. > + * @mem_ranges: Range list to add the memory range to. > + * @base: Base address of the range to add. > + * @size: Size of the memory range to add. > + * > + * (Re)allocates memory, if needed. > + * > + * Returns 0 on success, negative errno on error. > + */ > +static int __add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) > +{ > + struct crash_mem *mrngs = *mem_ranges; > + > + if ((mrngs == NULL) || (mrngs->nr_ranges == mrngs->max_nr_ranges)) { (mrngs == NULL) should just be !mrngs. > + mrngs = realloc_mem_ranges(mem_ranges); > + if (!mrngs) > + return -ENOMEM; > + } > + > + mrngs->ranges[mrngs->nr_ranges].start = base; > + mrngs->ranges[mrngs->nr_ranges].end = base + size - 1; > + pr_debug("Added memory range [%#016llx - %#016llx] at index %d\n", > + base, base + size - 1, mrngs->nr_ranges); > + mrngs->nr_ranges++; > + return 0; > +} > + > +/** > + * __merge_memory_ranges - Merges the given memory ranges list. > + * @mem_ranges: Range list to merge. > + * > + * Assumes a sorted range list. > + * > + * Returns nothing. > + */ A lot of this code is annoyingly similar to the memblock code, though the internals of that are all static these days. I guess for now we'll just have to add all this. Maybe in future it can be consolidated. > +static void __merge_memory_ranges(struct crash_mem *mrngs) > +{ > + struct crash_mem_range *rngs; > + int i, idx; > + > + if (!mrngs) > + return; > + > + idx = 0; > + rngs = &mrngs->ranges[0]; > + for (i = 1; i < mrngs->nr_ranges; i++) { > + if (rngs[i].start <= (rngs[i-1].end + 1)) > + rngs[idx].end = rngs[i].end; > + else { > + idx++; > + if (i == idx) > + continue; > + > + rngs[idx] = rngs[i]; > + } > + } > + mrngs->nr_ranges = idx + 1; > +} > + > +/** > + * realloc_mem_ranges - reallocate mem_ranges with size incremented > + * by MEM_RANGE_CHUNK_SZ. Frees up the old memory, > + * if memory allocation fails. > + * @mem_ranges: Memory ranges to reallocate. > + * > + * Returns pointer to reallocated memory on success, NULL otherwise. > + */ > +struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges) > +{ > + struct crash_mem *mrngs = *mem_ranges; > + unsigned int nr_ranges; > + size_t size; > + > + size = get_mem_rngs_size(mrngs); > + nr_ranges = mrngs ? mrngs->nr_ranges : 0; > + > + size += MEM_RANGE_CHUNK_SZ; > + mrngs = krealloc(*mem_ranges, size, GFP_KERNEL); > + if (!mrngs) { > + kfree(*mem_ranges); > + *mem_ranges = NULL; > + return NULL; > + } > + > + mrngs->nr_ranges = nr_ranges; > + mrngs->max_nr_ranges = get_max_nr_ranges(size); > + *mem_ranges = mrngs; > + > + return mrngs; > +} > + > +/** > + * add_mem_range - Updates existing memory range, if there is an overlap. > + * Else, adds a new memory range. > + * @mem_ranges: Range list to add the memory range to. > + * @base: Base address of the range to add. > + * @size: Size of the memory range to add. > + * > + * (Re)allocates memory, if needed. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) > +{ > + struct crash_mem *mrngs = *mem_ranges; > + u64 mstart, mend, end; > + unsigned int i; > + > + if (!size) > + return 0; > + > + end = base + size - 1; > + > + if ((mrngs == NULL) || (mrngs->nr_ranges == 0)) > + return __add_mem_range(mem_ranges, base, size); > + > + for (i = 0; i < mrngs->nr_ranges; i++) { > + mstart = mrngs->ranges[i].start; > + mend = mrngs->ranges[i].end; > + if (base < mend && end > mstart) { > + if (base < mstart) > + mrngs->ranges[i].start = base; > + if (end > mend) > + mrngs->ranges[i].end = end; > + return 0; > + } > + } > + > + return __add_mem_range(mem_ranges, base, size); > +} > + > +/** > + * add_tce_mem_ranges - Adds tce-table range to the given memory ranges list. > + * @mem_ranges: Range list to add the memory range(s) to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_tce_mem_ranges(struct crash_mem **mem_ranges) Not sure this and the other add_foo_mem_ranges() really belong in this patch. > +{ > + struct device_node *dn; > + int ret = 0; > + > + for_each_node_by_type(dn, "pci") { > + u64 base; > + u32 size; > + int rc; Do you really need ret and rc? > + /* > + * It is ok to have pci nodes without tce. So, ignore > + * any read errors here. > + */ > + rc = of_property_read_u64(dn, "linux,tce-base", &base); > + rc |= of_property_read_u32(dn, "linux,tce-size", &size); > + if (rc) > + continue; > + > + ret = add_mem_range(mem_ranges, base, size); > + if (ret) > + break; ^ dn leaked. > + } > + > + return ret; > +} > + > +/** > + * add_initrd_mem_range - Adds initrd range to the given memory ranges list, > + * if the initrd was retained. > + * @mem_ranges: Range list to add the memory range to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_initrd_mem_range(struct crash_mem **mem_ranges) > +{ > + u64 base, end; > + char *str; > + int ret; > + > + /* This range means something only if initrd was retained */ > + str = strstr(saved_command_line, "retain_initrd"); > + if (!str) > + return 0; Unfortunate that we have to go and scan the command line again. But I don't see a better way ATM. Could be more concise: if (!strstr(saved_command_line, "retain_initrd")) return 0; > + > + ret = of_property_read_u64(of_chosen, "linux,initrd-start", &base); > + ret |= of_property_read_u64(of_chosen, "linux,initrd-end", &end); > + if (!ret) > + ret = add_mem_range(mem_ranges, base, end - base + 1); > + return ret; > +} > + > +#ifdef CONFIG_PPC_BOOK3S_64 > +/** > + * add_htab_mem_range - Adds htab range to the given memory ranges list, > + * if it exists > + * @mem_ranges: Range list to add the memory range to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_htab_mem_range(struct crash_mem **mem_ranges) > +{ > + if (!htab_address) > + return 0; > + > + return add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes); > +} > +#endif > + > +/** > + * add_kernel_mem_range - Adds kernel text region to the given > + * memory ranges list. > + * @mem_ranges: Range list to add the memory range to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_kernel_mem_range(struct crash_mem **mem_ranges) > +{ > + return add_mem_range(mem_ranges, 0, __pa(_end)); > +} > + > +/** > + * add_rtas_mem_range - Adds RTAS region to the given memory ranges list. > + * @mem_ranges: Range list to add the memory range to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_rtas_mem_range(struct crash_mem **mem_ranges) > +{ > + struct device_node *dn; > + int ret = 0; > + > + dn = of_find_node_by_path("/rtas"); > + if (dn) { > + u32 base, size; > + > + ret = of_property_read_u32(dn, "linux,rtas-base", &base); > + ret |= of_property_read_u32(dn, "rtas-size", &size); > + if (ret) > + goto out; > + > + ret = add_mem_range(mem_ranges, base, size); > + } > + > +out: > + of_node_put(dn); > + return ret; > +} Or: struct device_node *dn; u32 base, size; int rc; dn = of_find_node_by_path("/rtas"); if (!dn) return 0; rc = of_property_read_u32(dn, "linux,rtas-base", &base); rc |= of_property_read_u32(dn, "rtas-size", &size); if (rc == 0) rc = add_mem_range(mem_ranges, base, size); of_node_put(dn); return rc; } > + > +/** > + * add_opal_mem_range - Adds OPAL region to the given memory ranges list. > + * @mem_ranges: Range list to add the memory range to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_opal_mem_range(struct crash_mem **mem_ranges) > +{ > + struct device_node *dn; > + int ret = 0; > + > + dn = of_find_node_by_path("/ibm,opal"); > + if (dn) { > + u64 base, size; > + > + ret = of_property_read_u64(dn, "opal-base-address", &base); > + ret |= of_property_read_u64(dn, "opal-runtime-size", &size); > + if (ret) > + goto out; > + > + ret = add_mem_range(mem_ranges, base, size); > + } > + > +out: > + of_node_put(dn); > + return ret; > +} > + > +/** > + * add_reserved_ranges - Adds "/reserved-ranges" regions exported by f/w > + * to the given memory ranges list. > + * @mem_ranges: Range list to add the memory ranges to. > + * > + * Returns 0 on success, negative errno on error. > + */ > +int add_reserved_ranges(struct crash_mem **mem_ranges) > +{ > + int n_mem_addr_cells, n_mem_size_cells, i, len, cells, ret = 0; > + const __be32 *prop; > + > + prop = of_get_property(of_root, "reserved-ranges", &len); > + if (!prop) > + return 0; > + > + of_node_get(of_root); You shouldn't need to get the root node, you already used it above anyway. > + n_mem_addr_cells = of_n_addr_cells(of_root); > + n_mem_size_cells = of_n_size_cells(of_root); > + cells = n_mem_addr_cells + n_mem_size_cells; > + > + /* Each reserved range is an (address,size) pair */ > + for (i = 0; i < (len / (sizeof(*prop) * cells)); i++) { ^ just u32 would be clearer I think. cheers
diff --git a/arch/powerpc/include/asm/kexec_ranges.h b/arch/powerpc/include/asm/kexec_ranges.h new file mode 100644 index 000000000000..78f3111e4e74 --- /dev/null +++ b/arch/powerpc/include/asm/kexec_ranges.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_POWERPC_KEXEC_RANGES_H +#define _ASM_POWERPC_KEXEC_RANGES_H + +#define MEM_RANGE_CHUNK_SZ 2048 /* Memory ranges size chunk */ + +struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges); +int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size); +int add_tce_mem_ranges(struct crash_mem **mem_ranges); +int add_initrd_mem_range(struct crash_mem **mem_ranges); +#ifdef CONFIG_PPC_BOOK3S_64 +int add_htab_mem_range(struct crash_mem **mem_ranges); +#else +static inline int add_htab_mem_range(struct crash_mem **mem_ranges) +{ + return 0; +} +#endif +int add_kernel_mem_range(struct crash_mem **mem_ranges); +int add_rtas_mem_range(struct crash_mem **mem_ranges); +int add_opal_mem_range(struct crash_mem **mem_ranges); +int add_reserved_ranges(struct crash_mem **mem_ranges); +void sort_memory_ranges(struct crash_mem *mrngs, bool merge); + +#endif /* _ASM_POWERPC_KEXEC_RANGES_H */ diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile index 67c355329457..4aff6846c772 100644 --- a/arch/powerpc/kexec/Makefile +++ b/arch/powerpc/kexec/Makefile @@ -7,7 +7,7 @@ obj-y += core.o crash.o core_$(BITS).o obj-$(CONFIG_PPC32) += relocate_32.o -obj-$(CONFIG_KEXEC_FILE) += file_load.o file_load_$(BITS).o elf_$(BITS).o +obj-$(CONFIG_KEXEC_FILE) += file_load.o ranges.o file_load_$(BITS).o elf_$(BITS).o ifdef CONFIG_HAVE_IMA_KEXEC ifdef CONFIG_IMA diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c new file mode 100644 index 000000000000..21bea1b78443 --- /dev/null +++ b/arch/powerpc/kexec/ranges.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * powerpc code to implement the kexec_file_load syscall + * + * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) + * Copyright (C) 2004 IBM Corp. + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) + * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) + * Copyright (C) 2020 IBM Corporation + * + * Based on kexec-tools' kexec-ppc64.c, fs2dt.c. + * Heavily modified for the kernel by + * Hari Bathini <hbathini@linux.ibm.com>. + */ + +#undef DEBUG +#define pr_fmt(fmt) "kexec ranges: " fmt + +#include <linux/sort.h> +#include <linux/kexec.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <asm/sections.h> +#include <asm/kexec_ranges.h> + +/** + * get_max_nr_ranges - Get the max no. of ranges crash_mem structure + * could hold, given the size allocated for it. + * @size: Allocation size of crash_mem structure. + * + * Returns the maximum no. of ranges. + */ +static inline unsigned int get_max_nr_ranges(size_t size) +{ + return ((size - sizeof(struct crash_mem)) / + sizeof(struct crash_mem_range)); +} + +/** + * get_mem_rngs_size - Get the allocated size of mrngs based on + * max_nr_ranges and chunk size. + * @mrngs: Memory ranges. + * + * Returns the maximum size of @mrngs. + */ +static inline size_t get_mem_rngs_size(struct crash_mem *mrngs) +{ + size_t size; + + if (!mrngs) + return 0; + + size = (sizeof(struct crash_mem) + + (mrngs->max_nr_ranges * sizeof(struct crash_mem_range))); + + /* + * Memory is allocated in size multiple of MEM_RANGE_CHUNK_SZ. + * So, align to get the actual length. + */ + return ALIGN(size, MEM_RANGE_CHUNK_SZ); +} + +/** + * __add_mem_range - add a memory range to memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * @base: Base address of the range to add. + * @size: Size of the memory range to add. + * + * (Re)allocates memory, if needed. + * + * Returns 0 on success, negative errno on error. + */ +static int __add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) +{ + struct crash_mem *mrngs = *mem_ranges; + + if ((mrngs == NULL) || (mrngs->nr_ranges == mrngs->max_nr_ranges)) { + mrngs = realloc_mem_ranges(mem_ranges); + if (!mrngs) + return -ENOMEM; + } + + mrngs->ranges[mrngs->nr_ranges].start = base; + mrngs->ranges[mrngs->nr_ranges].end = base + size - 1; + pr_debug("Added memory range [%#016llx - %#016llx] at index %d\n", + base, base + size - 1, mrngs->nr_ranges); + mrngs->nr_ranges++; + return 0; +} + +/** + * __merge_memory_ranges - Merges the given memory ranges list. + * @mem_ranges: Range list to merge. + * + * Assumes a sorted range list. + * + * Returns nothing. + */ +static void __merge_memory_ranges(struct crash_mem *mrngs) +{ + struct crash_mem_range *rngs; + int i, idx; + + if (!mrngs) + return; + + idx = 0; + rngs = &mrngs->ranges[0]; + for (i = 1; i < mrngs->nr_ranges; i++) { + if (rngs[i].start <= (rngs[i-1].end + 1)) + rngs[idx].end = rngs[i].end; + else { + idx++; + if (i == idx) + continue; + + rngs[idx] = rngs[i]; + } + } + mrngs->nr_ranges = idx + 1; +} + +/** + * realloc_mem_ranges - reallocate mem_ranges with size incremented + * by MEM_RANGE_CHUNK_SZ. Frees up the old memory, + * if memory allocation fails. + * @mem_ranges: Memory ranges to reallocate. + * + * Returns pointer to reallocated memory on success, NULL otherwise. + */ +struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges) +{ + struct crash_mem *mrngs = *mem_ranges; + unsigned int nr_ranges; + size_t size; + + size = get_mem_rngs_size(mrngs); + nr_ranges = mrngs ? mrngs->nr_ranges : 0; + + size += MEM_RANGE_CHUNK_SZ; + mrngs = krealloc(*mem_ranges, size, GFP_KERNEL); + if (!mrngs) { + kfree(*mem_ranges); + *mem_ranges = NULL; + return NULL; + } + + mrngs->nr_ranges = nr_ranges; + mrngs->max_nr_ranges = get_max_nr_ranges(size); + *mem_ranges = mrngs; + + return mrngs; +} + +/** + * add_mem_range - Updates existing memory range, if there is an overlap. + * Else, adds a new memory range. + * @mem_ranges: Range list to add the memory range to. + * @base: Base address of the range to add. + * @size: Size of the memory range to add. + * + * (Re)allocates memory, if needed. + * + * Returns 0 on success, negative errno on error. + */ +int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) +{ + struct crash_mem *mrngs = *mem_ranges; + u64 mstart, mend, end; + unsigned int i; + + if (!size) + return 0; + + end = base + size - 1; + + if ((mrngs == NULL) || (mrngs->nr_ranges == 0)) + return __add_mem_range(mem_ranges, base, size); + + for (i = 0; i < mrngs->nr_ranges; i++) { + mstart = mrngs->ranges[i].start; + mend = mrngs->ranges[i].end; + if (base < mend && end > mstart) { + if (base < mstart) + mrngs->ranges[i].start = base; + if (end > mend) + mrngs->ranges[i].end = end; + return 0; + } + } + + return __add_mem_range(mem_ranges, base, size); +} + +/** + * add_tce_mem_ranges - Adds tce-table range to the given memory ranges list. + * @mem_ranges: Range list to add the memory range(s) to. + * + * Returns 0 on success, negative errno on error. + */ +int add_tce_mem_ranges(struct crash_mem **mem_ranges) +{ + struct device_node *dn; + int ret = 0; + + for_each_node_by_type(dn, "pci") { + u64 base; + u32 size; + int rc; + + /* + * It is ok to have pci nodes without tce. So, ignore + * any read errors here. + */ + rc = of_property_read_u64(dn, "linux,tce-base", &base); + rc |= of_property_read_u32(dn, "linux,tce-size", &size); + if (rc) + continue; + + ret = add_mem_range(mem_ranges, base, size); + if (ret) + break; + } + + return ret; +} + +/** + * add_initrd_mem_range - Adds initrd range to the given memory ranges list, + * if the initrd was retained. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_initrd_mem_range(struct crash_mem **mem_ranges) +{ + u64 base, end; + char *str; + int ret; + + /* This range means something only if initrd was retained */ + str = strstr(saved_command_line, "retain_initrd"); + if (!str) + return 0; + + ret = of_property_read_u64(of_chosen, "linux,initrd-start", &base); + ret |= of_property_read_u64(of_chosen, "linux,initrd-end", &end); + if (!ret) + ret = add_mem_range(mem_ranges, base, end - base + 1); + return ret; +} + +#ifdef CONFIG_PPC_BOOK3S_64 +/** + * add_htab_mem_range - Adds htab range to the given memory ranges list, + * if it exists + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_htab_mem_range(struct crash_mem **mem_ranges) +{ + if (!htab_address) + return 0; + + return add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes); +} +#endif + +/** + * add_kernel_mem_range - Adds kernel text region to the given + * memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_kernel_mem_range(struct crash_mem **mem_ranges) +{ + return add_mem_range(mem_ranges, 0, __pa(_end)); +} + +/** + * add_rtas_mem_range - Adds RTAS region to the given memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_rtas_mem_range(struct crash_mem **mem_ranges) +{ + struct device_node *dn; + int ret = 0; + + dn = of_find_node_by_path("/rtas"); + if (dn) { + u32 base, size; + + ret = of_property_read_u32(dn, "linux,rtas-base", &base); + ret |= of_property_read_u32(dn, "rtas-size", &size); + if (ret) + goto out; + + ret = add_mem_range(mem_ranges, base, size); + } + +out: + of_node_put(dn); + return ret; +} + +/** + * add_opal_mem_range - Adds OPAL region to the given memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_opal_mem_range(struct crash_mem **mem_ranges) +{ + struct device_node *dn; + int ret = 0; + + dn = of_find_node_by_path("/ibm,opal"); + if (dn) { + u64 base, size; + + ret = of_property_read_u64(dn, "opal-base-address", &base); + ret |= of_property_read_u64(dn, "opal-runtime-size", &size); + if (ret) + goto out; + + ret = add_mem_range(mem_ranges, base, size); + } + +out: + of_node_put(dn); + return ret; +} + +/** + * add_reserved_ranges - Adds "/reserved-ranges" regions exported by f/w + * to the given memory ranges list. + * @mem_ranges: Range list to add the memory ranges to. + * + * Returns 0 on success, negative errno on error. + */ +int add_reserved_ranges(struct crash_mem **mem_ranges) +{ + int n_mem_addr_cells, n_mem_size_cells, i, len, cells, ret = 0; + const __be32 *prop; + + prop = of_get_property(of_root, "reserved-ranges", &len); + if (!prop) + return 0; + + of_node_get(of_root); + n_mem_addr_cells = of_n_addr_cells(of_root); + n_mem_size_cells = of_n_size_cells(of_root); + cells = n_mem_addr_cells + n_mem_size_cells; + + /* Each reserved range is an (address,size) pair */ + for (i = 0; i < (len / (sizeof(*prop) * cells)); i++) { + u64 base, size; + + base = of_read_number(prop + (i * cells), n_mem_addr_cells); + size = of_read_number(prop + (i * cells) + n_mem_addr_cells, + n_mem_size_cells); + + ret = add_mem_range(mem_ranges, base, size); + if (ret) + break; + } + + of_node_put(of_root); + return ret; +} + +/* cmp_func_t callback to sort ranges with sort() */ +static int rngcmp(const void *_x, const void *_y) +{ + const struct crash_mem_range *x = _x, *y = _y; + + if (x->start > y->start) + return 1; + if (x->start < y->start) + return -1; + return 0; +} + +/** + * sort_memory_ranges - Sorts the given memory ranges list. + * @mem_ranges: Range list to sort. + * @merge: If true, merge the list after sorting. + * + * Returns nothing. + */ +void sort_memory_ranges(struct crash_mem *mrngs, bool merge) +{ + int i; + + if (!mrngs) + return; + + /* Sort the ranges in-place */ + sort(&(mrngs->ranges[0]), mrngs->nr_ranges, sizeof(mrngs->ranges[0]), + rngcmp, NULL); + + if (merge) + __merge_memory_ranges(mrngs); + + /* For debugging purpose */ + pr_debug("Memory ranges:\n"); + for (i = 0; i < mrngs->nr_ranges; i++) { + pr_debug("\t[%03d][%#016llx - %#016llx]\n", i, + mrngs->ranges[i].start, + mrngs->ranges[i].end); + } +}