1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/kernel.h> 4 #include <linux/libfdt.h> 5 #include <linux/sizes.h> 6 #include "misc.h" 7 8 static const void *get_prop(const void *fdt, const char *node_path, 9 const char *property, int minlen) 10 { 11 const void *prop; 12 int offset, len; 13 14 offset = fdt_path_offset(fdt, node_path); 15 if (offset < 0) 16 return NULL; 17 18 prop = fdt_getprop(fdt, offset, property, &len); 19 if (!prop || len < minlen) 20 return NULL; 21 22 return prop; 23 } 24 25 static uint32_t get_cells(const void *fdt, const char *name) 26 { 27 const fdt32_t *prop = get_prop(fdt, "/", name, sizeof(fdt32_t)); 28 29 if (!prop) { 30 /* default */ 31 return 1; 32 } 33 34 return fdt32_ld(prop); 35 } 36 37 static uint64_t get_val(const fdt32_t *cells, uint32_t ncells) 38 { 39 uint64_t r; 40 41 r = fdt32_ld(cells); 42 if (ncells > 1) 43 r = (r << 32) | fdt32_ld(cells + 1); 44 45 return r; 46 } 47 48 /* 49 * Check the start of physical memory 50 * 51 * Traditionally, the start address of physical memory is obtained by masking 52 * the program counter. However, this does require that this address is a 53 * multiple of 128 MiB, precluding booting Linux on platforms where this 54 * requirement is not fulfilled. 55 * Hence validate the calculated address against the memory information in the 56 * DTB, and, if out-of-range, replace it by the real start address. 57 * To preserve backwards compatibility (systems reserving a block of memory 58 * at the start of physical memory, kdump, ...), the traditional method is 59 * used if it yields a valid address, unless the "linux,usable-memory-range" 60 * property is present. 61 * 62 * Return value: start address of physical memory to use 63 */ 64 uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt) 65 { 66 uint32_t addr_cells, size_cells, usable_base, base; 67 uint32_t fdt_mem_start = 0xffffffff; 68 const fdt32_t *usable, *reg, *endp; 69 uint64_t size, usable_end, end; 70 const char *type; 71 int offset, len; 72 73 if (!fdt) 74 return mem_start; 75 76 if (fdt_magic(fdt) != FDT_MAGIC) 77 return mem_start; 78 79 /* There may be multiple cells on LPAE platforms */ 80 addr_cells = get_cells(fdt, "#address-cells"); 81 size_cells = get_cells(fdt, "#size-cells"); 82 if (addr_cells > 2 || size_cells > 2) 83 return mem_start; 84 85 /* 86 * Usable memory in case of a crash dump kernel 87 * This property describes a limitation: memory within this range is 88 * only valid when also described through another mechanism 89 */ 90 usable = get_prop(fdt, "/chosen", "linux,usable-memory-range", 91 (addr_cells + size_cells) * sizeof(fdt32_t)); 92 if (usable) { 93 size = get_val(usable + addr_cells, size_cells); 94 if (!size) 95 return mem_start; 96 97 if (addr_cells > 1 && fdt32_ld(usable)) { 98 /* Outside 32-bit address space */ 99 return mem_start; 100 } 101 102 usable_base = fdt32_ld(usable + addr_cells - 1); 103 usable_end = usable_base + size; 104 } 105 106 /* Walk all memory nodes and regions */ 107 for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0; 108 offset = fdt_next_node(fdt, offset, NULL)) { 109 type = fdt_getprop(fdt, offset, "device_type", NULL); 110 if (!type || strcmp(type, "memory")) 111 continue; 112 113 reg = fdt_getprop(fdt, offset, "linux,usable-memory", &len); 114 if (!reg) 115 reg = fdt_getprop(fdt, offset, "reg", &len); 116 if (!reg) 117 continue; 118 119 for (endp = reg + (len / sizeof(fdt32_t)); 120 endp - reg >= addr_cells + size_cells; 121 reg += addr_cells + size_cells) { 122 size = get_val(reg + addr_cells, size_cells); 123 if (!size) 124 continue; 125 126 if (addr_cells > 1 && fdt32_ld(reg)) { 127 /* Outside 32-bit address space, skipping */ 128 continue; 129 } 130 131 base = fdt32_ld(reg + addr_cells - 1); 132 end = base + size; 133 if (usable) { 134 /* 135 * Clip to usable range, which takes precedence 136 * over mem_start 137 */ 138 if (base < usable_base) 139 base = usable_base; 140 141 if (end > usable_end) 142 end = usable_end; 143 144 if (end <= base) 145 continue; 146 } else if (mem_start >= base && mem_start < end) { 147 /* Calculated address is valid, use it */ 148 return mem_start; 149 } 150 151 if (base < fdt_mem_start) 152 fdt_mem_start = base; 153 } 154 } 155 156 if (fdt_mem_start == 0xffffffff) { 157 /* No usable memory found, falling back to default */ 158 return mem_start; 159 } 160 161 /* 162 * The calculated address is not usable, or was overridden by the 163 * "linux,usable-memory-range" property. 164 * Use the lowest usable physical memory address from the DTB instead, 165 * and make sure this is a multiple of 2 MiB for phys/virt patching. 166 */ 167 return round_up(fdt_mem_start, SZ_2M); 168 } 169
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.