162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * powerpc code to implement the kexec_file_load syscall 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) 662306a36Sopenharmony_ci * Copyright (C) 2004 IBM Corp. 762306a36Sopenharmony_ci * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation 862306a36Sopenharmony_ci * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) 962306a36Sopenharmony_ci * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) 1062306a36Sopenharmony_ci * Copyright (C) 2020 IBM Corporation 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Based on kexec-tools' kexec-ppc64.c, fs2dt.c. 1362306a36Sopenharmony_ci * Heavily modified for the kernel by 1462306a36Sopenharmony_ci * Hari Bathini, IBM Corporation. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) "kexec ranges: " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/sort.h> 2062306a36Sopenharmony_ci#include <linux/kexec.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <asm/sections.h> 2462306a36Sopenharmony_ci#include <asm/kexec_ranges.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/** 2762306a36Sopenharmony_ci * get_max_nr_ranges - Get the max no. of ranges crash_mem structure 2862306a36Sopenharmony_ci * could hold, given the size allocated for it. 2962306a36Sopenharmony_ci * @size: Allocation size of crash_mem structure. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Returns the maximum no. of ranges. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic inline unsigned int get_max_nr_ranges(size_t size) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return ((size - sizeof(struct crash_mem)) / 3662306a36Sopenharmony_ci sizeof(struct range)); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * get_mem_rngs_size - Get the allocated size of mem_rngs based on 4162306a36Sopenharmony_ci * max_nr_ranges and chunk size. 4262306a36Sopenharmony_ci * @mem_rngs: Memory ranges. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Returns the maximum size of @mem_rngs. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic inline size_t get_mem_rngs_size(struct crash_mem *mem_rngs) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci size_t size; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (!mem_rngs) 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci size = (sizeof(struct crash_mem) + 5462306a36Sopenharmony_ci (mem_rngs->max_nr_ranges * sizeof(struct range))); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * Memory is allocated in size multiple of MEM_RANGE_CHUNK_SZ. 5862306a36Sopenharmony_ci * So, align to get the actual length. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci return ALIGN(size, MEM_RANGE_CHUNK_SZ); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * __add_mem_range - add a memory range to memory ranges list. 6562306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 6662306a36Sopenharmony_ci * @base: Base address of the range to add. 6762306a36Sopenharmony_ci * @size: Size of the memory range to add. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * (Re)allocates memory, if needed. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic int __add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct crash_mem *mem_rngs = *mem_ranges; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!mem_rngs || (mem_rngs->nr_ranges == mem_rngs->max_nr_ranges)) { 7862306a36Sopenharmony_ci mem_rngs = realloc_mem_ranges(mem_ranges); 7962306a36Sopenharmony_ci if (!mem_rngs) 8062306a36Sopenharmony_ci return -ENOMEM; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci mem_rngs->ranges[mem_rngs->nr_ranges].start = base; 8462306a36Sopenharmony_ci mem_rngs->ranges[mem_rngs->nr_ranges].end = base + size - 1; 8562306a36Sopenharmony_ci pr_debug("Added memory range [%#016llx - %#016llx] at index %d\n", 8662306a36Sopenharmony_ci base, base + size - 1, mem_rngs->nr_ranges); 8762306a36Sopenharmony_ci mem_rngs->nr_ranges++; 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * __merge_memory_ranges - Merges the given memory ranges list. 9362306a36Sopenharmony_ci * @mem_rngs: Range list to merge. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Assumes a sorted range list. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * Returns nothing. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic void __merge_memory_ranges(struct crash_mem *mem_rngs) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct range *ranges; 10262306a36Sopenharmony_ci int i, idx; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!mem_rngs) 10562306a36Sopenharmony_ci return; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci idx = 0; 10862306a36Sopenharmony_ci ranges = &(mem_rngs->ranges[0]); 10962306a36Sopenharmony_ci for (i = 1; i < mem_rngs->nr_ranges; i++) { 11062306a36Sopenharmony_ci if (ranges[i].start <= (ranges[i-1].end + 1)) 11162306a36Sopenharmony_ci ranges[idx].end = ranges[i].end; 11262306a36Sopenharmony_ci else { 11362306a36Sopenharmony_ci idx++; 11462306a36Sopenharmony_ci if (i == idx) 11562306a36Sopenharmony_ci continue; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ranges[idx] = ranges[i]; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci mem_rngs->nr_ranges = idx + 1; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* cmp_func_t callback to sort ranges with sort() */ 12462306a36Sopenharmony_cistatic int rngcmp(const void *_x, const void *_y) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci const struct range *x = _x, *y = _y; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (x->start > y->start) 12962306a36Sopenharmony_ci return 1; 13062306a36Sopenharmony_ci if (x->start < y->start) 13162306a36Sopenharmony_ci return -1; 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * sort_memory_ranges - Sorts the given memory ranges list. 13762306a36Sopenharmony_ci * @mem_rngs: Range list to sort. 13862306a36Sopenharmony_ci * @merge: If true, merge the list after sorting. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Returns nothing. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_civoid sort_memory_ranges(struct crash_mem *mem_rngs, bool merge) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!mem_rngs) 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Sort the ranges in-place */ 15062306a36Sopenharmony_ci sort(&(mem_rngs->ranges[0]), mem_rngs->nr_ranges, 15162306a36Sopenharmony_ci sizeof(mem_rngs->ranges[0]), rngcmp, NULL); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (merge) 15462306a36Sopenharmony_ci __merge_memory_ranges(mem_rngs); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* For debugging purpose */ 15762306a36Sopenharmony_ci pr_debug("Memory ranges:\n"); 15862306a36Sopenharmony_ci for (i = 0; i < mem_rngs->nr_ranges; i++) { 15962306a36Sopenharmony_ci pr_debug("\t[%03d][%#016llx - %#016llx]\n", i, 16062306a36Sopenharmony_ci mem_rngs->ranges[i].start, 16162306a36Sopenharmony_ci mem_rngs->ranges[i].end); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * realloc_mem_ranges - reallocate mem_ranges with size incremented 16762306a36Sopenharmony_ci * by MEM_RANGE_CHUNK_SZ. Frees up the old memory, 16862306a36Sopenharmony_ci * if memory allocation fails. 16962306a36Sopenharmony_ci * @mem_ranges: Memory ranges to reallocate. 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * Returns pointer to reallocated memory on success, NULL otherwise. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistruct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct crash_mem *mem_rngs = *mem_ranges; 17662306a36Sopenharmony_ci unsigned int nr_ranges; 17762306a36Sopenharmony_ci size_t size; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci size = get_mem_rngs_size(mem_rngs); 18062306a36Sopenharmony_ci nr_ranges = mem_rngs ? mem_rngs->nr_ranges : 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci size += MEM_RANGE_CHUNK_SZ; 18362306a36Sopenharmony_ci mem_rngs = krealloc(*mem_ranges, size, GFP_KERNEL); 18462306a36Sopenharmony_ci if (!mem_rngs) { 18562306a36Sopenharmony_ci kfree(*mem_ranges); 18662306a36Sopenharmony_ci *mem_ranges = NULL; 18762306a36Sopenharmony_ci return NULL; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci mem_rngs->nr_ranges = nr_ranges; 19162306a36Sopenharmony_ci mem_rngs->max_nr_ranges = get_max_nr_ranges(size); 19262306a36Sopenharmony_ci *mem_ranges = mem_rngs; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return mem_rngs; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/** 19862306a36Sopenharmony_ci * add_mem_range - Updates existing memory range, if there is an overlap. 19962306a36Sopenharmony_ci * Else, adds a new memory range. 20062306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 20162306a36Sopenharmony_ci * @base: Base address of the range to add. 20262306a36Sopenharmony_ci * @size: Size of the memory range to add. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * (Re)allocates memory, if needed. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ciint add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct crash_mem *mem_rngs = *mem_ranges; 21162306a36Sopenharmony_ci u64 mstart, mend, end; 21262306a36Sopenharmony_ci unsigned int i; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!size) 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci end = base + size - 1; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!mem_rngs || !(mem_rngs->nr_ranges)) 22062306a36Sopenharmony_ci return __add_mem_range(mem_ranges, base, size); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci for (i = 0; i < mem_rngs->nr_ranges; i++) { 22362306a36Sopenharmony_ci mstart = mem_rngs->ranges[i].start; 22462306a36Sopenharmony_ci mend = mem_rngs->ranges[i].end; 22562306a36Sopenharmony_ci if (base < mend && end > mstart) { 22662306a36Sopenharmony_ci if (base < mstart) 22762306a36Sopenharmony_ci mem_rngs->ranges[i].start = base; 22862306a36Sopenharmony_ci if (end > mend) 22962306a36Sopenharmony_ci mem_rngs->ranges[i].end = end; 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return __add_mem_range(mem_ranges, base, size); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/** 23862306a36Sopenharmony_ci * add_tce_mem_ranges - Adds tce-table range to the given memory ranges list. 23962306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range(s) to. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ciint add_tce_mem_ranges(struct crash_mem **mem_ranges) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct device_node *dn = NULL; 24662306a36Sopenharmony_ci int ret = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci for_each_node_by_type(dn, "pci") { 24962306a36Sopenharmony_ci u64 base; 25062306a36Sopenharmony_ci u32 size; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = of_property_read_u64(dn, "linux,tce-base", &base); 25362306a36Sopenharmony_ci ret |= of_property_read_u32(dn, "linux,tce-size", &size); 25462306a36Sopenharmony_ci if (ret) { 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * It is ok to have pci nodes without tce. So, ignore 25762306a36Sopenharmony_ci * property does not exist error. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci if (ret == -EINVAL) { 26062306a36Sopenharmony_ci ret = 0; 26162306a36Sopenharmony_ci continue; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, base, size); 26762306a36Sopenharmony_ci if (ret) 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci of_node_put(dn); 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/** 27662306a36Sopenharmony_ci * add_initrd_mem_range - Adds initrd range to the given memory ranges list, 27762306a36Sopenharmony_ci * if the initrd was retained. 27862306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ciint add_initrd_mem_range(struct crash_mem **mem_ranges) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci u64 base, end; 28562306a36Sopenharmony_ci int ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* This range means something, only if initrd was retained */ 28862306a36Sopenharmony_ci if (!strstr(saved_command_line, "retain_initrd")) 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = of_property_read_u64(of_chosen, "linux,initrd-start", &base); 29262306a36Sopenharmony_ci ret |= of_property_read_u64(of_chosen, "linux,initrd-end", &end); 29362306a36Sopenharmony_ci if (!ret) 29462306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, base, end - base + 1); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#ifdef CONFIG_PPC_64S_HASH_MMU 30062306a36Sopenharmony_ci/** 30162306a36Sopenharmony_ci * add_htab_mem_range - Adds htab range to the given memory ranges list, 30262306a36Sopenharmony_ci * if it exists 30362306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 30462306a36Sopenharmony_ci * 30562306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ciint add_htab_mem_range(struct crash_mem **mem_ranges) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci if (!htab_address) 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci#endif 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * add_kernel_mem_range - Adds kernel text region to the given 31862306a36Sopenharmony_ci * memory ranges list. 31962306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ciint add_kernel_mem_range(struct crash_mem **mem_ranges) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci return add_mem_range(mem_ranges, 0, __pa(_end)); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/** 32962306a36Sopenharmony_ci * add_rtas_mem_range - Adds RTAS region to the given memory ranges list. 33062306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ciint add_rtas_mem_range(struct crash_mem **mem_ranges) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct device_node *dn; 33762306a36Sopenharmony_ci u32 base, size; 33862306a36Sopenharmony_ci int ret = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci dn = of_find_node_by_path("/rtas"); 34162306a36Sopenharmony_ci if (!dn) 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = of_property_read_u32(dn, "linux,rtas-base", &base); 34562306a36Sopenharmony_ci ret |= of_property_read_u32(dn, "rtas-size", &size); 34662306a36Sopenharmony_ci if (!ret) 34762306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, base, size); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci of_node_put(dn); 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/** 35462306a36Sopenharmony_ci * add_opal_mem_range - Adds OPAL region to the given memory ranges list. 35562306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory range to. 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ciint add_opal_mem_range(struct crash_mem **mem_ranges) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct device_node *dn; 36262306a36Sopenharmony_ci u64 base, size; 36362306a36Sopenharmony_ci int ret; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci dn = of_find_node_by_path("/ibm,opal"); 36662306a36Sopenharmony_ci if (!dn) 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = of_property_read_u64(dn, "opal-base-address", &base); 37062306a36Sopenharmony_ci ret |= of_property_read_u64(dn, "opal-runtime-size", &size); 37162306a36Sopenharmony_ci if (!ret) 37262306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, base, size); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci of_node_put(dn); 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/** 37962306a36Sopenharmony_ci * add_reserved_mem_ranges - Adds "/reserved-ranges" regions exported by f/w 38062306a36Sopenharmony_ci * to the given memory ranges list. 38162306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory ranges to. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ciint add_reserved_mem_ranges(struct crash_mem **mem_ranges) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int n_mem_addr_cells, n_mem_size_cells, i, len, cells, ret = 0; 38862306a36Sopenharmony_ci const __be32 *prop; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci prop = of_get_property(of_root, "reserved-ranges", &len); 39162306a36Sopenharmony_ci if (!prop) 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci n_mem_addr_cells = of_n_addr_cells(of_root); 39562306a36Sopenharmony_ci n_mem_size_cells = of_n_size_cells(of_root); 39662306a36Sopenharmony_ci cells = n_mem_addr_cells + n_mem_size_cells; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Each reserved range is an (address,size) pair */ 39962306a36Sopenharmony_ci for (i = 0; i < (len / (sizeof(u32) * cells)); i++) { 40062306a36Sopenharmony_ci u64 base, size; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci base = of_read_number(prop + (i * cells), n_mem_addr_cells); 40362306a36Sopenharmony_ci size = of_read_number(prop + (i * cells) + n_mem_addr_cells, 40462306a36Sopenharmony_ci n_mem_size_cells); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, base, size); 40762306a36Sopenharmony_ci if (ret) 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 413