Message ID | 159466087136.24747.16494497863685481495.stgit@hbathini.in.ibm.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | ppc64: enable kdump support for kexec_file_load syscall | expand |
Context | Check | Description |
---|---|---|
snowpatch_ozlabs/apply_patch | success | Successfully applied on branch powerpc/merge (71d6070a8e0e0a1ed82365544f97b86475cb161e) |
snowpatch_ozlabs/checkpatch | warning | total: 0 errors, 0 warnings, 7 checks, 423 lines checked |
snowpatch_ozlabs/needsstable | success | Patch has no Fixes tags |
Hello Hari, Hari Bathini <hbathini@linux.ibm.com> writes: > In kexec case, the kernel to be loaded uses the same memory layout as > the running kernel. So, passing on the DT of the running kernel would > be good enough. > > But in case of kdump, different memory ranges are needed to manage > loading the kdump kernel, booting into it and exporting the elfcore > of the crashing kernel. The ranges are exlude memory ranges, usable s/exlude/exclude/ > memory ranges, reserved memory ranges and crash memory ranges. > > Exclude memory ranges specify the list of memory ranges to avoid while > loading kdump segments. Usable memory ranges list the memory ranges > that could be used for booting kdump kernel. Reserved memory ranges > list the memory regions for the loading kernel's reserve map. Crash > memory ranges list the memory ranges to be exported as the crashing > kernel's elfcore. > > Add helper functions for setting up the above mentioned memory ranges. > This helpers facilitate in understanding the subsequent changes better > and make it easy to setup the different memory ranges listed above, as > and when appropriate. > > Signed-off-by: Hari Bathini <hbathini@linux.ibm.com> > Tested-by: Pingfan Liu <piliu@redhat.com> <snip> > +/** > + * get_mem_rngs_size - Get the allocated size of mrngs based on > + * max_nr_ranges and chunk size. > + * @mrngs: Memory ranges. > + * > + * Returns the maximum no. of ranges. This isn't correct. It 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); > +} <snip> > +/** > + * 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; > + > + for_each_node_by_type(dn, "pci") { > + u64 base; > + u32 size; > + > + ret = of_property_read_u64(dn, "linux,tce-base", &base); > + ret |= of_property_read_u32(dn, "linux,tce-size", &size); > + if (!ret) Shouldn't the condition be `ret` instead of `!ret`? > + 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; > + int ret = 0; > + char *str; > + > + /* 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; > +} > + > +/** > + * 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) > +{ > +#ifdef CONFIG_PPC_BOOK3S_64 > + int ret; > + > + if (!htab_address) > + return 0; > + > + ret = add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes); > + return ret; > +#else > + return 0; > +#endif > +} If I'm not mistaken, this is not the preferred way of having alternative implementations of a function. The "Conditional Compilation" section of the coding style document doesn't mention this directly, but does say that it's better to put the conditionals in a header file. In this case, I would do this in <asm/kexec_ranges.h> #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 And in ranges.c just surround the add_htab_mem_range() definition with #ifdef CONFIG_PPC_BOOK3S_64 and #endif Also, there's no need for the ret variable. You can just `return add_mem_range(...)` directly. > + > +/** > + * 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) > +{ > + int ret; > + > + ret = add_mem_range(mem_ranges, 0, __pa(_end)); > + return ret; > +} No need for the ret variable here, just `return add_mem_range()` directly. > + > +/** > + * 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) > + return ret; > + > + ret = add_mem_range(mem_ranges, base, size); You're missing an of_node_put(dn) here (also in the early return in the line above). > + } > + 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) > + return ret; > + > + ret = add_mem_range(mem_ranges, base, size); You're missing an of_node_put(dn) here (also in the early return in the line above). > + } > + 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 i, len, ret = 0; > + const __be32 *prop; > + > + prop = of_get_property(of_root, "reserved-ranges", &len); > + if (!prop) > + return 0; > + > + /* > + * Each reserved range is an (address,size) pair, 2 cells each, > + * totalling 4 cells per range. Can you assume that, or do you need to check the #address-cells and #size-cells properties of the root node? > + */ > + for (i = 0; i < len / (sizeof(*prop) * 4); i++) { > + u64 base, size; > + > + base = of_read_number(prop + (i * 4) + 0, 2); > + size = of_read_number(prop + (i * 4) + 2, 2); > + > + ret = add_mem_range(mem_ranges, base, size); > + if (ret) > + break; > + } > + > + return ret; > +} > + > +/** > + * 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) > +{ > + struct crash_mem_range *rngs; > + struct crash_mem_range rng; > + int i, j, idx; > + > + if (!mrngs) > + return; > + > + /* Sort the ranges in-place */ > + rngs = &mrngs->ranges[0]; > + for (i = 0; i < mrngs->nr_ranges; i++) { > + idx = i; > + for (j = (i + 1); j < mrngs->nr_ranges; j++) { > + if (rngs[idx].start > rngs[j].start) > + idx = j; > + } > + if (idx != i) { > + rng = rngs[idx]; > + rngs[idx] = rngs[i]; > + rngs[i] = rng; > + } > + } Would it work using sort() from lib/sort.c here? > + > + if (merge) > + __merge_memory_ranges(mrngs); > +} -- Thiago Jung Bauermann IBM Linux Technology Center
On 15/07/20 5:19 am, Thiago Jung Bauermann wrote: > <snip> > <snip> > >> +/** >> + * get_mem_rngs_size - Get the allocated size of mrngs based on >> + * max_nr_ranges and chunk size. >> + * @mrngs: Memory ranges. >> + * >> + * Returns the maximum no. of ranges. > > This isn't correct. It returns the maximum size of @mrngs. True. Will update.. > <snip> > >> +/** >> + * 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; >> + >> + for_each_node_by_type(dn, "pci") { >> + u64 base; >> + u32 size; >> + >> + ret = of_property_read_u64(dn, "linux,tce-base", &base); >> + ret |= of_property_read_u32(dn, "linux,tce-size", &size); >> + if (!ret) > > Shouldn't the condition be `ret` instead of `!ret`? Oops! Will fix it. >> +/** >> + * 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) >> +{ >> + struct crash_mem_range *rngs; >> + struct crash_mem_range rng; >> + int i, j, idx; >> + >> + if (!mrngs) >> + return; >> + >> + /* Sort the ranges in-place */ >> + rngs = &mrngs->ranges[0]; >> + for (i = 0; i < mrngs->nr_ranges; i++) { >> + idx = i; >> + for (j = (i + 1); j < mrngs->nr_ranges; j++) { >> + if (rngs[idx].start > rngs[j].start) >> + idx = j; >> + } >> + if (idx != i) { >> + rng = rngs[idx]; >> + rngs[idx] = rngs[i]; >> + rngs[i] = rng; >> + } >> + } > > Would it work using sort() from lib/sort.c here? Yeah. I think we could reuse it with a simple compare callback. Will do that. Thanks Hari
On 15/07/20 5:19 am, Thiago Jung Bauermann wrote: > > Hello Hari, > > Hari Bathini <hbathini@linux.ibm.com> writes: > >> In kexec case, the kernel to be loaded uses the same memory layout as >> the running kernel. So, passing on the DT of the running kernel would >> be good enough. >> >> But in case of kdump, different memory ranges are needed to manage >> loading the kdump kernel, booting into it and exporting the elfcore >> of the crashing kernel. The ranges are exlude memory ranges, usable > > s/exlude/exclude/ > >> memory ranges, reserved memory ranges and crash memory ranges. >> >> Exclude memory ranges specify the list of memory ranges to avoid while >> loading kdump segments. Usable memory ranges list the memory ranges >> that could be used for booting kdump kernel. Reserved memory ranges >> list the memory regions for the loading kernel's reserve map. Crash >> memory ranges list the memory ranges to be exported as the crashing >> kernel's elfcore. >> >> Add helper functions for setting up the above mentioned memory ranges. >> This helpers facilitate in understanding the subsequent changes better >> and make it easy to setup the different memory ranges listed above, as >> and when appropriate. >> >> Signed-off-by: Hari Bathini <hbathini@linux.ibm.com> >> Tested-by: Pingfan Liu <piliu@redhat.com> > <snip> >> +/** >> + * 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 i, len, ret = 0; >> + const __be32 *prop; >> + >> + prop = of_get_property(of_root, "reserved-ranges", &len); >> + if (!prop) >> + return 0; >> + >> + /* >> + * Each reserved range is an (address,size) pair, 2 cells each, >> + * totalling 4 cells per range. > > Can you assume that, or do you need to check the #address-cells and > #size-cells properties of the root node? Taken from early_reserve_mem_dt() which did not seem to care. Should we be doing any different here? Thanks Hari
On 17/07/20 10:02 am, Hari Bathini wrote: > > > On 15/07/20 5:19 am, Thiago Jung Bauermann wrote: >> >> Hello Hari, >> >> Hari Bathini <hbathini@linux.ibm.com> writes: >> >>> In kexec case, the kernel to be loaded uses the same memory layout as >>> the running kernel. So, passing on the DT of the running kernel would >>> be good enough. >>> >>> But in case of kdump, different memory ranges are needed to manage >>> loading the kdump kernel, booting into it and exporting the elfcore >>> of the crashing kernel. The ranges are exlude memory ranges, usable >> >> s/exlude/exclude/ >> >>> memory ranges, reserved memory ranges and crash memory ranges. >>> >>> Exclude memory ranges specify the list of memory ranges to avoid while >>> loading kdump segments. Usable memory ranges list the memory ranges >>> that could be used for booting kdump kernel. Reserved memory ranges >>> list the memory regions for the loading kernel's reserve map. Crash >>> memory ranges list the memory ranges to be exported as the crashing >>> kernel's elfcore. >>> >>> Add helper functions for setting up the above mentioned memory ranges. >>> This helpers facilitate in understanding the subsequent changes better >>> and make it easy to setup the different memory ranges listed above, as >>> and when appropriate. >>> >>> Signed-off-by: Hari Bathini <hbathini@linux.ibm.com> >>> Tested-by: Pingfan Liu <piliu@redhat.com> >> > > <snip> > >>> +/** >>> + * 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 i, len, ret = 0; >>> + const __be32 *prop; >>> + >>> + prop = of_get_property(of_root, "reserved-ranges", &len); >>> + if (!prop) >>> + return 0; >>> + >>> + /* >>> + * Each reserved range is an (address,size) pair, 2 cells each, >>> + * totalling 4 cells per range. >> >> Can you assume that, or do you need to check the #address-cells and >> #size-cells properties of the root node? > > Taken from early_reserve_mem_dt() which did not seem to care. > Should we be doing any different here? On second thoughts, wouldn't hurt to be extra cautious. Will use #address-cells & #size-cells to parse reserved-ranges. Thanks Hari
diff --git a/arch/powerpc/include/asm/kexec_ranges.h b/arch/powerpc/include/asm/kexec_ranges.h new file mode 100644 index 0000000..799dc40 --- /dev/null +++ b/arch/powerpc/include/asm/kexec_ranges.h @@ -0,0 +1,18 @@ +/* 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); +int add_htab_mem_range(struct crash_mem **mem_ranges); +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 67c3553..4aff684 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 0000000..a704819 --- /dev/null +++ b/arch/powerpc/kexec/ranges.c @@ -0,0 +1,397 @@ +// 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>. + */ + +#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 no. of ranges. + */ +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; + 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; + + for_each_node_by_type(dn, "pci") { + u64 base; + u32 size; + + ret = of_property_read_u64(dn, "linux,tce-base", &base); + ret |= of_property_read_u32(dn, "linux,tce-size", &size); + if (!ret) + 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; + int ret = 0; + char *str; + + /* 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; +} + +/** + * 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) +{ +#ifdef CONFIG_PPC_BOOK3S_64 + int ret; + + if (!htab_address) + return 0; + + ret = add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes); + return ret; +#else + return 0; +#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) +{ + int ret; + + ret = add_mem_range(mem_ranges, 0, __pa(_end)); + return ret; +} + +/** + * 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) + return ret; + + ret = add_mem_range(mem_ranges, base, size); + } + 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) + return ret; + + ret = add_mem_range(mem_ranges, base, size); + } + 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 i, len, ret = 0; + const __be32 *prop; + + prop = of_get_property(of_root, "reserved-ranges", &len); + if (!prop) + return 0; + + /* + * Each reserved range is an (address,size) pair, 2 cells each, + * totalling 4 cells per range. + */ + for (i = 0; i < len / (sizeof(*prop) * 4); i++) { + u64 base, size; + + base = of_read_number(prop + (i * 4) + 0, 2); + size = of_read_number(prop + (i * 4) + 2, 2); + + ret = add_mem_range(mem_ranges, base, size); + if (ret) + break; + } + + return ret; +} + +/** + * 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) +{ + struct crash_mem_range *rngs; + struct crash_mem_range rng; + int i, j, idx; + + if (!mrngs) + return; + + /* Sort the ranges in-place */ + rngs = &mrngs->ranges[0]; + for (i = 0; i < mrngs->nr_ranges; i++) { + idx = i; + for (j = (i + 1); j < mrngs->nr_ranges; j++) { + if (rngs[idx].start > rngs[j].start) + idx = j; + } + if (idx != i) { + rng = rngs[idx]; + rngs[idx] = rngs[i]; + rngs[i] = rng; + } + } + + if (merge) + __merge_memory_ranges(mrngs); +}