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