blob: be03aea9eb006f4bf9d3f908003bd30ef1ca9363 [file] [log] [blame]
Tom Rini4549e782018-05-06 18:27:01 -04001// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
Gerald Van Baren35748172007-03-31 12:00:56 -04002/*
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
Gerald Van Baren35748172007-03-31 12:00:56 -04005 */
Masahiro Yamadab08c8c42018-03-05 01:20:11 +09006#include <linux/libfdt_env.h>
Gerald Van Baren35748172007-03-31 12:00:56 -04007
Bartlomiej Sieka8cf30802008-02-29 16:00:24 +01008#ifndef USE_HOSTCC
Gerald Van Baren35748172007-03-31 12:00:56 -04009#include <fdt.h>
Masahiro Yamadab08c8c42018-03-05 01:20:11 +090010#include <linux/libfdt.h>
Bartlomiej Sieka8cf30802008-02-29 16:00:24 +010011#else
12#include "fdt_host.h"
13#endif
Gerald Van Baren35748172007-03-31 12:00:56 -040014
15#include "libfdt_internal.h"
16
Simon Glassf0921f52019-10-27 09:47:42 -060017static int fdt_nodename_eq_(const void *fdt, int offset,
David Gibsonfc7758e2008-07-09 14:10:24 +100018 const char *s, int len)
Gerald Van Baren35748172007-03-31 12:00:56 -040019{
Simon Glassf0921f52019-10-27 09:47:42 -060020 int olen;
21 const char *p = fdt_get_name(fdt, offset, &olen);
Gerald Van Baren35748172007-03-31 12:00:56 -040022
Simon Glassf0921f52019-10-27 09:47:42 -060023 if (!p || (fdt_chk_extra() && olen < len))
Gerald Van Baren35748172007-03-31 12:00:56 -040024 /* short match */
25 return 0;
26
27 if (memcmp(p, s, len) != 0)
28 return 0;
29
Kumar Gala8d04f022007-10-24 11:04:22 -050030 if (p[len] == '\0')
31 return 1;
32 else if (!memchr(s, '@', len) && (p[len] == '@'))
33 return 1;
34 else
Gerald Van Baren35748172007-03-31 12:00:56 -040035 return 0;
Gerald Van Baren35748172007-03-31 12:00:56 -040036}
37
Simon Glassf0921f52019-10-27 09:47:42 -060038const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
Gerald Van Baren35748172007-03-31 12:00:56 -040039{
Simon Glassf0921f52019-10-27 09:47:42 -060040 int32_t totalsize;
41 uint32_t absoffset;
42 size_t len;
43 int err;
44 const char *s, *n;
Gerald Van Baren35748172007-03-31 12:00:56 -040045
Simon Glassf0921f52019-10-27 09:47:42 -060046 if (!fdt_chk_extra()) {
47 s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
David Gibson02193992008-08-06 14:50:49 +100048
Simon Glassf0921f52019-10-27 09:47:42 -060049 if (lenp)
50 *lenp = strlen(s);
51 return s;
52 }
53 totalsize = fdt_ro_probe_(fdt);
54 err = totalsize;
55 if (totalsize < 0)
56 goto fail;
David Gibson02193992008-08-06 14:50:49 +100057
Simon Glassf0921f52019-10-27 09:47:42 -060058 err = -FDT_ERR_BADOFFSET;
59 absoffset = stroffset + fdt_off_dt_strings(fdt);
60 if (absoffset >= totalsize)
61 goto fail;
62 len = totalsize - absoffset;
Maxime Ripard57c78092016-07-05 10:26:39 +020063
Simon Glassf0921f52019-10-27 09:47:42 -060064 if (fdt_magic(fdt) == FDT_MAGIC) {
65 if (stroffset < 0)
66 goto fail;
67 if (!fdt_chk_version() || fdt_version(fdt) >= 17) {
68 if (stroffset >= fdt_size_dt_strings(fdt))
69 goto fail;
70 if ((fdt_size_dt_strings(fdt) - stroffset) < len)
71 len = fdt_size_dt_strings(fdt) - stroffset;
72 }
73 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
74 if ((stroffset >= 0)
75 || (stroffset < -fdt_size_dt_strings(fdt)))
76 goto fail;
77 if ((-stroffset) < len)
78 len = -stroffset;
79 } else {
80 err = -FDT_ERR_INTERNAL;
81 goto fail;
Maxime Ripard57c78092016-07-05 10:26:39 +020082 }
83
Simon Glassf0921f52019-10-27 09:47:42 -060084 s = (const char *)fdt + absoffset;
85 n = memchr(s, '\0', len);
86 if (!n) {
87 /* missing terminating NULL */
88 err = -FDT_ERR_TRUNCATED;
89 goto fail;
90 }
91
92 if (lenp)
93 *lenp = n - s;
94 return s;
95
96fail:
97 if (lenp)
98 *lenp = err;
99 return NULL;
Maxime Ripard57c78092016-07-05 10:26:39 +0200100}
101
Simon Glassf0921f52019-10-27 09:47:42 -0600102const char *fdt_string(const void *fdt, int stroffset)
103{
104 return fdt_get_string(fdt, stroffset, NULL);
105}
106
107static int fdt_string_eq_(const void *fdt, int stroffset,
108 const char *s, int len)
109{
110 int slen;
111 const char *p = fdt_get_string(fdt, stroffset, &slen);
112
113 return p && (slen == len) && (memcmp(p, s, len) == 0);
114}
115
116int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
Thierry Redingea1df3e2019-03-21 19:09:58 +0100117{
118 uint32_t max = 0;
119 int offset = -1;
120
121 while (true) {
122 uint32_t value;
123
124 offset = fdt_next_node(fdt, offset, NULL);
125 if (offset < 0) {
126 if (offset == -FDT_ERR_NOTFOUND)
127 break;
128
129 return offset;
130 }
131
132 value = fdt_get_phandle(fdt, offset);
133
134 if (value > max)
135 max = value;
136 }
137
Simon Glassf0921f52019-10-27 09:47:42 -0600138 if (phandle)
139 *phandle = max;
140
141 return 0;
142}
143
144int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
145{
146 uint32_t max;
147 int err;
148
149 err = fdt_find_max_phandle(fdt, &max);
150 if (err < 0)
151 return err;
152
Thierry Redingea1df3e2019-03-21 19:09:58 +0100153 if (max == FDT_MAX_PHANDLE)
154 return -FDT_ERR_NOPHANDLES;
155
156 if (phandle)
157 *phandle = max + 1;
158
159 return 0;
160}
161
Simon Glassf0921f52019-10-27 09:47:42 -0600162static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
163{
164 int offset = n * sizeof(struct fdt_reserve_entry);
165 int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
166
167 if (fdt_chk_extra()) {
168 if (absoffset < fdt_off_mem_rsvmap(fdt))
169 return NULL;
170 if (absoffset > fdt_totalsize(fdt) -
171 sizeof(struct fdt_reserve_entry))
172 return NULL;
173 }
174 return fdt_mem_rsv_(fdt, n);
175}
176
Kumar Gala8d04f022007-10-24 11:04:22 -0500177int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
178{
Simon Glassf0921f52019-10-27 09:47:42 -0600179 const struct fdt_reserve_entry *re;
180
181 FDT_RO_PROBE(fdt);
182 re = fdt_mem_rsv(fdt, n);
183 if (fdt_chk_extra() && !re)
184 return -FDT_ERR_BADOFFSET;
185
Tom Rinie8c2d252020-01-27 12:10:31 -0500186 *address = fdt64_to_cpu(re->address);
187 *size = fdt64_to_cpu(re->size);
Kumar Gala8d04f022007-10-24 11:04:22 -0500188 return 0;
189}
190
191int fdt_num_mem_rsv(const void *fdt)
192{
Simon Glassf0921f52019-10-27 09:47:42 -0600193 int i;
194 const struct fdt_reserve_entry *re;
Kumar Gala8d04f022007-10-24 11:04:22 -0500195
Simon Glassf0921f52019-10-27 09:47:42 -0600196 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
Tom Rinie8c2d252020-01-27 12:10:31 -0500197 if (fdt64_to_cpu(re->size) == 0)
Simon Glassf0921f52019-10-27 09:47:42 -0600198 return i;
199 }
200 return -FDT_ERR_TRUNCATED;
Kumar Gala8d04f022007-10-24 11:04:22 -0500201}
202
Simon Glassf0921f52019-10-27 09:47:42 -0600203static int nextprop_(const void *fdt, int offset)
David Gibsond1c63142010-03-09 17:39:14 +1100204{
205 uint32_t tag;
206 int nextoffset;
207
208 do {
209 tag = fdt_next_tag(fdt, offset, &nextoffset);
210
211 switch (tag) {
212 case FDT_END:
213 if (nextoffset >= 0)
214 return -FDT_ERR_BADSTRUCTURE;
215 else
216 return nextoffset;
217
218 case FDT_PROP:
219 return offset;
220 }
221 offset = nextoffset;
222 } while (tag == FDT_NOP);
223
224 return -FDT_ERR_NOTFOUND;
225}
226
David Gibsonae0b5902008-02-12 11:58:31 +1100227int fdt_subnode_offset_namelen(const void *fdt, int offset,
Gerald Van Baren35748172007-03-31 12:00:56 -0400228 const char *name, int namelen)
229{
David Gibson2c0b8432009-02-06 14:01:56 +1100230 int depth;
Gerald Van Baren35748172007-03-31 12:00:56 -0400231
Simon Glassf0921f52019-10-27 09:47:42 -0600232 FDT_RO_PROBE(fdt);
Gerald Van Baren35748172007-03-31 12:00:56 -0400233
David Gibson2c0b8432009-02-06 14:01:56 +1100234 for (depth = 0;
235 (offset >= 0) && (depth >= 0);
236 offset = fdt_next_node(fdt, offset, &depth))
237 if ((depth == 1)
Simon Glassf0921f52019-10-27 09:47:42 -0600238 && fdt_nodename_eq_(fdt, offset, name, namelen))
David Gibsonae0b5902008-02-12 11:58:31 +1100239 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400240
David Gibson2c0b8432009-02-06 14:01:56 +1100241 if (depth < 0)
David Gibson4bc7dee2008-10-29 23:27:45 -0500242 return -FDT_ERR_NOTFOUND;
David Gibson2c0b8432009-02-06 14:01:56 +1100243 return offset; /* error */
Gerald Van Baren35748172007-03-31 12:00:56 -0400244}
245
246int fdt_subnode_offset(const void *fdt, int parentoffset,
247 const char *name)
248{
249 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
250}
251
Maxime Ripard8e968572016-07-05 10:26:41 +0200252int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
Gerald Van Baren35748172007-03-31 12:00:56 -0400253{
Maxime Ripard8e968572016-07-05 10:26:41 +0200254 const char *end = path + namelen;
Gerald Van Baren35748172007-03-31 12:00:56 -0400255 const char *p = path;
256 int offset = 0;
257
Simon Glassf0921f52019-10-27 09:47:42 -0600258 FDT_RO_PROBE(fdt);
Gerald Van Baren35748172007-03-31 12:00:56 -0400259
Kumar Galafeeca3f2008-08-14 08:28:19 -0500260 /* see if we have an alias */
261 if (*path != '/') {
Simon Glassf0921f52019-10-27 09:47:42 -0600262 const char *q = memchr(path, '/', end - p);
Kumar Galafeeca3f2008-08-14 08:28:19 -0500263
Kumar Galafeeca3f2008-08-14 08:28:19 -0500264 if (!q)
265 q = end;
266
David Gibson9a6cf732008-08-20 16:55:14 +1000267 p = fdt_get_alias_namelen(fdt, p, q - p);
Kumar Galafeeca3f2008-08-14 08:28:19 -0500268 if (!p)
269 return -FDT_ERR_BADPATH;
270 offset = fdt_path_offset(fdt, p);
271
272 p = q;
273 }
Gerald Van Baren35748172007-03-31 12:00:56 -0400274
Simon Glassf0921f52019-10-27 09:47:42 -0600275 while (p < end) {
Gerald Van Baren35748172007-03-31 12:00:56 -0400276 const char *q;
277
Simon Glassf0921f52019-10-27 09:47:42 -0600278 while (*p == '/') {
Gerald Van Baren35748172007-03-31 12:00:56 -0400279 p++;
Simon Glassf0921f52019-10-27 09:47:42 -0600280 if (p == end)
281 return offset;
282 }
283 q = memchr(p, '/', end - p);
284 if (! q)
Gerald Van Baren35748172007-03-31 12:00:56 -0400285 q = end;
286
287 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
288 if (offset < 0)
289 return offset;
290
291 p = q;
292 }
293
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400294 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400295}
296
Simon Glass42b76002016-10-02 17:59:30 -0600297int fdt_path_offset(const void *fdt, const char *path)
298{
299 return fdt_path_offset_namelen(fdt, path, strlen(path));
300}
301
Kumar Gala8d04f022007-10-24 11:04:22 -0500302const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
Gerald Van Baren35748172007-03-31 12:00:56 -0400303{
Rob Herringdb405d12018-05-19 14:13:53 +0200304 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
Simon Glassf0921f52019-10-27 09:47:42 -0600305 const char *nameptr;
Kumar Gala8d04f022007-10-24 11:04:22 -0500306 int err;
307
Simon Glassf0921f52019-10-27 09:47:42 -0600308 if (fdt_chk_extra() &&
309 (((err = fdt_ro_probe_(fdt)) < 0)
310 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
311 goto fail;
312
313 nameptr = nh->name;
314
315 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
316 /*
317 * For old FDT versions, match the naming conventions of V16:
318 * give only the leaf name (after all /). The actual tree
319 * contents are loosely checked.
320 */
321 const char *leaf;
322 leaf = strrchr(nameptr, '/');
323 if (leaf == NULL) {
324 err = -FDT_ERR_BADSTRUCTURE;
David Gibson2f08bfa2008-05-20 17:19:11 +1000325 goto fail;
Simon Glassf0921f52019-10-27 09:47:42 -0600326 }
327 nameptr = leaf+1;
328 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500329
330 if (len)
Simon Glassf0921f52019-10-27 09:47:42 -0600331 *len = strlen(nameptr);
Kumar Gala8d04f022007-10-24 11:04:22 -0500332
Simon Glassf0921f52019-10-27 09:47:42 -0600333 return nameptr;
Kumar Gala8d04f022007-10-24 11:04:22 -0500334
335 fail:
336 if (len)
337 *len = err;
338 return NULL;
339}
340
David Gibsond1c63142010-03-09 17:39:14 +1100341int fdt_first_property_offset(const void *fdt, int nodeoffset)
342{
343 int offset;
344
Rob Herringdb405d12018-05-19 14:13:53 +0200345 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
David Gibsond1c63142010-03-09 17:39:14 +1100346 return offset;
347
Simon Glassf0921f52019-10-27 09:47:42 -0600348 return nextprop_(fdt, offset);
David Gibsond1c63142010-03-09 17:39:14 +1100349}
350
351int fdt_next_property_offset(const void *fdt, int offset)
352{
Rob Herringdb405d12018-05-19 14:13:53 +0200353 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
David Gibsond1c63142010-03-09 17:39:14 +1100354 return offset;
355
Simon Glassf0921f52019-10-27 09:47:42 -0600356 return nextprop_(fdt, offset);
David Gibsond1c63142010-03-09 17:39:14 +1100357}
358
Simon Glassf0921f52019-10-27 09:47:42 -0600359static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
360 int offset,
361 int *lenp)
David Gibsond1c63142010-03-09 17:39:14 +1100362{
363 int err;
364 const struct fdt_property *prop;
365
Simon Glassf0921f52019-10-27 09:47:42 -0600366 if (fdt_chk_basic() && (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
David Gibsond1c63142010-03-09 17:39:14 +1100367 if (lenp)
368 *lenp = err;
369 return NULL;
370 }
371
Rob Herringdb405d12018-05-19 14:13:53 +0200372 prop = fdt_offset_ptr_(fdt, offset);
David Gibsond1c63142010-03-09 17:39:14 +1100373
374 if (lenp)
Tom Rinie8c2d252020-01-27 12:10:31 -0500375 *lenp = fdt32_to_cpu(prop->len);
David Gibsond1c63142010-03-09 17:39:14 +1100376
377 return prop;
378}
379
Simon Glassf0921f52019-10-27 09:47:42 -0600380const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
381 int offset,
382 int *lenp)
383{
384 /* Prior to version 16, properties may need realignment
385 * and this API does not work. fdt_getprop_*() will, however. */
386
387 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
388 if (lenp)
389 *lenp = -FDT_ERR_BADVERSION;
390 return NULL;
391 }
392
393 return fdt_get_property_by_offset_(fdt, offset, lenp);
394}
395
396static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
397 int offset,
398 const char *name,
399 int namelen,
400 int *lenp,
401 int *poffset)
Kumar Gala8d04f022007-10-24 11:04:22 -0500402{
David Gibsond1c63142010-03-09 17:39:14 +1100403 for (offset = fdt_first_property_offset(fdt, offset);
404 (offset >= 0);
405 (offset = fdt_next_property_offset(fdt, offset))) {
406 const struct fdt_property *prop;
Gerald Van Baren35748172007-03-31 12:00:56 -0400407
Simon Glassf0921f52019-10-27 09:47:42 -0600408 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
409 if (fdt_chk_extra() && !prop) {
David Gibsond1c63142010-03-09 17:39:14 +1100410 offset = -FDT_ERR_INTERNAL;
Gerald Van Baren35748172007-03-31 12:00:56 -0400411 break;
Gerald Van Baren35748172007-03-31 12:00:56 -0400412 }
Tom Rinie8c2d252020-01-27 12:10:31 -0500413 if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
Simon Glassf0921f52019-10-27 09:47:42 -0600414 name, namelen)) {
415 if (poffset)
416 *poffset = offset;
David Gibsond1c63142010-03-09 17:39:14 +1100417 return prop;
Simon Glassf0921f52019-10-27 09:47:42 -0600418 }
David Gibsond1c63142010-03-09 17:39:14 +1100419 }
Gerald Van Baren35748172007-03-31 12:00:56 -0400420
Gerald Van Baren35748172007-03-31 12:00:56 -0400421 if (lenp)
David Gibsond1c63142010-03-09 17:39:14 +1100422 *lenp = offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400423 return NULL;
424}
425
Simon Glassf0921f52019-10-27 09:47:42 -0600426
427const struct fdt_property *fdt_get_property_namelen(const void *fdt,
428 int offset,
429 const char *name,
430 int namelen, int *lenp)
431{
432 /* Prior to version 16, properties may need realignment
433 * and this API does not work. fdt_getprop_*() will, however. */
434 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
435 if (lenp)
436 *lenp = -FDT_ERR_BADVERSION;
437 return NULL;
438 }
439
440 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
441 NULL);
442}
443
444
David Gibson02193992008-08-06 14:50:49 +1000445const struct fdt_property *fdt_get_property(const void *fdt,
446 int nodeoffset,
447 const char *name, int *lenp)
448{
449 return fdt_get_property_namelen(fdt, nodeoffset, name,
450 strlen(name), lenp);
451}
452
453const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
454 const char *name, int namelen, int *lenp)
Gerald Van Baren35748172007-03-31 12:00:56 -0400455{
Simon Glassf0921f52019-10-27 09:47:42 -0600456 int poffset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400457 const struct fdt_property *prop;
458
Simon Glassf0921f52019-10-27 09:47:42 -0600459 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
460 &poffset);
Robert P. J. Day6feed2a2016-05-23 05:40:55 -0400461 if (!prop)
Gerald Van Baren35748172007-03-31 12:00:56 -0400462 return NULL;
463
Simon Glassf0921f52019-10-27 09:47:42 -0600464 /* Handle realignment */
465 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
Tom Rinie8c2d252020-01-27 12:10:31 -0500466 (poffset + sizeof(*prop)) % 8 && fdt32_to_cpu(prop->len) >= 8)
Simon Glassf0921f52019-10-27 09:47:42 -0600467 return prop->data + 4;
Kumar Gala8d04f022007-10-24 11:04:22 -0500468 return prop->data;
Gerald Van Baren35748172007-03-31 12:00:56 -0400469}
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400470
David Gibsond1c63142010-03-09 17:39:14 +1100471const void *fdt_getprop_by_offset(const void *fdt, int offset,
472 const char **namep, int *lenp)
473{
474 const struct fdt_property *prop;
475
Simon Glassf0921f52019-10-27 09:47:42 -0600476 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
David Gibsond1c63142010-03-09 17:39:14 +1100477 if (!prop)
478 return NULL;
Simon Glassf0921f52019-10-27 09:47:42 -0600479 if (namep) {
480 const char *name;
481 int namelen;
482
483 if (fdt_chk_extra()) {
Tom Rinie8c2d252020-01-27 12:10:31 -0500484 name = fdt_get_string(fdt, fdt32_to_cpu(prop->nameoff),
Simon Glassf0921f52019-10-27 09:47:42 -0600485 &namelen);
486 if (!name) {
487 if (lenp)
488 *lenp = namelen;
489 return NULL;
490 }
491 *namep = name;
492 } else {
Tom Rinie8c2d252020-01-27 12:10:31 -0500493 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
Simon Glassf0921f52019-10-27 09:47:42 -0600494 }
495 }
496
497 /* Handle realignment */
498 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
Tom Rinie8c2d252020-01-27 12:10:31 -0500499 (offset + sizeof(*prop)) % 8 && fdt32_to_cpu(prop->len) >= 8)
Simon Glassf0921f52019-10-27 09:47:42 -0600500 return prop->data + 4;
David Gibsond1c63142010-03-09 17:39:14 +1100501 return prop->data;
502}
503
David Gibson02193992008-08-06 14:50:49 +1000504const void *fdt_getprop(const void *fdt, int nodeoffset,
505 const char *name, int *lenp)
506{
507 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
508}
509
Kumar Gala8d04f022007-10-24 11:04:22 -0500510uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400511{
Kim Phillipsb2ba62a2013-01-16 13:59:50 +0000512 const fdt32_t *php;
Kumar Gala8d04f022007-10-24 11:04:22 -0500513 int len;
514
David Gibson05a22ba2009-11-26 15:37:13 +1100515 /* FIXME: This is a bit sub-optimal, since we potentially scan
516 * over all the properties twice. */
517 php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
518 if (!php || (len != sizeof(*php))) {
519 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
520 if (!php || (len != sizeof(*php)))
521 return 0;
522 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500523
Tom Rinie8c2d252020-01-27 12:10:31 -0500524 return fdt32_to_cpu(*php);
Kumar Gala8d04f022007-10-24 11:04:22 -0500525}
526
David Gibson9a6cf732008-08-20 16:55:14 +1000527const char *fdt_get_alias_namelen(const void *fdt,
528 const char *name, int namelen)
529{
530 int aliasoffset;
531
532 aliasoffset = fdt_path_offset(fdt, "/aliases");
533 if (aliasoffset < 0)
534 return NULL;
535
536 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
537}
538
539const char *fdt_get_alias(const void *fdt, const char *name)
540{
541 return fdt_get_alias_namelen(fdt, name, strlen(name));
542}
543
Kumar Gala8d04f022007-10-24 11:04:22 -0500544int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
545{
David Gibsonae0b5902008-02-12 11:58:31 +1100546 int pdepth = 0, p = 0;
547 int offset, depth, namelen;
Kumar Gala8d04f022007-10-24 11:04:22 -0500548 const char *name;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400549
Simon Glassf0921f52019-10-27 09:47:42 -0600550 FDT_RO_PROBE(fdt);
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400551
Kumar Gala8d04f022007-10-24 11:04:22 -0500552 if (buflen < 2)
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400553 return -FDT_ERR_NOSPACE;
Kumar Gala8d04f022007-10-24 11:04:22 -0500554
David Gibsonae0b5902008-02-12 11:58:31 +1100555 for (offset = 0, depth = 0;
556 (offset >= 0) && (offset <= nodeoffset);
557 offset = fdt_next_node(fdt, offset, &depth)) {
David Gibsonae0b5902008-02-12 11:58:31 +1100558 while (pdepth > depth) {
559 do {
560 p--;
561 } while (buf[p-1] != '/');
562 pdepth--;
563 }
564
David Gibsonbbdbc7c2008-08-29 14:19:13 +1000565 if (pdepth >= depth) {
566 name = fdt_get_name(fdt, offset, &namelen);
567 if (!name)
568 return namelen;
569 if ((p + namelen + 1) <= buflen) {
570 memcpy(buf + p, name, namelen);
571 p += namelen;
572 buf[p++] = '/';
573 pdepth++;
574 }
David Gibsonae0b5902008-02-12 11:58:31 +1100575 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500576
David Gibsonae0b5902008-02-12 11:58:31 +1100577 if (offset == nodeoffset) {
578 if (pdepth < (depth + 1))
579 return -FDT_ERR_NOSPACE;
580
581 if (p > 1) /* special case so that root path is "/", not "" */
Kumar Gala8d04f022007-10-24 11:04:22 -0500582 p--;
David Gibsonae0b5902008-02-12 11:58:31 +1100583 buf[p] = '\0';
David Gibsonbbdbc7c2008-08-29 14:19:13 +1000584 return 0;
Kumar Gala8d04f022007-10-24 11:04:22 -0500585 }
586 }
587
David Gibsonae0b5902008-02-12 11:58:31 +1100588 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
589 return -FDT_ERR_BADOFFSET;
590 else if (offset == -FDT_ERR_BADOFFSET)
591 return -FDT_ERR_BADSTRUCTURE;
Kumar Gala8d04f022007-10-24 11:04:22 -0500592
David Gibsonae0b5902008-02-12 11:58:31 +1100593 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500594}
595
596int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
597 int supernodedepth, int *nodedepth)
598{
David Gibsonae0b5902008-02-12 11:58:31 +1100599 int offset, depth;
Kumar Gala8d04f022007-10-24 11:04:22 -0500600 int supernodeoffset = -FDT_ERR_INTERNAL;
601
Simon Glassf0921f52019-10-27 09:47:42 -0600602 FDT_RO_PROBE(fdt);
Kumar Gala8d04f022007-10-24 11:04:22 -0500603
604 if (supernodedepth < 0)
605 return -FDT_ERR_NOTFOUND;
606
David Gibsonae0b5902008-02-12 11:58:31 +1100607 for (offset = 0, depth = 0;
608 (offset >= 0) && (offset <= nodeoffset);
609 offset = fdt_next_node(fdt, offset, &depth)) {
610 if (depth == supernodedepth)
611 supernodeoffset = offset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500612
David Gibsonae0b5902008-02-12 11:58:31 +1100613 if (offset == nodeoffset) {
614 if (nodedepth)
615 *nodedepth = depth;
Kumar Gala8d04f022007-10-24 11:04:22 -0500616
David Gibsonae0b5902008-02-12 11:58:31 +1100617 if (supernodedepth > depth)
618 return -FDT_ERR_NOTFOUND;
619 else
620 return supernodeoffset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500621 }
David Gibsonae0b5902008-02-12 11:58:31 +1100622 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500623
Simon Glassf0921f52019-10-27 09:47:42 -0600624 if (fdt_chk_extra()) {
625 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
626 return -FDT_ERR_BADOFFSET;
627 else if (offset == -FDT_ERR_BADOFFSET)
628 return -FDT_ERR_BADSTRUCTURE;
629 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500630
David Gibsonae0b5902008-02-12 11:58:31 +1100631 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500632}
633
634int fdt_node_depth(const void *fdt, int nodeoffset)
635{
636 int nodedepth;
637 int err;
638
639 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
640 if (err)
Simon Glassf0921f52019-10-27 09:47:42 -0600641 return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL;
Kumar Gala8d04f022007-10-24 11:04:22 -0500642 return nodedepth;
643}
644
645int fdt_parent_offset(const void *fdt, int nodeoffset)
646{
647 int nodedepth = fdt_node_depth(fdt, nodeoffset);
648
649 if (nodedepth < 0)
650 return nodedepth;
651 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
652 nodedepth - 1, NULL);
653}
654
655int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
656 const char *propname,
657 const void *propval, int proplen)
658{
David Gibsonae0b5902008-02-12 11:58:31 +1100659 int offset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500660 const void *val;
661 int len;
662
Simon Glassf0921f52019-10-27 09:47:42 -0600663 FDT_RO_PROBE(fdt);
Kumar Gala8d04f022007-10-24 11:04:22 -0500664
Kumar Gala8d04f022007-10-24 11:04:22 -0500665 /* FIXME: The algorithm here is pretty horrible: we scan each
666 * property of a node in fdt_getprop(), then if that didn't
667 * find what we want, we scan over them again making our way
668 * to the next node. Still it's the easiest to implement
669 * approach; performance can come later. */
David Gibsonae0b5902008-02-12 11:58:31 +1100670 for (offset = fdt_next_node(fdt, startoffset, NULL);
671 offset >= 0;
672 offset = fdt_next_node(fdt, offset, NULL)) {
673 val = fdt_getprop(fdt, offset, propname, &len);
674 if (val && (len == proplen)
675 && (memcmp(val, propval, len) == 0))
676 return offset;
677 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500678
David Gibsonae0b5902008-02-12 11:58:31 +1100679 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500680}
681
682int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
683{
David Gibson05a22ba2009-11-26 15:37:13 +1100684 int offset;
685
Kumar Gala8d04f022007-10-24 11:04:22 -0500686 if ((phandle == 0) || (phandle == -1))
687 return -FDT_ERR_BADPHANDLE;
David Gibson05a22ba2009-11-26 15:37:13 +1100688
Simon Glassf0921f52019-10-27 09:47:42 -0600689 FDT_RO_PROBE(fdt);
David Gibson05a22ba2009-11-26 15:37:13 +1100690
691 /* FIXME: The algorithm here is pretty horrible: we
692 * potentially scan each property of a node in
693 * fdt_get_phandle(), then if that didn't find what
694 * we want, we scan over them again making our way to the next
695 * node. Still it's the easiest to implement approach;
696 * performance can come later. */
697 for (offset = fdt_next_node(fdt, -1, NULL);
698 offset >= 0;
699 offset = fdt_next_node(fdt, offset, NULL)) {
700 if (fdt_get_phandle(fdt, offset) == phandle)
701 return offset;
702 }
703
704 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500705}
706
Simon Glasse853b322013-01-21 12:59:18 -0800707int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
Kumar Gala8d04f022007-10-24 11:04:22 -0500708{
709 int len = strlen(str);
David Gibsonef4e8ce2008-07-07 10:10:48 +1000710 const char *p;
Kumar Gala8d04f022007-10-24 11:04:22 -0500711
712 while (listlen >= len) {
713 if (memcmp(str, strlist, len+1) == 0)
714 return 1;
715 p = memchr(strlist, '\0', listlen);
716 if (!p)
717 return 0; /* malformed strlist.. */
718 listlen -= (p-strlist) + 1;
719 strlist = p + 1;
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400720 }
721 return 0;
722}
Kumar Gala8d04f022007-10-24 11:04:22 -0500723
Simon Glassb02e4042016-10-02 17:59:28 -0600724int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
Thierry Redingbc4147a2014-08-26 17:33:50 +0200725{
Simon Glassb02e4042016-10-02 17:59:28 -0600726 const char *list, *end;
727 int length, count = 0;
Thierry Redingbc4147a2014-08-26 17:33:50 +0200728
Simon Glassb02e4042016-10-02 17:59:28 -0600729 list = fdt_getprop(fdt, nodeoffset, property, &length);
Thierry Redingbc4147a2014-08-26 17:33:50 +0200730 if (!list)
Masahiro Yamada7c9786d2016-10-17 20:22:33 +0900731 return length;
Thierry Redingbc4147a2014-08-26 17:33:50 +0200732
Simon Glassb02e4042016-10-02 17:59:28 -0600733 end = list + length;
Thierry Redingbc4147a2014-08-26 17:33:50 +0200734
Simon Glassb02e4042016-10-02 17:59:28 -0600735 while (list < end) {
736 length = strnlen(list, end - list) + 1;
737
738 /* Abort if the last string isn't properly NUL-terminated. */
739 if (list + length > end)
740 return -FDT_ERR_BADVALUE;
741
742 list += length;
Thierry Redingbc4147a2014-08-26 17:33:50 +0200743 count++;
744 }
745
746 return count;
747}
748
Simon Glassb02e4042016-10-02 17:59:28 -0600749int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
750 const char *string)
Thierry Redingfc503c12014-08-26 17:33:51 +0200751{
Simon Glassb02e4042016-10-02 17:59:28 -0600752 int length, len, idx = 0;
Thierry Redingfc503c12014-08-26 17:33:51 +0200753 const char *list, *end;
Thierry Redingfc503c12014-08-26 17:33:51 +0200754
Simon Glassb02e4042016-10-02 17:59:28 -0600755 list = fdt_getprop(fdt, nodeoffset, property, &length);
Thierry Redingfc503c12014-08-26 17:33:51 +0200756 if (!list)
Masahiro Yamada01ae56c2016-10-17 20:22:34 +0900757 return length;
Thierry Redingfc503c12014-08-26 17:33:51 +0200758
Simon Glassb02e4042016-10-02 17:59:28 -0600759 len = strlen(string) + 1;
760 end = list + length;
Thierry Redingfc503c12014-08-26 17:33:51 +0200761
762 while (list < end) {
Simon Glassb02e4042016-10-02 17:59:28 -0600763 length = strnlen(list, end - list) + 1;
Thierry Redingfc503c12014-08-26 17:33:51 +0200764
Simon Glassb02e4042016-10-02 17:59:28 -0600765 /* Abort if the last string isn't properly NUL-terminated. */
766 if (list + length > end)
767 return -FDT_ERR_BADVALUE;
Thierry Redingfc503c12014-08-26 17:33:51 +0200768
Simon Glassb02e4042016-10-02 17:59:28 -0600769 if (length == len && memcmp(list, string, length) == 0)
770 return idx;
771
772 list += length;
773 idx++;
Thierry Redingfc503c12014-08-26 17:33:51 +0200774 }
775
776 return -FDT_ERR_NOTFOUND;
777}
778
Simon Glassb02e4042016-10-02 17:59:28 -0600779const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
780 const char *property, int idx,
781 int *lenp)
Thierry Reding5094eb42014-08-26 17:33:52 +0200782{
Simon Glassb02e4042016-10-02 17:59:28 -0600783 const char *list, *end;
784 int length;
Thierry Reding5094eb42014-08-26 17:33:52 +0200785
Simon Glassb02e4042016-10-02 17:59:28 -0600786 list = fdt_getprop(fdt, nodeoffset, property, &length);
787 if (!list) {
788 if (lenp)
789 *lenp = length;
Thierry Reding5094eb42014-08-26 17:33:52 +0200790
Simon Glassb02e4042016-10-02 17:59:28 -0600791 return NULL;
792 }
Thierry Reding5094eb42014-08-26 17:33:52 +0200793
Simon Glassb02e4042016-10-02 17:59:28 -0600794 end = list + length;
795
796 while (list < end) {
797 length = strnlen(list, end - list) + 1;
798
799 /* Abort if the last string isn't properly NUL-terminated. */
800 if (list + length > end) {
801 if (lenp)
802 *lenp = -FDT_ERR_BADVALUE;
803
804 return NULL;
Thierry Reding5094eb42014-08-26 17:33:52 +0200805 }
806
Simon Glassb02e4042016-10-02 17:59:28 -0600807 if (idx == 0) {
808 if (lenp)
809 *lenp = length - 1;
810
811 return list;
812 }
813
814 list += length;
815 idx--;
Thierry Reding5094eb42014-08-26 17:33:52 +0200816 }
817
Simon Glassb02e4042016-10-02 17:59:28 -0600818 if (lenp)
819 *lenp = -FDT_ERR_NOTFOUND;
Thierry Reding5094eb42014-08-26 17:33:52 +0200820
Simon Glassb02e4042016-10-02 17:59:28 -0600821 return NULL;
Thierry Reding5094eb42014-08-26 17:33:52 +0200822}
823
Kumar Gala8d04f022007-10-24 11:04:22 -0500824int fdt_node_check_compatible(const void *fdt, int nodeoffset,
825 const char *compatible)
826{
827 const void *prop;
828 int len;
829
830 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
831 if (!prop)
832 return len;
Simon Glass9c07b982016-10-02 17:59:27 -0600833
834 return !fdt_stringlist_contains(prop, len, compatible);
Kumar Gala8d04f022007-10-24 11:04:22 -0500835}
836
837int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
838 const char *compatible)
839{
David Gibson11abe452008-02-18 18:09:04 +1100840 int offset, err;
Kumar Gala8d04f022007-10-24 11:04:22 -0500841
Simon Glassf0921f52019-10-27 09:47:42 -0600842 FDT_RO_PROBE(fdt);
Kumar Gala8d04f022007-10-24 11:04:22 -0500843
Kumar Gala8d04f022007-10-24 11:04:22 -0500844 /* FIXME: The algorithm here is pretty horrible: we scan each
845 * property of a node in fdt_node_check_compatible(), then if
846 * that didn't find what we want, we scan over them again
847 * making our way to the next node. Still it's the easiest to
848 * implement approach; performance can come later. */
David Gibsonae0b5902008-02-12 11:58:31 +1100849 for (offset = fdt_next_node(fdt, startoffset, NULL);
850 offset >= 0;
851 offset = fdt_next_node(fdt, offset, NULL)) {
852 err = fdt_node_check_compatible(fdt, offset, compatible);
853 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
854 return err;
855 else if (err == 0)
856 return offset;
857 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500858
David Gibsonae0b5902008-02-12 11:58:31 +1100859 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500860}
Simon Glassf0921f52019-10-27 09:47:42 -0600861
862#if !defined(CHECK_LEVEL) || CHECK_LEVEL > 0
863int fdt_check_full(const void *fdt, size_t bufsize)
864{
865 int err;
866 int num_memrsv;
867 int offset, nextoffset = 0;
868 uint32_t tag;
869 unsigned depth = 0;
870 const void *prop;
871 const char *propname;
872
873 if (bufsize < FDT_V1_SIZE)
874 return -FDT_ERR_TRUNCATED;
875 err = fdt_check_header(fdt);
876 if (err != 0)
877 return err;
878 if (bufsize < fdt_totalsize(fdt))
879 return -FDT_ERR_TRUNCATED;
880
881 num_memrsv = fdt_num_mem_rsv(fdt);
882 if (num_memrsv < 0)
883 return num_memrsv;
884
885 while (1) {
886 offset = nextoffset;
887 tag = fdt_next_tag(fdt, offset, &nextoffset);
888
889 if (nextoffset < 0)
890 return nextoffset;
891
892 switch (tag) {
893 case FDT_NOP:
894 break;
895
896 case FDT_END:
897 if (depth != 0)
898 return -FDT_ERR_BADSTRUCTURE;
899 return 0;
900
901 case FDT_BEGIN_NODE:
902 depth++;
903 if (depth > INT_MAX)
904 return -FDT_ERR_BADSTRUCTURE;
905 break;
906
907 case FDT_END_NODE:
908 if (depth == 0)
909 return -FDT_ERR_BADSTRUCTURE;
910 depth--;
911 break;
912
913 case FDT_PROP:
914 prop = fdt_getprop_by_offset(fdt, offset, &propname,
915 &err);
916 if (!prop)
917 return err;
918 break;
919
920 default:
921 return -FDT_ERR_INTERNAL;
922 }
923 }
924}
925#endif