1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2024, Linaro Limited
4  */
5 
6 #include <fdt_support.h>
7 #include <fdtdec.h>
8 #include <memtop.h>
9 
10 #include <asm/types.h>
11 
12 #define MEM_RGN_COUNT	16
13 
14 struct region {
15 	phys_addr_t base;
16 	phys_size_t size;
17 };
18 
19 struct mem_region {
20 	struct region rgn[MEM_RGN_COUNT];
21 	uint count;
22 };
23 
add_mem_region(struct mem_region * mem_rgn,phys_addr_t base,phys_size_t size)24 static void add_mem_region(struct mem_region *mem_rgn, phys_addr_t base,
25 			   phys_size_t size)
26 {
27 	long i;
28 
29 	for (i = mem_rgn->count; i >= 0; i--) {
30 		if (i && base < mem_rgn->rgn[i - 1].base) {
31 			mem_rgn->rgn[i] = mem_rgn->rgn[i - 1];
32 		} else {
33 			mem_rgn->rgn[i].base = base;
34 			mem_rgn->rgn[i].size = size;
35 			break;
36 		}
37 	}
38 
39 	mem_rgn->count++;
40 }
41 
mem_regions_init(struct mem_region * mem)42 static void mem_regions_init(struct mem_region *mem)
43 {
44 	uint i;
45 
46 	mem->count = 0;
47 	for (i = 0; i < MEM_RGN_COUNT; i++) {
48 		mem->rgn[i].base = 0;
49 		mem->rgn[i].size = 0;
50 	}
51 }
52 
fdt_add_reserved_regions(struct mem_region * free_mem,struct mem_region * reserved_mem,void * fdt_blob)53 static int fdt_add_reserved_regions(struct mem_region *free_mem,
54 				    struct mem_region *reserved_mem,
55 				    void *fdt_blob)
56 {
57 	u64 addr, size;
58 	int i, total, ret;
59 	int nodeoffset, subnode;
60 	struct fdt_resource res;
61 
62 	if (fdt_check_header(fdt_blob) != 0)
63 		return -1;
64 
65 	/* process memreserve sections */
66 	total = fdt_num_mem_rsv(fdt_blob);
67 	assert_noisy(total < MEM_RGN_COUNT);
68 	for (i = 0; i < total; i++) {
69 		if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
70 			continue;
71 		add_mem_region(reserved_mem, addr, size);
72 	}
73 
74 	i = 0;
75 	/* process reserved-memory */
76 	nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory");
77 	if (nodeoffset >= 0) {
78 		subnode = fdt_first_subnode(fdt_blob, nodeoffset);
79 		while (subnode >= 0) {
80 			/* check if this subnode has a reg property */
81 			ret = fdt_get_resource(fdt_blob, subnode, "reg", 0,
82 					       &res);
83 			if (!ret && fdtdec_get_is_enabled(fdt_blob, subnode)) {
84 				addr = res.start;
85 				size = res.end - res.start + 1;
86 				assert_noisy(i < MEM_RGN_COUNT);
87 				add_mem_region(reserved_mem, addr, size);
88 			}
89 
90 			subnode = fdt_next_subnode(fdt_blob, subnode);
91 			++i;
92 		}
93 	}
94 
95 	return 0;
96 }
97 
addrs_overlap(phys_addr_t base1,phys_size_t size1,phys_addr_t base2,phys_size_t size2)98 static long addrs_overlap(phys_addr_t base1, phys_size_t size1,
99 			  phys_addr_t base2, phys_size_t size2)
100 {
101 	const phys_addr_t base1_end = base1 + size1 - 1;
102 	const phys_addr_t base2_end = base2 + size2 - 1;
103 
104 	return ((base1 <= base2_end) && (base2 <= base1_end));
105 }
106 
region_overlap_check(struct mem_region * mem_rgn,phys_addr_t base,phys_size_t size)107 static long region_overlap_check(struct mem_region *mem_rgn, phys_addr_t base,
108 				 phys_size_t size)
109 {
110 	unsigned long i;
111 	struct region *rgn = mem_rgn->rgn;
112 
113 	for (i = 0; i < mem_rgn->count; i++) {
114 		phys_addr_t rgnbase = rgn[i].base;
115 		phys_size_t rgnsize = rgn[i].size;
116 
117 		if (addrs_overlap(base, size, rgnbase, rgnsize))
118 			break;
119 	}
120 
121 	return (i < mem_rgn->count) ? i : -1;
122 }
123 
find_ram_top(struct mem_region * free_mem,struct mem_region * reserved_mem,phys_size_t size)124 static phys_addr_t find_ram_top(struct mem_region *free_mem,
125 				struct mem_region *reserved_mem, phys_size_t size)
126 {
127 	long i, rgn;
128 	phys_addr_t base = 0;
129 	phys_addr_t res_base;
130 
131 	for (i = free_mem->count - 1; i >= 0; i--) {
132 		phys_addr_t rgnbase = free_mem->rgn[i].base;
133 		phys_size_t rgnsize = free_mem->rgn[i].size;
134 
135 		if (rgnsize < size)
136 			continue;
137 
138 		base = rgnbase + rgnsize - size;
139 		while (base && rgnbase <= base) {
140 			rgn = region_overlap_check(reserved_mem, base, size);
141 			if (rgn < 0)
142 				return base;
143 
144 			res_base = reserved_mem->rgn[rgn].base;
145 			if (res_base < size)
146 				break;
147 			base = res_base - size;
148 		}
149 	}
150 
151 	return 0;
152 }
153 
get_mem_top(phys_addr_t ram_start,phys_size_t ram_size,phys_size_t size,void * fdt)154 phys_addr_t get_mem_top(phys_addr_t ram_start, phys_size_t ram_size,
155 			phys_size_t size, void *fdt)
156 {
157 	int i;
158 	struct mem_region free_mem;
159 	struct mem_region reserved_mem;
160 
161 	mem_regions_init(&free_mem);
162 	mem_regions_init(&reserved_mem);
163 
164 	add_mem_region(&free_mem, ram_start, ram_size);
165 
166 	i = fdt_add_reserved_regions(&free_mem, &reserved_mem, fdt);
167 	if (i < 0)
168 		return 0;
169 
170 	return find_ram_top(&free_mem, &reserved_mem, size);
171 }
172