1*08dd5822Smlelstv /* $NetBSD: fdt_memory.c,v 1.10 2024/01/14 07:53:38 mlelstv 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*08dd5822Smlelstv __KERNEL_RCSID(0, "$NetBSD: fdt_memory.c,v 1.10 2024/01/14 07:53:38 mlelstv 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 struct fdt_memory_range {
45ff970d37Sskrll struct fdt_memory mr_mem;
46ff970d37Sskrll bool mr_used;
47ff970d37Sskrll TAILQ_ENTRY(fdt_memory_range) mr_list;
48ff970d37Sskrll };
49ff970d37Sskrll
50ff970d37Sskrll static TAILQ_HEAD(fdt_memory_rangehead, fdt_memory_range) fdt_memory_ranges =
51ff970d37Sskrll TAILQ_HEAD_INITIALIZER(fdt_memory_ranges);
52ff970d37Sskrll
53ff970d37Sskrll static struct fdt_memory_range fdt_memory_range_pool[FDT_MEMORY_RANGES];
54ff970d37Sskrll
55ff970d37Sskrll static struct fdt_memory_range *
fdt_memory_range_alloc(void)56ff970d37Sskrll fdt_memory_range_alloc(void)
57ff970d37Sskrll {
58ff970d37Sskrll for (size_t n = 0; n < FDT_MEMORY_RANGES; n++)
59ff970d37Sskrll if (!fdt_memory_range_pool[n].mr_used) {
60ff970d37Sskrll fdt_memory_range_pool[n].mr_used = true;
61ff970d37Sskrll return &fdt_memory_range_pool[n];
62ff970d37Sskrll }
63ff970d37Sskrll
64ff970d37Sskrll printf("%s: no free memory ranges, increase FDT_MEMORY_RANGES!\n", __func__);
65ff970d37Sskrll return NULL;
66ff970d37Sskrll }
67ff970d37Sskrll
68ff970d37Sskrll static void
fdt_memory_range_free(struct fdt_memory_range * mr)69ff970d37Sskrll fdt_memory_range_free(struct fdt_memory_range *mr)
70ff970d37Sskrll {
71ff970d37Sskrll mr->mr_used = false;
72ff970d37Sskrll }
73ff970d37Sskrll
74ff970d37Sskrll /*
75ff970d37Sskrll * Get all of physical memory, including holes.
76ff970d37Sskrll */
77ff970d37Sskrll void
fdt_memory_get(uint64_t * pstart,uint64_t * pend)78ff970d37Sskrll fdt_memory_get(uint64_t *pstart, uint64_t *pend)
79ff970d37Sskrll {
80*08dd5822Smlelstv const void *fdt_data = fdtbus_get_data();
81ff970d37Sskrll uint64_t cur_addr, cur_size;
82*08dd5822Smlelstv int index, nadd = 0, off, memory;
83ff970d37Sskrll
84*08dd5822Smlelstv off = fdt_node_offset_by_prop_value(fdt_data, -1,
85*08dd5822Smlelstv "device_type", "memory", sizeof("memory"));
86*08dd5822Smlelstv
87*08dd5822Smlelstv /*
88*08dd5822Smlelstv * Device Tree Specification 3.2 says that memory
89*08dd5822Smlelstv * nodes are named "memory" and have device_type
90*08dd5822Smlelstv * "memory", but if the device_type is missing, try
91*08dd5822Smlelstv * to find the (then single) node by name.
92*08dd5822Smlelstv */
93*08dd5822Smlelstv if (off == -FDT_ERR_NOTFOUND)
94*08dd5822Smlelstv off = fdt_path_offset(fdt_data, "/memory");
95*08dd5822Smlelstv
96*08dd5822Smlelstv while (off != -FDT_ERR_NOTFOUND) {
97*08dd5822Smlelstv memory = fdtbus_offset2phandle(off);
98*08dd5822Smlelstv for (index = 0;
99ff970d37Sskrll fdtbus_get_reg64(memory, index, &cur_addr, &cur_size) == 0;
100ff970d37Sskrll index++) {
101c7071a70Sryo if (cur_size == 0)
102c7071a70Sryo continue;
103ff970d37Sskrll fdt_memory_add_range(cur_addr, cur_size);
104ff970d37Sskrll
1054526d8b3Sryo if (nadd++ == 0) {
106ff970d37Sskrll *pstart = cur_addr;
107ff970d37Sskrll *pend = cur_addr + cur_size;
108ff970d37Sskrll continue;
109ff970d37Sskrll }
1108a514868Smlelstv if (cur_addr < *pstart)
1118a514868Smlelstv *pstart = cur_addr;
112ff970d37Sskrll if (cur_addr + cur_size > *pend)
113ff970d37Sskrll *pend = cur_addr + cur_size;
114ff970d37Sskrll }
115*08dd5822Smlelstv off = fdt_node_offset_by_prop_value(fdt_data, off,
116*08dd5822Smlelstv "device_type", "memory", sizeof("memory"));
117*08dd5822Smlelstv }
1184526d8b3Sryo if (nadd == 0)
119ff970d37Sskrll panic("Cannot determine memory size");
120ff970d37Sskrll }
121ff970d37Sskrll
122ff970d37Sskrll /*
123ff970d37Sskrll * Exclude memory ranges from memory config from the device tree
124ff970d37Sskrll */
125ff970d37Sskrll void
fdt_memory_remove_reserved(uint64_t min_addr,uint64_t max_addr)126ff970d37Sskrll fdt_memory_remove_reserved(uint64_t min_addr, uint64_t max_addr)
127ff970d37Sskrll {
128ff970d37Sskrll uint64_t lstart = 0, lend = 0;
129a18fd2f5Sryo int index, error, phandle, child;
13013ecc471Sskrll const void *fdt_data = fdtbus_get_data();
13113ecc471Sskrll const int num = fdt_num_mem_rsv(fdt_data);
132ff970d37Sskrll
133ff970d37Sskrll for (index = 0; index <= num; index++) {
134b5110628Sjmcneill uint64_t addr, size;
135b5110628Sjmcneill
13613ecc471Sskrll error = fdt_get_mem_rsv(fdt_data, index, &addr, &size);
137ff970d37Sskrll if (error != 0)
138ff970d37Sskrll continue;
13913ecc471Sskrll
140ff970d37Sskrll if (lstart <= addr && addr <= lend) {
141ff970d37Sskrll size -= (lend - addr);
142ff970d37Sskrll addr = lend;
143ff970d37Sskrll }
144ff970d37Sskrll if (size == 0)
145ff970d37Sskrll continue;
146ff970d37Sskrll if (addr + size <= min_addr)
147ff970d37Sskrll continue;
148ff970d37Sskrll if (addr >= max_addr)
149ff970d37Sskrll continue;
150ff970d37Sskrll if (addr < min_addr) {
151ff970d37Sskrll size -= (min_addr - addr);
152ff970d37Sskrll addr = min_addr;
153ff970d37Sskrll }
154ff970d37Sskrll if (addr + size > max_addr)
155ff970d37Sskrll size = max_addr - addr;
156ff970d37Sskrll fdt_memory_remove_range(addr, size);
157ff970d37Sskrll lstart = addr;
158ff970d37Sskrll lend = addr + size;
159ff970d37Sskrll }
160a18fd2f5Sryo
161a18fd2f5Sryo /*
162a18fd2f5Sryo * "no-map" ranges defined in the /reserved-memory node
163a18fd2f5Sryo * must also be excluded.
164a18fd2f5Sryo */
165a18fd2f5Sryo phandle = OF_finddevice("/reserved-memory");
166a18fd2f5Sryo if (phandle != -1) {
167a18fd2f5Sryo for (child = OF_child(phandle); child; child = OF_peer(child)) {
168b5110628Sjmcneill bus_addr_t addr;
169b5110628Sjmcneill bus_size_t size;
170b5110628Sjmcneill
171a18fd2f5Sryo if (fdtbus_get_reg(child, 0, &addr, &size) != 0)
172a18fd2f5Sryo continue;
173a18fd2f5Sryo if (size == 0)
174a18fd2f5Sryo continue;
175a18fd2f5Sryo fdt_memory_remove_range(addr, size);
176a18fd2f5Sryo }
177a18fd2f5Sryo }
178ff970d37Sskrll }
179ff970d37Sskrll
180ff970d37Sskrll void
fdt_memory_add_range(uint64_t start,uint64_t size)181ff970d37Sskrll fdt_memory_add_range(uint64_t start, uint64_t size)
182ff970d37Sskrll {
183ff970d37Sskrll struct fdt_memory_range *mr, *prev, *cur, *tmp;
184ff970d37Sskrll bool inserted = false;
185ff970d37Sskrll
186ff970d37Sskrll mr = fdt_memory_range_alloc();
187ff970d37Sskrll if (mr == NULL)
188ff970d37Sskrll return;
189ff970d37Sskrll
190ff970d37Sskrll mr->mr_mem.start = start;
191ff970d37Sskrll mr->mr_mem.end = start + size;
192ff970d37Sskrll
193ff970d37Sskrll /*
194ff970d37Sskrll * Add the new range to the list of sorted ranges.
195ff970d37Sskrll */
196ff970d37Sskrll TAILQ_FOREACH(cur, &fdt_memory_ranges, mr_list)
197ff970d37Sskrll if (mr->mr_mem.start <= cur->mr_mem.start) {
198ff970d37Sskrll TAILQ_INSERT_BEFORE(cur, mr, mr_list);
199ff970d37Sskrll inserted = true;
200ff970d37Sskrll break;
201ff970d37Sskrll }
202ff970d37Sskrll if (!inserted)
203ff970d37Sskrll TAILQ_INSERT_TAIL(&fdt_memory_ranges, mr, mr_list);
204ff970d37Sskrll
205ff970d37Sskrll /*
206ff970d37Sskrll * Remove overlaps.
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 mr->mr_mem.start = prev->mr_mem.end;
212ff970d37Sskrll if (mr->mr_mem.start >= mr->mr_mem.end) {
213ff970d37Sskrll TAILQ_REMOVE(&fdt_memory_ranges, mr, mr_list);
214ff970d37Sskrll fdt_memory_range_free(mr);
215ff970d37Sskrll }
216ff970d37Sskrll }
217ff970d37Sskrll }
218ff970d37Sskrll
219ff970d37Sskrll /*
220ff970d37Sskrll * Combine adjacent ranges.
221ff970d37Sskrll */
222ff970d37Sskrll TAILQ_FOREACH_SAFE(mr, &fdt_memory_ranges, mr_list, tmp) {
223ff970d37Sskrll prev = TAILQ_PREV(mr, fdt_memory_rangehead, mr_list);
224ff970d37Sskrll if (prev && prev->mr_mem.end == mr->mr_mem.start) {
225ff970d37Sskrll prev->mr_mem.end = mr->mr_mem.end;
226ff970d37Sskrll TAILQ_REMOVE(&fdt_memory_ranges, mr, mr_list);
227ff970d37Sskrll fdt_memory_range_free(mr);
228ff970d37Sskrll }
229ff970d37Sskrll }
230ff970d37Sskrll }
231ff970d37Sskrll
232ff970d37Sskrll void
fdt_memory_remove_range(uint64_t start,uint64_t size)233ff970d37Sskrll fdt_memory_remove_range(uint64_t start, uint64_t size)
234ff970d37Sskrll {
235ff970d37Sskrll struct fdt_memory_range *mr, *next, *tmp;
236ff970d37Sskrll const uint64_t end = start + size;
237ff970d37Sskrll
238ff970d37Sskrll TAILQ_FOREACH_SAFE(mr, &fdt_memory_ranges, mr_list, tmp) {
239ff970d37Sskrll if (start <= mr->mr_mem.start && end >= mr->mr_mem.end) {
240ff970d37Sskrll /*
241ff970d37Sskrll * Removed range completely covers this range,
242ff970d37Sskrll * just remove it.
243ff970d37Sskrll */
244ff970d37Sskrll TAILQ_REMOVE(&fdt_memory_ranges, mr, mr_list);
245ff970d37Sskrll fdt_memory_range_free(mr);
246ff970d37Sskrll } else if (start > mr->mr_mem.start && end < mr->mr_mem.end) {
247ff970d37Sskrll /*
248ff970d37Sskrll * Removed range is completely contained by this range,
249ff970d37Sskrll * split it.
250ff970d37Sskrll */
251ff970d37Sskrll next = fdt_memory_range_alloc();
252ff970d37Sskrll if (next == NULL)
253ff970d37Sskrll panic("fdt_memory_remove_range");
254ff970d37Sskrll next->mr_mem.start = end;
255ff970d37Sskrll next->mr_mem.end = mr->mr_mem.end;
256ff970d37Sskrll mr->mr_mem.end = start;
257ff970d37Sskrll TAILQ_INSERT_AFTER(&fdt_memory_ranges, mr, next, mr_list);
258ff970d37Sskrll } else if (start <= mr->mr_mem.start && end > mr->mr_mem.start && end < mr->mr_mem.end) {
259ff970d37Sskrll /*
260ff970d37Sskrll * Partial overlap at the beginning of the range.
261ff970d37Sskrll */
262ff970d37Sskrll mr->mr_mem.start = end;
263ff970d37Sskrll } else if (start > mr->mr_mem.start && start < mr->mr_mem.end && end >= mr->mr_mem.end) {
264ff970d37Sskrll /*
265ff970d37Sskrll * Partial overlap at the end of the range.
266ff970d37Sskrll */
267ff970d37Sskrll mr->mr_mem.end = start;
268ff970d37Sskrll }
269ff970d37Sskrll KASSERT(mr->mr_mem.start < mr->mr_mem.end);
270ff970d37Sskrll }
271ff970d37Sskrll }
272ff970d37Sskrll
273ff970d37Sskrll void
fdt_memory_foreach(void (* fn)(const struct fdt_memory *,void *),void * arg)274ff970d37Sskrll fdt_memory_foreach(void (*fn)(const struct fdt_memory *, void *), void *arg)
275ff970d37Sskrll {
276ff970d37Sskrll struct fdt_memory_range *mr;
277ff970d37Sskrll
278ff970d37Sskrll TAILQ_FOREACH(mr, &fdt_memory_ranges, mr_list)
279ff970d37Sskrll fn(&mr->mr_mem, arg);
280ff970d37Sskrll }
281