18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * powerpc code to implement the kexec_file_load syscall
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
68c2ecf20Sopenharmony_ci * Copyright (C) 2004  IBM Corp.
78c2ecf20Sopenharmony_ci * Copyright (C) 2004,2005  Milton D Miller II, IBM Corporation
88c2ecf20Sopenharmony_ci * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
98c2ecf20Sopenharmony_ci * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
108c2ecf20Sopenharmony_ci * Copyright (C) 2020  IBM Corporation
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Based on kexec-tools' kexec-ppc64.c, fs2dt.c.
138c2ecf20Sopenharmony_ci * Heavily modified for the kernel by
148c2ecf20Sopenharmony_ci * Hari Bathini, IBM Corporation.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "kexec ranges: " fmt
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/sort.h>
208c2ecf20Sopenharmony_ci#include <linux/kexec.h>
218c2ecf20Sopenharmony_ci#include <linux/of_device.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <asm/sections.h>
248c2ecf20Sopenharmony_ci#include <asm/kexec_ranges.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * get_max_nr_ranges - Get the max no. of ranges crash_mem structure
288c2ecf20Sopenharmony_ci *                     could hold, given the size allocated for it.
298c2ecf20Sopenharmony_ci * @size:              Allocation size of crash_mem structure.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Returns the maximum no. of ranges.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_cistatic inline unsigned int get_max_nr_ranges(size_t size)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	return ((size - sizeof(struct crash_mem)) /
368c2ecf20Sopenharmony_ci		sizeof(struct crash_mem_range));
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/**
408c2ecf20Sopenharmony_ci * get_mem_rngs_size - Get the allocated size of mem_rngs based on
418c2ecf20Sopenharmony_ci *                     max_nr_ranges and chunk size.
428c2ecf20Sopenharmony_ci * @mem_rngs:          Memory ranges.
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * Returns the maximum size of @mem_rngs.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic inline size_t get_mem_rngs_size(struct crash_mem *mem_rngs)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	size_t size;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (!mem_rngs)
518c2ecf20Sopenharmony_ci		return 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	size = (sizeof(struct crash_mem) +
548c2ecf20Sopenharmony_ci		(mem_rngs->max_nr_ranges * sizeof(struct crash_mem_range)));
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/*
578c2ecf20Sopenharmony_ci	 * Memory is allocated in size multiple of MEM_RANGE_CHUNK_SZ.
588c2ecf20Sopenharmony_ci	 * So, align to get the actual length.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	return ALIGN(size, MEM_RANGE_CHUNK_SZ);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/**
648c2ecf20Sopenharmony_ci * __add_mem_range - add a memory range to memory ranges list.
658c2ecf20Sopenharmony_ci * @mem_ranges:      Range list to add the memory range to.
668c2ecf20Sopenharmony_ci * @base:            Base address of the range to add.
678c2ecf20Sopenharmony_ci * @size:            Size of the memory range to add.
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * (Re)allocates memory, if needed.
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistatic int __add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct crash_mem *mem_rngs = *mem_ranges;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!mem_rngs || (mem_rngs->nr_ranges == mem_rngs->max_nr_ranges)) {
788c2ecf20Sopenharmony_ci		mem_rngs = realloc_mem_ranges(mem_ranges);
798c2ecf20Sopenharmony_ci		if (!mem_rngs)
808c2ecf20Sopenharmony_ci			return -ENOMEM;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	mem_rngs->ranges[mem_rngs->nr_ranges].start = base;
848c2ecf20Sopenharmony_ci	mem_rngs->ranges[mem_rngs->nr_ranges].end = base + size - 1;
858c2ecf20Sopenharmony_ci	pr_debug("Added memory range [%#016llx - %#016llx] at index %d\n",
868c2ecf20Sopenharmony_ci		 base, base + size - 1, mem_rngs->nr_ranges);
878c2ecf20Sopenharmony_ci	mem_rngs->nr_ranges++;
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * __merge_memory_ranges - Merges the given memory ranges list.
938c2ecf20Sopenharmony_ci * @mem_rngs:              Range list to merge.
948c2ecf20Sopenharmony_ci *
958c2ecf20Sopenharmony_ci * Assumes a sorted range list.
968c2ecf20Sopenharmony_ci *
978c2ecf20Sopenharmony_ci * Returns nothing.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_cistatic void __merge_memory_ranges(struct crash_mem *mem_rngs)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct crash_mem_range *ranges;
1028c2ecf20Sopenharmony_ci	int i, idx;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!mem_rngs)
1058c2ecf20Sopenharmony_ci		return;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	idx = 0;
1088c2ecf20Sopenharmony_ci	ranges = &(mem_rngs->ranges[0]);
1098c2ecf20Sopenharmony_ci	for (i = 1; i < mem_rngs->nr_ranges; i++) {
1108c2ecf20Sopenharmony_ci		if (ranges[i].start <= (ranges[i-1].end + 1))
1118c2ecf20Sopenharmony_ci			ranges[idx].end = ranges[i].end;
1128c2ecf20Sopenharmony_ci		else {
1138c2ecf20Sopenharmony_ci			idx++;
1148c2ecf20Sopenharmony_ci			if (i == idx)
1158c2ecf20Sopenharmony_ci				continue;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci			ranges[idx] = ranges[i];
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	mem_rngs->nr_ranges = idx + 1;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* cmp_func_t callback to sort ranges with sort() */
1248c2ecf20Sopenharmony_cistatic int rngcmp(const void *_x, const void *_y)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	const struct crash_mem_range *x = _x, *y = _y;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (x->start > y->start)
1298c2ecf20Sopenharmony_ci		return 1;
1308c2ecf20Sopenharmony_ci	if (x->start < y->start)
1318c2ecf20Sopenharmony_ci		return -1;
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/**
1368c2ecf20Sopenharmony_ci * sort_memory_ranges - Sorts the given memory ranges list.
1378c2ecf20Sopenharmony_ci * @mem_rngs:           Range list to sort.
1388c2ecf20Sopenharmony_ci * @merge:              If true, merge the list after sorting.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * Returns nothing.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_civoid sort_memory_ranges(struct crash_mem *mem_rngs, bool merge)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int i;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (!mem_rngs)
1478c2ecf20Sopenharmony_ci		return;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* Sort the ranges in-place */
1508c2ecf20Sopenharmony_ci	sort(&(mem_rngs->ranges[0]), mem_rngs->nr_ranges,
1518c2ecf20Sopenharmony_ci	     sizeof(mem_rngs->ranges[0]), rngcmp, NULL);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (merge)
1548c2ecf20Sopenharmony_ci		__merge_memory_ranges(mem_rngs);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* For debugging purpose */
1578c2ecf20Sopenharmony_ci	pr_debug("Memory ranges:\n");
1588c2ecf20Sopenharmony_ci	for (i = 0; i < mem_rngs->nr_ranges; i++) {
1598c2ecf20Sopenharmony_ci		pr_debug("\t[%03d][%#016llx - %#016llx]\n", i,
1608c2ecf20Sopenharmony_ci			 mem_rngs->ranges[i].start,
1618c2ecf20Sopenharmony_ci			 mem_rngs->ranges[i].end);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * realloc_mem_ranges - reallocate mem_ranges with size incremented
1678c2ecf20Sopenharmony_ci *                      by MEM_RANGE_CHUNK_SZ. Frees up the old memory,
1688c2ecf20Sopenharmony_ci *                      if memory allocation fails.
1698c2ecf20Sopenharmony_ci * @mem_ranges:         Memory ranges to reallocate.
1708c2ecf20Sopenharmony_ci *
1718c2ecf20Sopenharmony_ci * Returns pointer to reallocated memory on success, NULL otherwise.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistruct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct crash_mem *mem_rngs = *mem_ranges;
1768c2ecf20Sopenharmony_ci	unsigned int nr_ranges;
1778c2ecf20Sopenharmony_ci	size_t size;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	size = get_mem_rngs_size(mem_rngs);
1808c2ecf20Sopenharmony_ci	nr_ranges = mem_rngs ? mem_rngs->nr_ranges : 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	size += MEM_RANGE_CHUNK_SZ;
1838c2ecf20Sopenharmony_ci	mem_rngs = krealloc(*mem_ranges, size, GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (!mem_rngs) {
1858c2ecf20Sopenharmony_ci		kfree(*mem_ranges);
1868c2ecf20Sopenharmony_ci		*mem_ranges = NULL;
1878c2ecf20Sopenharmony_ci		return NULL;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	mem_rngs->nr_ranges = nr_ranges;
1918c2ecf20Sopenharmony_ci	mem_rngs->max_nr_ranges = get_max_nr_ranges(size);
1928c2ecf20Sopenharmony_ci	*mem_ranges = mem_rngs;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return mem_rngs;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/**
1988c2ecf20Sopenharmony_ci * add_mem_range - Updates existing memory range, if there is an overlap.
1998c2ecf20Sopenharmony_ci *                 Else, adds a new memory range.
2008c2ecf20Sopenharmony_ci * @mem_ranges:    Range list to add the memory range to.
2018c2ecf20Sopenharmony_ci * @base:          Base address of the range to add.
2028c2ecf20Sopenharmony_ci * @size:          Size of the memory range to add.
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci * (Re)allocates memory, if needed.
2058c2ecf20Sopenharmony_ci *
2068c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_ciint add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct crash_mem *mem_rngs = *mem_ranges;
2118c2ecf20Sopenharmony_ci	u64 mstart, mend, end;
2128c2ecf20Sopenharmony_ci	unsigned int i;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (!size)
2158c2ecf20Sopenharmony_ci		return 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	end = base + size - 1;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (!mem_rngs || !(mem_rngs->nr_ranges))
2208c2ecf20Sopenharmony_ci		return __add_mem_range(mem_ranges, base, size);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	for (i = 0; i < mem_rngs->nr_ranges; i++) {
2238c2ecf20Sopenharmony_ci		mstart = mem_rngs->ranges[i].start;
2248c2ecf20Sopenharmony_ci		mend = mem_rngs->ranges[i].end;
2258c2ecf20Sopenharmony_ci		if (base < mend && end > mstart) {
2268c2ecf20Sopenharmony_ci			if (base < mstart)
2278c2ecf20Sopenharmony_ci				mem_rngs->ranges[i].start = base;
2288c2ecf20Sopenharmony_ci			if (end > mend)
2298c2ecf20Sopenharmony_ci				mem_rngs->ranges[i].end = end;
2308c2ecf20Sopenharmony_ci			return 0;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return __add_mem_range(mem_ranges, base, size);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/**
2388c2ecf20Sopenharmony_ci * add_tce_mem_ranges - Adds tce-table range to the given memory ranges list.
2398c2ecf20Sopenharmony_ci * @mem_ranges:         Range list to add the memory range(s) to.
2408c2ecf20Sopenharmony_ci *
2418c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_ciint add_tce_mem_ranges(struct crash_mem **mem_ranges)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct device_node *dn = NULL;
2468c2ecf20Sopenharmony_ci	int ret = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	for_each_node_by_type(dn, "pci") {
2498c2ecf20Sopenharmony_ci		u64 base;
2508c2ecf20Sopenharmony_ci		u32 size;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		ret = of_property_read_u64(dn, "linux,tce-base", &base);
2538c2ecf20Sopenharmony_ci		ret |= of_property_read_u32(dn, "linux,tce-size", &size);
2548c2ecf20Sopenharmony_ci		if (ret) {
2558c2ecf20Sopenharmony_ci			/*
2568c2ecf20Sopenharmony_ci			 * It is ok to have pci nodes without tce. So, ignore
2578c2ecf20Sopenharmony_ci			 * property does not exist error.
2588c2ecf20Sopenharmony_ci			 */
2598c2ecf20Sopenharmony_ci			if (ret == -EINVAL) {
2608c2ecf20Sopenharmony_ci				ret = 0;
2618c2ecf20Sopenharmony_ci				continue;
2628c2ecf20Sopenharmony_ci			}
2638c2ecf20Sopenharmony_ci			break;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		ret = add_mem_range(mem_ranges, base, size);
2678c2ecf20Sopenharmony_ci		if (ret)
2688c2ecf20Sopenharmony_ci			break;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	of_node_put(dn);
2728c2ecf20Sopenharmony_ci	return ret;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/**
2768c2ecf20Sopenharmony_ci * add_initrd_mem_range - Adds initrd range to the given memory ranges list,
2778c2ecf20Sopenharmony_ci *                        if the initrd was retained.
2788c2ecf20Sopenharmony_ci * @mem_ranges:           Range list to add the memory range to.
2798c2ecf20Sopenharmony_ci *
2808c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
2818c2ecf20Sopenharmony_ci */
2828c2ecf20Sopenharmony_ciint add_initrd_mem_range(struct crash_mem **mem_ranges)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	u64 base, end;
2858c2ecf20Sopenharmony_ci	int ret;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* This range means something, only if initrd was retained */
2888c2ecf20Sopenharmony_ci	if (!strstr(saved_command_line, "retain_initrd"))
2898c2ecf20Sopenharmony_ci		return 0;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	ret = of_property_read_u64(of_chosen, "linux,initrd-start", &base);
2928c2ecf20Sopenharmony_ci	ret |= of_property_read_u64(of_chosen, "linux,initrd-end", &end);
2938c2ecf20Sopenharmony_ci	if (!ret)
2948c2ecf20Sopenharmony_ci		ret = add_mem_range(mem_ranges, base, end - base + 1);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return ret;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64
3008c2ecf20Sopenharmony_ci/**
3018c2ecf20Sopenharmony_ci * add_htab_mem_range - Adds htab range to the given memory ranges list,
3028c2ecf20Sopenharmony_ci *                      if it exists
3038c2ecf20Sopenharmony_ci * @mem_ranges:         Range list to add the memory range to.
3048c2ecf20Sopenharmony_ci *
3058c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
3068c2ecf20Sopenharmony_ci */
3078c2ecf20Sopenharmony_ciint add_htab_mem_range(struct crash_mem **mem_ranges)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	if (!htab_address)
3108c2ecf20Sopenharmony_ci		return 0;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci#endif
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/**
3178c2ecf20Sopenharmony_ci * add_kernel_mem_range - Adds kernel text region to the given
3188c2ecf20Sopenharmony_ci *                        memory ranges list.
3198c2ecf20Sopenharmony_ci * @mem_ranges:           Range list to add the memory range to.
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
3228c2ecf20Sopenharmony_ci */
3238c2ecf20Sopenharmony_ciint add_kernel_mem_range(struct crash_mem **mem_ranges)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	return add_mem_range(mem_ranges, 0, __pa(_end));
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci/**
3298c2ecf20Sopenharmony_ci * add_rtas_mem_range - Adds RTAS region to the given memory ranges list.
3308c2ecf20Sopenharmony_ci * @mem_ranges:         Range list to add the memory range to.
3318c2ecf20Sopenharmony_ci *
3328c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_ciint add_rtas_mem_range(struct crash_mem **mem_ranges)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct device_node *dn;
3378c2ecf20Sopenharmony_ci	u32 base, size;
3388c2ecf20Sopenharmony_ci	int ret = 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	dn = of_find_node_by_path("/rtas");
3418c2ecf20Sopenharmony_ci	if (!dn)
3428c2ecf20Sopenharmony_ci		return 0;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	ret = of_property_read_u32(dn, "linux,rtas-base", &base);
3458c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(dn, "rtas-size", &size);
3468c2ecf20Sopenharmony_ci	if (!ret)
3478c2ecf20Sopenharmony_ci		ret = add_mem_range(mem_ranges, base, size);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	of_node_put(dn);
3508c2ecf20Sopenharmony_ci	return ret;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/**
3548c2ecf20Sopenharmony_ci * add_opal_mem_range - Adds OPAL region to the given memory ranges list.
3558c2ecf20Sopenharmony_ci * @mem_ranges:         Range list to add the memory range to.
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_ciint add_opal_mem_range(struct crash_mem **mem_ranges)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct device_node *dn;
3628c2ecf20Sopenharmony_ci	u64 base, size;
3638c2ecf20Sopenharmony_ci	int ret;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	dn = of_find_node_by_path("/ibm,opal");
3668c2ecf20Sopenharmony_ci	if (!dn)
3678c2ecf20Sopenharmony_ci		return 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	ret = of_property_read_u64(dn, "opal-base-address", &base);
3708c2ecf20Sopenharmony_ci	ret |= of_property_read_u64(dn, "opal-runtime-size", &size);
3718c2ecf20Sopenharmony_ci	if (!ret)
3728c2ecf20Sopenharmony_ci		ret = add_mem_range(mem_ranges, base, size);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	of_node_put(dn);
3758c2ecf20Sopenharmony_ci	return ret;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/**
3798c2ecf20Sopenharmony_ci * add_reserved_mem_ranges - Adds "/reserved-ranges" regions exported by f/w
3808c2ecf20Sopenharmony_ci *                           to the given memory ranges list.
3818c2ecf20Sopenharmony_ci * @mem_ranges:              Range list to add the memory ranges to.
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci * Returns 0 on success, negative errno on error.
3848c2ecf20Sopenharmony_ci */
3858c2ecf20Sopenharmony_ciint add_reserved_mem_ranges(struct crash_mem **mem_ranges)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	int n_mem_addr_cells, n_mem_size_cells, i, len, cells, ret = 0;
3888c2ecf20Sopenharmony_ci	const __be32 *prop;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	prop = of_get_property(of_root, "reserved-ranges", &len);
3918c2ecf20Sopenharmony_ci	if (!prop)
3928c2ecf20Sopenharmony_ci		return 0;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	n_mem_addr_cells = of_n_addr_cells(of_root);
3958c2ecf20Sopenharmony_ci	n_mem_size_cells = of_n_size_cells(of_root);
3968c2ecf20Sopenharmony_ci	cells = n_mem_addr_cells + n_mem_size_cells;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* Each reserved range is an (address,size) pair */
3998c2ecf20Sopenharmony_ci	for (i = 0; i < (len / (sizeof(u32) * cells)); i++) {
4008c2ecf20Sopenharmony_ci		u64 base, size;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		base = of_read_number(prop + (i * cells), n_mem_addr_cells);
4038c2ecf20Sopenharmony_ci		size = of_read_number(prop + (i * cells) + n_mem_addr_cells,
4048c2ecf20Sopenharmony_ci				      n_mem_size_cells);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		ret = add_mem_range(mem_ranges, base, size);
4078c2ecf20Sopenharmony_ci		if (ret)
4088c2ecf20Sopenharmony_ci			break;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return ret;
4128c2ecf20Sopenharmony_ci}
413