xref: /netbsd-src/sys/dev/fdt/fdt_memory.c (revision b511062804e983232d0f8e4c1924fa970c38ad1e)
1*b5110628Sjmcneill /* $NetBSD: fdt_memory.c,v 1.3 2021/06/26 10:43:52 jmcneill Exp $ */
2ff970d37Sskrll 
3ff970d37Sskrll /*-
4ff970d37Sskrll  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5ff970d37Sskrll  * All rights reserved.
6ff970d37Sskrll  *
7ff970d37Sskrll  * This code is derived from software contributed to The NetBSD Foundation
8ff970d37Sskrll  * by Jared McNeill <jmcneill@invisible.ca>.
9ff970d37Sskrll  *
10ff970d37Sskrll  * Redistribution and use in source and binary forms, with or without
11ff970d37Sskrll  * modification, are permitted provided that the following conditions
12ff970d37Sskrll  * are met:
13ff970d37Sskrll  * 1. Redistributions of source code must retain the above copyright
14ff970d37Sskrll  *    notice, this list of conditions and the following disclaimer.
15ff970d37Sskrll  * 2. Redistributions in binary form must reproduce the above copyright
16ff970d37Sskrll  *    notice, this list of conditions and the following disclaimer in the
17ff970d37Sskrll  *    documentation and/or other materials provided with the distribution.
18ff970d37Sskrll  *
19ff970d37Sskrll  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20ff970d37Sskrll  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21ff970d37Sskrll  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22ff970d37Sskrll  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23ff970d37Sskrll  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24ff970d37Sskrll  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25ff970d37Sskrll  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26ff970d37Sskrll  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27ff970d37Sskrll  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28ff970d37Sskrll  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29ff970d37Sskrll  * POSSIBILITY OF SUCH DAMAGE.
30ff970d37Sskrll  */
31ff970d37Sskrll 
32ff970d37Sskrll #include "opt_fdt.h"
33ff970d37Sskrll 
34ff970d37Sskrll #include <sys/cdefs.h>
35*b5110628Sjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_memory.c,v 1.3 2021/06/26 10:43:52 jmcneill Exp $");
36ff970d37Sskrll 
37ff970d37Sskrll #include <sys/param.h>
38ff970d37Sskrll #include <sys/queue.h>
39ff970d37Sskrll 
40ff970d37Sskrll #include <libfdt.h>
41ff970d37Sskrll #include <dev/fdt/fdtvar.h>
42ff970d37Sskrll #include <dev/fdt/fdt_memory.h>
43ff970d37Sskrll 
44ff970d37Sskrll #ifndef FDT_MEMORY_RANGES
45ff970d37Sskrll #define FDT_MEMORY_RANGES	256
46ff970d37Sskrll #endif
47ff970d37Sskrll 
48ff970d37Sskrll struct fdt_memory_range {
49ff970d37Sskrll 	struct fdt_memory               mr_mem;
50ff970d37Sskrll 	bool                            mr_used;
51ff970d37Sskrll 	TAILQ_ENTRY(fdt_memory_range)   mr_list;
52ff970d37Sskrll };
53ff970d37Sskrll 
54ff970d37Sskrll static TAILQ_HEAD(fdt_memory_rangehead, fdt_memory_range) fdt_memory_ranges =
55ff970d37Sskrll     TAILQ_HEAD_INITIALIZER(fdt_memory_ranges);
56ff970d37Sskrll 
57ff970d37Sskrll static struct fdt_memory_range fdt_memory_range_pool[FDT_MEMORY_RANGES];
58ff970d37Sskrll 
59ff970d37Sskrll static struct fdt_memory_range *
60ff970d37Sskrll fdt_memory_range_alloc(void)
61ff970d37Sskrll {
62ff970d37Sskrll 	for (size_t n = 0; n < FDT_MEMORY_RANGES; n++)
63ff970d37Sskrll 		if (!fdt_memory_range_pool[n].mr_used) {
64ff970d37Sskrll 			fdt_memory_range_pool[n].mr_used = true;
65ff970d37Sskrll 			return &fdt_memory_range_pool[n];
66ff970d37Sskrll 		}
67ff970d37Sskrll 
68ff970d37Sskrll 	printf("%s: no free memory ranges, increase FDT_MEMORY_RANGES!\n", __func__);
69ff970d37Sskrll 	return NULL;
70ff970d37Sskrll }
71ff970d37Sskrll 
72ff970d37Sskrll static void
73ff970d37Sskrll fdt_memory_range_free(struct fdt_memory_range *mr)
74ff970d37Sskrll {
75ff970d37Sskrll 	mr->mr_used = false;
76ff970d37Sskrll }
77ff970d37Sskrll 
78ff970d37Sskrll /*
79ff970d37Sskrll  * Get all of physical memory, including holes.
80ff970d37Sskrll  */
81ff970d37Sskrll void
82ff970d37Sskrll fdt_memory_get(uint64_t *pstart, uint64_t *pend)
83ff970d37Sskrll {
84ff970d37Sskrll 	const int memory = OF_finddevice("/memory");
85ff970d37Sskrll 	uint64_t cur_addr, cur_size;
86ff970d37Sskrll 	int index;
87ff970d37Sskrll 
88ff970d37Sskrll 	for (index = 0;
89ff970d37Sskrll 	     fdtbus_get_reg64(memory, index, &cur_addr, &cur_size) == 0;
90ff970d37Sskrll 	     index++) {
91ff970d37Sskrll 		fdt_memory_add_range(cur_addr, cur_size);
92ff970d37Sskrll 
93ff970d37Sskrll 		/* Assume the first entry is the start of memory */
94ff970d37Sskrll 		if (index == 0) {
95ff970d37Sskrll 			*pstart = cur_addr;
96ff970d37Sskrll 			*pend = cur_addr + cur_size;
97ff970d37Sskrll 			continue;
98ff970d37Sskrll 		}
99ff970d37Sskrll 		if (cur_addr + cur_size > *pend)
100ff970d37Sskrll 			*pend = cur_addr + cur_size;
101ff970d37Sskrll 	}
102ff970d37Sskrll 	if (index == 0)
103ff970d37Sskrll 		panic("Cannot determine memory size");
104ff970d37Sskrll }
105ff970d37Sskrll 
106ff970d37Sskrll /*
107ff970d37Sskrll  * Exclude memory ranges from memory config from the device tree
108ff970d37Sskrll  */
109ff970d37Sskrll void
110ff970d37Sskrll fdt_memory_remove_reserved(uint64_t min_addr, uint64_t max_addr)
111ff970d37Sskrll {
112ff970d37Sskrll 	uint64_t lstart = 0, lend = 0;
113a18fd2f5Sryo 	int index, error, phandle, child;
114ff970d37Sskrll 
115ff970d37Sskrll 	const int num = fdt_num_mem_rsv(fdtbus_get_data());
116ff970d37Sskrll 	for (index = 0; index <= num; index++) {
117*b5110628Sjmcneill 		uint64_t addr, size;
118*b5110628Sjmcneill 
119ff970d37Sskrll 		error = fdt_get_mem_rsv(fdtbus_get_data(), index,
120ff970d37Sskrll 		    &addr, &size);
121ff970d37Sskrll 		if (error != 0)
122ff970d37Sskrll 			continue;
123ff970d37Sskrll 		if (lstart <= addr && addr <= lend) {
124ff970d37Sskrll 			size -= (lend - addr);
125ff970d37Sskrll 			addr = lend;
126ff970d37Sskrll 		}
127ff970d37Sskrll 		if (size == 0)
128ff970d37Sskrll 			continue;
129ff970d37Sskrll 		if (addr + size <= min_addr)
130ff970d37Sskrll 			continue;
131ff970d37Sskrll 		if (addr >= max_addr)
132ff970d37Sskrll 			continue;
133ff970d37Sskrll 		if (addr < min_addr) {
134ff970d37Sskrll 			size -= (min_addr - addr);
135ff970d37Sskrll 			addr = min_addr;
136ff970d37Sskrll 		}
137ff970d37Sskrll 		if (addr + size > max_addr)
138ff970d37Sskrll 			size = max_addr - addr;
139ff970d37Sskrll 		fdt_memory_remove_range(addr, size);
140ff970d37Sskrll 		lstart = addr;
141ff970d37Sskrll 		lend = addr + size;
142ff970d37Sskrll 	}
143a18fd2f5Sryo 
144a18fd2f5Sryo 	/*
145a18fd2f5Sryo 	 * "no-map" ranges defined in the /reserved-memory node
146a18fd2f5Sryo 	 * must also be excluded.
147a18fd2f5Sryo 	 */
148a18fd2f5Sryo 	phandle = OF_finddevice("/reserved-memory");
149a18fd2f5Sryo 	if (phandle != -1) {
150a18fd2f5Sryo 		for (child = OF_child(phandle); child; child = OF_peer(child)) {
151*b5110628Sjmcneill 			bus_addr_t addr;
152*b5110628Sjmcneill 			bus_size_t size;
153*b5110628Sjmcneill 
154a18fd2f5Sryo 			if (!of_hasprop(child, "no-map"))
155a18fd2f5Sryo 				continue;
156a18fd2f5Sryo 
157a18fd2f5Sryo 			if (fdtbus_get_reg(child, 0, &addr, &size) != 0)
158a18fd2f5Sryo 				continue;
159a18fd2f5Sryo 			if (size == 0)
160a18fd2f5Sryo 				continue;
161a18fd2f5Sryo 			fdt_memory_remove_range(addr, size);
162a18fd2f5Sryo 		}
163a18fd2f5Sryo 	}
164ff970d37Sskrll }
165ff970d37Sskrll 
166ff970d37Sskrll void
167ff970d37Sskrll fdt_memory_add_range(uint64_t start, uint64_t size)
168ff970d37Sskrll {
169ff970d37Sskrll 	struct fdt_memory_range *mr, *prev, *cur, *tmp;
170ff970d37Sskrll 	bool inserted = false;
171ff970d37Sskrll 
172ff970d37Sskrll 	mr = fdt_memory_range_alloc();
173ff970d37Sskrll 	if (mr == NULL)
174ff970d37Sskrll 		return;
175ff970d37Sskrll 
176ff970d37Sskrll 	mr->mr_mem.start = start;
177ff970d37Sskrll 	mr->mr_mem.end = start + size;
178ff970d37Sskrll 
179ff970d37Sskrll 	/*
180ff970d37Sskrll 	 * Add the new range to the list of sorted ranges.
181ff970d37Sskrll 	 */
182ff970d37Sskrll 	TAILQ_FOREACH(cur, &fdt_memory_ranges, mr_list)
183ff970d37Sskrll 		if (mr->mr_mem.start <= cur->mr_mem.start) {
184ff970d37Sskrll 			TAILQ_INSERT_BEFORE(cur, mr, mr_list);
185ff970d37Sskrll 			inserted = true;
186ff970d37Sskrll 			break;
187ff970d37Sskrll 		}
188ff970d37Sskrll 	if (!inserted)
189ff970d37Sskrll 		TAILQ_INSERT_TAIL(&fdt_memory_ranges, mr, mr_list);
190ff970d37Sskrll 
191ff970d37Sskrll 	/*
192ff970d37Sskrll 	 * Remove overlaps.
193ff970d37Sskrll 	 */
194ff970d37Sskrll 	TAILQ_FOREACH_SAFE(mr, &fdt_memory_ranges, mr_list, tmp) {
195ff970d37Sskrll 		prev = TAILQ_PREV(mr, fdt_memory_rangehead, mr_list);
196ff970d37Sskrll 		if (prev && prev->mr_mem.end > mr->mr_mem.start) {
197ff970d37Sskrll 			mr->mr_mem.start = prev->mr_mem.end;
198ff970d37Sskrll 			if (mr->mr_mem.start >= mr->mr_mem.end) {
199ff970d37Sskrll 				TAILQ_REMOVE(&fdt_memory_ranges, mr, mr_list);
200ff970d37Sskrll 				fdt_memory_range_free(mr);
201ff970d37Sskrll 			}
202ff970d37Sskrll 		}
203ff970d37Sskrll 	}
204ff970d37Sskrll 
205ff970d37Sskrll 	/*
206ff970d37Sskrll 	 * Combine adjacent ranges.
207ff970d37Sskrll 	 */
208ff970d37Sskrll 	TAILQ_FOREACH_SAFE(mr, &fdt_memory_ranges, mr_list, tmp) {
209ff970d37Sskrll 		prev = TAILQ_PREV(mr, fdt_memory_rangehead, mr_list);
210ff970d37Sskrll 		if (prev && prev->mr_mem.end == mr->mr_mem.start) {
211ff970d37Sskrll 			prev->mr_mem.end = mr->mr_mem.end;
212ff970d37Sskrll 			TAILQ_REMOVE(&fdt_memory_ranges, mr, mr_list);
213ff970d37Sskrll 			fdt_memory_range_free(mr);
214ff970d37Sskrll 		}
215ff970d37Sskrll 	}
216ff970d37Sskrll }
217ff970d37Sskrll 
218ff970d37Sskrll void
219ff970d37Sskrll fdt_memory_remove_range(uint64_t start, uint64_t size)
220ff970d37Sskrll {
221ff970d37Sskrll 	struct fdt_memory_range *mr, *next, *tmp;
222ff970d37Sskrll 	const uint64_t end = start + size;
223ff970d37Sskrll 
224ff970d37Sskrll 	TAILQ_FOREACH_SAFE(mr, &fdt_memory_ranges, mr_list, tmp) {
225ff970d37Sskrll 		if (start <= mr->mr_mem.start && end >= mr->mr_mem.end) {
226ff970d37Sskrll 			/*
227ff970d37Sskrll 			 * Removed range completely covers this range,
228ff970d37Sskrll 			 * just remove it.
229ff970d37Sskrll 			 */
230ff970d37Sskrll 			TAILQ_REMOVE(&fdt_memory_ranges, mr, mr_list);
231ff970d37Sskrll 			fdt_memory_range_free(mr);
232ff970d37Sskrll 		} else if (start > mr->mr_mem.start && end < mr->mr_mem.end) {
233ff970d37Sskrll 			/*
234ff970d37Sskrll 			 * Removed range is completely contained by this range,
235ff970d37Sskrll 			 * split it.
236ff970d37Sskrll 			 */
237ff970d37Sskrll 			next = fdt_memory_range_alloc();
238ff970d37Sskrll 			if (next == NULL)
239ff970d37Sskrll 				panic("fdt_memory_remove_range");
240ff970d37Sskrll 			next->mr_mem.start = end;
241ff970d37Sskrll 			next->mr_mem.end = mr->mr_mem.end;
242ff970d37Sskrll 			mr->mr_mem.end = start;
243ff970d37Sskrll 			TAILQ_INSERT_AFTER(&fdt_memory_ranges, mr, next, mr_list);
244ff970d37Sskrll 		} else if (start <= mr->mr_mem.start && end > mr->mr_mem.start && end < mr->mr_mem.end) {
245ff970d37Sskrll 			/*
246ff970d37Sskrll 			 * Partial overlap at the beginning of the range.
247ff970d37Sskrll 			 */
248ff970d37Sskrll 			mr->mr_mem.start = end;
249ff970d37Sskrll 		} else if (start > mr->mr_mem.start && start < mr->mr_mem.end && end >= mr->mr_mem.end) {
250ff970d37Sskrll 			/*
251ff970d37Sskrll 			 * Partial overlap at the end of the range.
252ff970d37Sskrll 			 */
253ff970d37Sskrll 			mr->mr_mem.end = start;
254ff970d37Sskrll 		}
255ff970d37Sskrll 		KASSERT(mr->mr_mem.start < mr->mr_mem.end);
256ff970d37Sskrll 	}
257ff970d37Sskrll }
258ff970d37Sskrll 
259ff970d37Sskrll void
260ff970d37Sskrll fdt_memory_foreach(void (*fn)(const struct fdt_memory *, void *), void *arg)
261ff970d37Sskrll {
262ff970d37Sskrll 	struct fdt_memory_range *mr;
263ff970d37Sskrll 
264ff970d37Sskrll 	TAILQ_FOREACH(mr, &fdt_memory_ranges, mr_list)
265ff970d37Sskrll 		fn(&mr->mr_mem, arg);
266ff970d37Sskrll }
267