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