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, path: node_path); |
15 | if (offset < 0) |
16 | return NULL; |
17 | |
18 | prop = fdt_getprop(fdt, nodeoffset: offset, name: property, lenp: &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, node_path: "/" , property: name, minlen: sizeof(fdt32_t)); |
28 | |
29 | if (!prop) { |
30 | /* default */ |
31 | return 1; |
32 | } |
33 | |
34 | return fdt32_ld(p: 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(p: cells); |
42 | if (ncells > 1) |
43 | r = (r << 32) | fdt32_ld(p: 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, name: "#address-cells" ); |
81 | size_cells = get_cells(fdt, name: "#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, node_path: "/chosen" , property: "linux,usable-memory-range" , |
91 | minlen: (addr_cells + size_cells) * sizeof(fdt32_t)); |
92 | if (usable) { |
93 | size = get_val(cells: usable + addr_cells, ncells: size_cells); |
94 | if (!size) |
95 | return mem_start; |
96 | |
97 | if (addr_cells > 1 && fdt32_ld(p: usable)) { |
98 | /* Outside 32-bit address space */ |
99 | return mem_start; |
100 | } |
101 | |
102 | usable_base = fdt32_ld(p: 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, offset: -1, NULL); offset >= 0; |
108 | offset = fdt_next_node(fdt, offset, NULL)) { |
109 | type = fdt_getprop(fdt, nodeoffset: offset, name: "device_type" , NULL); |
110 | if (!type || strcmp(type, "memory" )) |
111 | continue; |
112 | |
113 | reg = fdt_getprop(fdt, nodeoffset: offset, name: "linux,usable-memory" , lenp: &len); |
114 | if (!reg) |
115 | reg = fdt_getprop(fdt, nodeoffset: offset, name: "reg" , lenp: &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(cells: reg + addr_cells, ncells: size_cells); |
123 | if (!size) |
124 | continue; |
125 | |
126 | if (addr_cells > 1 && fdt32_ld(p: reg)) { |
127 | /* Outside 32-bit address space, skipping */ |
128 | continue; |
129 | } |
130 | |
131 | base = fdt32_ld(p: 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 | |