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