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