Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/boot/compressed/fdt_check_mem_start.c
29269 views
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
170