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