]>
Commit | Line | Data |
---|---|---|
12869ecd | 1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) |
1cade994 DG |
2 | /* |
3 | * libfdt - Flat Device Tree manipulation | |
4 | * Copyright (C) 2006 David Gibson, IBM Corporation. | |
1cade994 DG |
5 | */ |
6 | #include "libfdt_env.h" | |
7 | ||
8 | #include <fdt.h> | |
9 | #include <libfdt.h> | |
10 | ||
11 | #include "libfdt_internal.h" | |
12 | ||
f858927f RH |
13 | /* |
14 | * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks | |
15 | * that the given buffer contains what appears to be a flattened | |
16 | * device tree with sane information in its header. | |
17 | */ | |
0cec114e | 18 | int32_t fdt_ro_probe_(const void *fdt) |
1cade994 | 19 | { |
0cec114e RH |
20 | uint32_t totalsize = fdt_totalsize(fdt); |
21 | ||
d047cd8a RH |
22 | if (can_assume(VALID_DTB)) |
23 | return totalsize; | |
24 | ||
79edff12 RH |
25 | /* The device tree must be at an 8-byte aligned address */ |
26 | if ((uintptr_t)fdt & 7) | |
27 | return -FDT_ERR_ALIGNMENT; | |
28 | ||
1cade994 DG |
29 | if (fdt_magic(fdt) == FDT_MAGIC) { |
30 | /* Complete tree */ | |
d047cd8a RH |
31 | if (!can_assume(LATEST)) { |
32 | if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) | |
33 | return -FDT_ERR_BADVERSION; | |
34 | if (fdt_last_comp_version(fdt) > | |
35 | FDT_LAST_SUPPORTED_VERSION) | |
36 | return -FDT_ERR_BADVERSION; | |
37 | } | |
ed95d745 | 38 | } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { |
1cade994 | 39 | /* Unfinished sequential-write blob */ |
d047cd8a | 40 | if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) |
1cade994 DG |
41 | return -FDT_ERR_BADSTATE; |
42 | } else { | |
43 | return -FDT_ERR_BADMAGIC; | |
44 | } | |
45 | ||
0cec114e RH |
46 | if (totalsize < INT32_MAX) |
47 | return totalsize; | |
48 | else | |
49 | return -FDT_ERR_TRUNCATED; | |
1cade994 DG |
50 | } |
51 | ||
f858927f RH |
52 | static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) |
53 | { | |
54 | return (off >= hdrsize) && (off <= totalsize); | |
55 | } | |
56 | ||
57 | static int check_block_(uint32_t hdrsize, uint32_t totalsize, | |
58 | uint32_t base, uint32_t size) | |
59 | { | |
60 | if (!check_off_(hdrsize, totalsize, base)) | |
61 | return 0; /* block start out of bounds */ | |
62 | if ((base + size) < base) | |
63 | return 0; /* overflow */ | |
64 | if (!check_off_(hdrsize, totalsize, base + size)) | |
65 | return 0; /* block end out of bounds */ | |
66 | return 1; | |
67 | } | |
68 | ||
69 | size_t fdt_header_size_(uint32_t version) | |
70 | { | |
71 | if (version <= 1) | |
72 | return FDT_V1_SIZE; | |
73 | else if (version <= 2) | |
74 | return FDT_V2_SIZE; | |
75 | else if (version <= 3) | |
76 | return FDT_V3_SIZE; | |
77 | else if (version <= 16) | |
78 | return FDT_V16_SIZE; | |
79 | else | |
80 | return FDT_V17_SIZE; | |
81 | } | |
82 | ||
d047cd8a RH |
83 | size_t fdt_header_size(const void *fdt) |
84 | { | |
85 | return can_assume(LATEST) ? FDT_V17_SIZE : | |
86 | fdt_header_size_(fdt_version(fdt)); | |
87 | } | |
88 | ||
f858927f RH |
89 | int fdt_check_header(const void *fdt) |
90 | { | |
91 | size_t hdrsize; | |
92 | ||
93 | if (fdt_magic(fdt) != FDT_MAGIC) | |
94 | return -FDT_ERR_BADMAGIC; | |
d047cd8a RH |
95 | if (!can_assume(LATEST)) { |
96 | if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) | |
97 | || (fdt_last_comp_version(fdt) > | |
98 | FDT_LAST_SUPPORTED_VERSION)) | |
99 | return -FDT_ERR_BADVERSION; | |
100 | if (fdt_version(fdt) < fdt_last_comp_version(fdt)) | |
101 | return -FDT_ERR_BADVERSION; | |
102 | } | |
f858927f | 103 | hdrsize = fdt_header_size(fdt); |
d047cd8a | 104 | if (!can_assume(VALID_DTB)) { |
f858927f | 105 | |
d047cd8a RH |
106 | if ((fdt_totalsize(fdt) < hdrsize) |
107 | || (fdt_totalsize(fdt) > INT_MAX)) | |
108 | return -FDT_ERR_TRUNCATED; | |
f858927f | 109 | |
d047cd8a | 110 | /* Bounds check memrsv block */ |
f858927f | 111 | if (!check_off_(hdrsize, fdt_totalsize(fdt), |
d047cd8a | 112 | fdt_off_mem_rsvmap(fdt))) |
f858927f | 113 | return -FDT_ERR_TRUNCATED; |
d047cd8a RH |
114 | } |
115 | ||
116 | if (!can_assume(VALID_DTB)) { | |
117 | /* Bounds check structure block */ | |
118 | if (!can_assume(LATEST) && fdt_version(fdt) < 17) { | |
119 | if (!check_off_(hdrsize, fdt_totalsize(fdt), | |
120 | fdt_off_dt_struct(fdt))) | |
121 | return -FDT_ERR_TRUNCATED; | |
122 | } else { | |
123 | if (!check_block_(hdrsize, fdt_totalsize(fdt), | |
124 | fdt_off_dt_struct(fdt), | |
125 | fdt_size_dt_struct(fdt))) | |
126 | return -FDT_ERR_TRUNCATED; | |
127 | } | |
128 | ||
129 | /* Bounds check strings block */ | |
f858927f | 130 | if (!check_block_(hdrsize, fdt_totalsize(fdt), |
d047cd8a RH |
131 | fdt_off_dt_strings(fdt), |
132 | fdt_size_dt_strings(fdt))) | |
f858927f RH |
133 | return -FDT_ERR_TRUNCATED; |
134 | } | |
135 | ||
f858927f RH |
136 | return 0; |
137 | } | |
138 | ||
cd296721 | 139 | const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) |
1cade994 | 140 | { |
6e9c9686 RH |
141 | unsigned int uoffset = offset; |
142 | unsigned int absoffset = offset + fdt_off_dt_struct(fdt); | |
143 | ||
144 | if (offset < 0) | |
145 | return NULL; | |
91feabc2 | 146 | |
d047cd8a | 147 | if (!can_assume(VALID_INPUT)) |
6e9c9686 | 148 | if ((absoffset < uoffset) |
d047cd8a RH |
149 | || ((absoffset + len) < absoffset) |
150 | || (absoffset + len) > fdt_totalsize(fdt)) | |
151 | return NULL; | |
1cade994 | 152 | |
d047cd8a | 153 | if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) |
6e9c9686 | 154 | if (((uoffset + len) < uoffset) |
1cade994 DG |
155 | || ((offset + len) > fdt_size_dt_struct(fdt))) |
156 | return NULL; | |
157 | ||
9130ba88 | 158 | return fdt_offset_ptr_(fdt, offset); |
1cade994 DG |
159 | } |
160 | ||
cd296721 | 161 | uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) |
1cade994 | 162 | { |
47605971 | 163 | const fdt32_t *tagp, *lenp; |
1cade994 | 164 | uint32_t tag; |
cd296721 | 165 | int offset = startoffset; |
1cade994 DG |
166 | const char *p; |
167 | ||
cd296721 | 168 | *nextoffset = -FDT_ERR_TRUNCATED; |
1cade994 | 169 | tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); |
d047cd8a | 170 | if (!can_assume(VALID_DTB) && !tagp) |
1cade994 DG |
171 | return FDT_END; /* premature end */ |
172 | tag = fdt32_to_cpu(*tagp); | |
173 | offset += FDT_TAGSIZE; | |
174 | ||
cd296721 | 175 | *nextoffset = -FDT_ERR_BADSTRUCTURE; |
1cade994 DG |
176 | switch (tag) { |
177 | case FDT_BEGIN_NODE: | |
178 | /* skip name */ | |
179 | do { | |
180 | p = fdt_offset_ptr(fdt, offset++, 1); | |
181 | } while (p && (*p != '\0')); | |
d047cd8a | 182 | if (!can_assume(VALID_DTB) && !p) |
cd296721 | 183 | return FDT_END; /* premature end */ |
1cade994 | 184 | break; |
cd296721 | 185 | |
1cade994 DG |
186 | case FDT_PROP: |
187 | lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); | |
d047cd8a | 188 | if (!can_assume(VALID_DTB) && !lenp) |
cd296721 SW |
189 | return FDT_END; /* premature end */ |
190 | /* skip-name offset, length and value */ | |
191 | offset += sizeof(struct fdt_property) - FDT_TAGSIZE | |
192 | + fdt32_to_cpu(*lenp); | |
d047cd8a RH |
193 | if (!can_assume(LATEST) && |
194 | fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && | |
9130ba88 RH |
195 | ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) |
196 | offset += 4; | |
cd296721 SW |
197 | break; |
198 | ||
199 | case FDT_END: | |
200 | case FDT_END_NODE: | |
201 | case FDT_NOP: | |
1cade994 | 202 | break; |
cd296721 SW |
203 | |
204 | default: | |
205 | return FDT_END; | |
1cade994 DG |
206 | } |
207 | ||
cd296721 SW |
208 | if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) |
209 | return FDT_END; /* premature end */ | |
1cade994 | 210 | |
cd296721 | 211 | *nextoffset = FDT_TAGALIGN(offset); |
1cade994 DG |
212 | return tag; |
213 | } | |
214 | ||
9130ba88 | 215 | int fdt_check_node_offset_(const void *fdt, int offset) |
ed95d745 | 216 | { |
6e9c9686 RH |
217 | if (!can_assume(VALID_INPUT) |
218 | && ((offset < 0) || (offset % FDT_TAGSIZE))) | |
219 | return -FDT_ERR_BADOFFSET; | |
220 | ||
221 | if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE) | |
ed95d745 DG |
222 | return -FDT_ERR_BADOFFSET; |
223 | ||
224 | return offset; | |
225 | } | |
226 | ||
9130ba88 | 227 | int fdt_check_prop_offset_(const void *fdt, int offset) |
cd296721 | 228 | { |
6e9c9686 RH |
229 | if (!can_assume(VALID_INPUT) |
230 | && ((offset < 0) || (offset % FDT_TAGSIZE))) | |
231 | return -FDT_ERR_BADOFFSET; | |
232 | ||
233 | if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) | |
cd296721 SW |
234 | return -FDT_ERR_BADOFFSET; |
235 | ||
236 | return offset; | |
237 | } | |
238 | ||
ed95d745 DG |
239 | int fdt_next_node(const void *fdt, int offset, int *depth) |
240 | { | |
241 | int nextoffset = 0; | |
242 | uint32_t tag; | |
243 | ||
244 | if (offset >= 0) | |
9130ba88 | 245 | if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) |
ed95d745 DG |
246 | return nextoffset; |
247 | ||
248 | do { | |
249 | offset = nextoffset; | |
250 | tag = fdt_next_tag(fdt, offset, &nextoffset); | |
251 | ||
252 | switch (tag) { | |
253 | case FDT_PROP: | |
254 | case FDT_NOP: | |
255 | break; | |
256 | ||
257 | case FDT_BEGIN_NODE: | |
258 | if (depth) | |
259 | (*depth)++; | |
260 | break; | |
261 | ||
262 | case FDT_END_NODE: | |
cd296721 SW |
263 | if (depth && ((--(*depth)) < 0)) |
264 | return nextoffset; | |
ed95d745 DG |
265 | break; |
266 | ||
267 | case FDT_END: | |
cd296721 SW |
268 | if ((nextoffset >= 0) |
269 | || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) | |
270 | return -FDT_ERR_NOTFOUND; | |
271 | else | |
272 | return nextoffset; | |
ed95d745 DG |
273 | } |
274 | } while (tag != FDT_BEGIN_NODE); | |
275 | ||
276 | return offset; | |
277 | } | |
278 | ||
47605971 RH |
279 | int fdt_first_subnode(const void *fdt, int offset) |
280 | { | |
281 | int depth = 0; | |
282 | ||
283 | offset = fdt_next_node(fdt, offset, &depth); | |
284 | if (offset < 0 || depth != 1) | |
285 | return -FDT_ERR_NOTFOUND; | |
286 | ||
287 | return offset; | |
288 | } | |
289 | ||
290 | int fdt_next_subnode(const void *fdt, int offset) | |
291 | { | |
292 | int depth = 1; | |
293 | ||
294 | /* | |
295 | * With respect to the parent, the depth of the next subnode will be | |
296 | * the same as the last. | |
297 | */ | |
298 | do { | |
299 | offset = fdt_next_node(fdt, offset, &depth); | |
300 | if (offset < 0 || depth < 1) | |
301 | return -FDT_ERR_NOTFOUND; | |
302 | } while (depth > 1); | |
303 | ||
304 | return offset; | |
305 | } | |
306 | ||
9130ba88 | 307 | const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) |
1cade994 DG |
308 | { |
309 | int len = strlen(s) + 1; | |
310 | const char *last = strtab + tabsize - len; | |
311 | const char *p; | |
312 | ||
313 | for (p = strtab; p <= last; p++) | |
ed95d745 | 314 | if (memcmp(p, s, len) == 0) |
1cade994 DG |
315 | return p; |
316 | return NULL; | |
317 | } | |
318 | ||
319 | int fdt_move(const void *fdt, void *buf, int bufsize) | |
320 | { | |
6e9c9686 RH |
321 | if (!can_assume(VALID_INPUT) && bufsize < 0) |
322 | return -FDT_ERR_NOSPACE; | |
323 | ||
f858927f | 324 | FDT_RO_PROBE(fdt); |
1cade994 | 325 | |
6e9c9686 | 326 | if (fdt_totalsize(fdt) > (unsigned int)bufsize) |
1cade994 DG |
327 | return -FDT_ERR_NOSPACE; |
328 | ||
329 | memmove(buf, fdt, fdt_totalsize(fdt)); | |
330 | return 0; | |
331 | } |