blob: be03aea9eb006f4bf9d3f908003bd30ef1ca9363 [file] [log] [blame]
Tom Rini8b0c8a12018-05-06 18:27:01 -04001// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
Gerald Van Baren9e61ed82007-03-31 12:00:56 -04002/*
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
Gerald Van Baren9e61ed82007-03-31 12:00:56 -04005 */
Masahiro Yamada75f82d02018-03-05 01:20:11 +09006#include <linux/libfdt_env.h>
Gerald Van Baren9e61ed82007-03-31 12:00:56 -04007
Bartlomiej Sieka61250862008-02-29 16:00:24 +01008#ifndef USE_HOSTCC
Gerald Van Baren9e61ed82007-03-31 12:00:56 -04009#include <fdt.h>
Masahiro Yamada75f82d02018-03-05 01:20:11 +090010#include <linux/libfdt.h>
Bartlomiej Sieka61250862008-02-29 16:00:24 +010011#else
12#include "fdt_host.h"
13#endif
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040014
15#include "libfdt_internal.h"
16
Simon Glassa5e1a582019-10-27 09:47:42 -060017static int fdt_nodename_eq_(const void *fdt, int offset,
David Gibson9457d9d2008-07-09 14:10:24 +100018 const char *s, int len)
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040019{
Simon Glassa5e1a582019-10-27 09:47:42 -060020 int olen;
21 const char *p = fdt_get_name(fdt, offset, &olen);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040022
Simon Glassa5e1a582019-10-27 09:47:42 -060023 if (!p || (fdt_chk_extra() && olen < len))
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040024 /* short match */
25 return 0;
26
27 if (memcmp(p, s, len) != 0)
28 return 0;
29
Kumar Galac8ab7052007-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 Baren9e61ed82007-03-31 12:00:56 -040035 return 0;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040036}
37
Simon Glassa5e1a582019-10-27 09:47:42 -060038const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
Gerald Van Baren18c75c62007-05-17 23:54:36 -040039{
Simon Glassa5e1a582019-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 Baren18c75c62007-05-17 23:54:36 -040045
Simon Glassa5e1a582019-10-27 09:47:42 -060046 if (!fdt_chk_extra()) {
47 s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
David Gibsonc5b130e2008-08-06 14:50:49 +100048
Simon Glassa5e1a582019-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 Gibsonc5b130e2008-08-06 14:50:49 +100057
Simon Glassa5e1a582019-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 Ripard2125ca52016-07-05 10:26:39 +020063
Simon Glassa5e1a582019-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;
82 }
Maxime Ripard2125ca52016-07-05 10:26:39 +020083
Simon Glassa5e1a582019-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 }
Maxime Ripard2125ca52016-07-05 10:26:39 +020091
Simon Glassa5e1a582019-10-27 09:47:42 -060092 if (lenp)
93 *lenp = n - s;
94 return s;
Maxime Ripard2125ca52016-07-05 10:26:39 +020095
Simon Glassa5e1a582019-10-27 09:47:42 -060096fail:
97 if (lenp)
98 *lenp = err;
99 return NULL;
100}
Maxime Ripard2125ca52016-07-05 10:26:39 +0200101
Simon Glassa5e1a582019-10-27 09:47:42 -0600102const char *fdt_string(const void *fdt, int stroffset)
103{
104 return fdt_get_string(fdt, stroffset, NULL);
105}
Maxime Ripard2125ca52016-07-05 10:26:39 +0200106
Simon Glassa5e1a582019-10-27 09:47:42 -0600107static 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);
Maxime Ripard2125ca52016-07-05 10:26:39 +0200114}
115
Simon Glassa5e1a582019-10-27 09:47:42 -0600116int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
Thierry Reding0431be02019-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 Glassa5e1a582019-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 Reding0431be02019-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 Glassa5e1a582019-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 Galac8ab7052007-10-24 11:04:22 -0500177int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
178{
Simon Glassa5e1a582019-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 Rinibea42b72020-01-27 12:10:31 -0500186 *address = fdt64_to_cpu(re->address);
187 *size = fdt64_to_cpu(re->size);
Gerald Van Baren18c75c62007-05-17 23:54:36 -0400188 return 0;
189}
190
Kumar Galac8ab7052007-10-24 11:04:22 -0500191int fdt_num_mem_rsv(const void *fdt)
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400192{
Simon Glassa5e1a582019-10-27 09:47:42 -0600193 int i;
194 const struct fdt_reserve_entry *re;
Kumar Galac8ab7052007-10-24 11:04:22 -0500195
Simon Glassa5e1a582019-10-27 09:47:42 -0600196 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
Tom Rinibea42b72020-01-27 12:10:31 -0500197 if (fdt64_to_cpu(re->size) == 0)
Simon Glassa5e1a582019-10-27 09:47:42 -0600198 return i;
199 }
200 return -FDT_ERR_TRUNCATED;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400201}
202
Simon Glassa5e1a582019-10-27 09:47:42 -0600203static int nextprop_(const void *fdt, int offset)
David Gibson7b64da12010-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 Gibsond9d793c2008-02-12 11:58:31 +1100227int fdt_subnode_offset_namelen(const void *fdt, int offset,
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400228 const char *name, int namelen)
229{
David Gibson954914a2009-02-06 14:01:56 +1100230 int depth;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400231
Simon Glassa5e1a582019-10-27 09:47:42 -0600232 FDT_RO_PROBE(fdt);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400233
David Gibson954914a2009-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 Glassa5e1a582019-10-27 09:47:42 -0600238 && fdt_nodename_eq_(fdt, offset, name, namelen))
David Gibsond9d793c2008-02-12 11:58:31 +1100239 return offset;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400240
David Gibson954914a2009-02-06 14:01:56 +1100241 if (depth < 0)
David Gibson1c0320e2008-10-29 23:27:45 -0500242 return -FDT_ERR_NOTFOUND;
David Gibson954914a2009-02-06 14:01:56 +1100243 return offset; /* error */
Gerald Van Baren9e61ed82007-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 Ripard87390e82016-07-05 10:26:41 +0200252int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400253{
Maxime Ripard87390e82016-07-05 10:26:41 +0200254 const char *end = path + namelen;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400255 const char *p = path;
256 int offset = 0;
257
Simon Glassa5e1a582019-10-27 09:47:42 -0600258 FDT_RO_PROBE(fdt);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400259
Kumar Galaeb45e032008-08-14 08:28:19 -0500260 /* see if we have an alias */
261 if (*path != '/') {
Simon Glassa5e1a582019-10-27 09:47:42 -0600262 const char *q = memchr(path, '/', end - p);
Kumar Galaeb45e032008-08-14 08:28:19 -0500263
Kumar Galaeb45e032008-08-14 08:28:19 -0500264 if (!q)
265 q = end;
266
David Gibsond6656ea2008-08-20 16:55:14 +1000267 p = fdt_get_alias_namelen(fdt, p, q - p);
Kumar Galaeb45e032008-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 Baren9e61ed82007-03-31 12:00:56 -0400274
Simon Glassa5e1a582019-10-27 09:47:42 -0600275 while (p < end) {
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400276 const char *q;
277
Simon Glassa5e1a582019-10-27 09:47:42 -0600278 while (*p == '/') {
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400279 p++;
Simon Glassa5e1a582019-10-27 09:47:42 -0600280 if (p == end)
281 return offset;
282 }
283 q = memchr(p, '/', end - p);
284 if (! q)
Gerald Van Baren9e61ed82007-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 Baren32480892007-03-31 14:30:53 -0400294 return offset;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400295}
296
Simon Glassa2235e42016-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 Galac8ab7052007-10-24 11:04:22 -0500302const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400303{
Rob Herringcd5f70d2018-05-19 14:13:53 +0200304 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
Simon Glassa5e1a582019-10-27 09:47:42 -0600305 const char *nameptr;
Kumar Galac8ab7052007-10-24 11:04:22 -0500306 int err;
307
Simon Glassa5e1a582019-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 Gibson148a49b2008-05-20 17:19:11 +1000325 goto fail;
Simon Glassa5e1a582019-10-27 09:47:42 -0600326 }
327 nameptr = leaf+1;
328 }
Kumar Galac8ab7052007-10-24 11:04:22 -0500329
330 if (len)
Simon Glassa5e1a582019-10-27 09:47:42 -0600331 *len = strlen(nameptr);
Kumar Galac8ab7052007-10-24 11:04:22 -0500332
Simon Glassa5e1a582019-10-27 09:47:42 -0600333 return nameptr;
Kumar Galac8ab7052007-10-24 11:04:22 -0500334
335 fail:
336 if (len)
337 *len = err;
338 return NULL;
339}
340
David Gibson7b64da12010-03-09 17:39:14 +1100341int fdt_first_property_offset(const void *fdt, int nodeoffset)
Kumar Galac8ab7052007-10-24 11:04:22 -0500342{
David Gibson7b64da12010-03-09 17:39:14 +1100343 int offset;
344
Rob Herringcd5f70d2018-05-19 14:13:53 +0200345 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
David Gibson7b64da12010-03-09 17:39:14 +1100346 return offset;
347
Simon Glassa5e1a582019-10-27 09:47:42 -0600348 return nextprop_(fdt, offset);
David Gibson7b64da12010-03-09 17:39:14 +1100349}
350
351int fdt_next_property_offset(const void *fdt, int offset)
352{
Rob Herringcd5f70d2018-05-19 14:13:53 +0200353 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
David Gibson7b64da12010-03-09 17:39:14 +1100354 return offset;
355
Simon Glassa5e1a582019-10-27 09:47:42 -0600356 return nextprop_(fdt, offset);
David Gibson7b64da12010-03-09 17:39:14 +1100357}
358
Simon Glassa5e1a582019-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 Gibson7b64da12010-03-09 17:39:14 +1100362{
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400363 int err;
David Gibson7b64da12010-03-09 17:39:14 +1100364 const struct fdt_property *prop;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400365
Simon Glassa5e1a582019-10-27 09:47:42 -0600366 if (fdt_chk_basic() && (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
David Gibson7b64da12010-03-09 17:39:14 +1100367 if (lenp)
368 *lenp = err;
369 return NULL;
370 }
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400371
Rob Herringcd5f70d2018-05-19 14:13:53 +0200372 prop = fdt_offset_ptr_(fdt, offset);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400373
David Gibson7b64da12010-03-09 17:39:14 +1100374 if (lenp)
Tom Rinibea42b72020-01-27 12:10:31 -0500375 *lenp = fdt32_to_cpu(prop->len);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400376
David Gibson7b64da12010-03-09 17:39:14 +1100377 return prop;
378}
379
Simon Glassa5e1a582019-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)
David Gibson7b64da12010-03-09 17:39:14 +1100402{
403 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;
Kumar Galac8ab7052007-10-24 11:04:22 -0500407
Simon Glassa5e1a582019-10-27 09:47:42 -0600408 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
409 if (fdt_chk_extra() && !prop) {
David Gibson7b64da12010-03-09 17:39:14 +1100410 offset = -FDT_ERR_INTERNAL;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400411 break;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400412 }
Tom Rinibea42b72020-01-27 12:10:31 -0500413 if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
Simon Glassa5e1a582019-10-27 09:47:42 -0600414 name, namelen)) {
415 if (poffset)
416 *poffset = offset;
David Gibson7b64da12010-03-09 17:39:14 +1100417 return prop;
Simon Glassa5e1a582019-10-27 09:47:42 -0600418 }
David Gibson7b64da12010-03-09 17:39:14 +1100419 }
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400420
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400421 if (lenp)
David Gibson7b64da12010-03-09 17:39:14 +1100422 *lenp = offset;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400423 return NULL;
424}
425
Simon Glassa5e1a582019-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 Gibsonc5b130e2008-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 Baren9e61ed82007-03-31 12:00:56 -0400455{
Simon Glassa5e1a582019-10-27 09:47:42 -0600456 int poffset;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400457 const struct fdt_property *prop;
458
Simon Glassa5e1a582019-10-27 09:47:42 -0600459 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
460 &poffset);
Robert P. J. Day55019c52016-05-23 05:40:55 -0400461 if (!prop)
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400462 return NULL;
463
Simon Glassa5e1a582019-10-27 09:47:42 -0600464 /* Handle realignment */
465 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
Tom Rinibea42b72020-01-27 12:10:31 -0500466 (poffset + sizeof(*prop)) % 8 && fdt32_to_cpu(prop->len) >= 8)
Simon Glassa5e1a582019-10-27 09:47:42 -0600467 return prop->data + 4;
David Gibson7b64da12010-03-09 17:39:14 +1100468 return prop->data;
469}
470
471const 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 Glassa5e1a582019-10-27 09:47:42 -0600476 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
David Gibson7b64da12010-03-09 17:39:14 +1100477 if (!prop)
478 return NULL;
Simon Glassa5e1a582019-10-27 09:47:42 -0600479 if (namep) {
480 const char *name;
481 int namelen;
482
483 if (fdt_chk_extra()) {
Tom Rinibea42b72020-01-27 12:10:31 -0500484 name = fdt_get_string(fdt, fdt32_to_cpu(prop->nameoff),
Simon Glassa5e1a582019-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 Rinibea42b72020-01-27 12:10:31 -0500493 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
Simon Glassa5e1a582019-10-27 09:47:42 -0600494 }
495 }
496
497 /* Handle realignment */
498 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
Tom Rinibea42b72020-01-27 12:10:31 -0500499 (offset + sizeof(*prop)) % 8 && fdt32_to_cpu(prop->len) >= 8)
Simon Glassa5e1a582019-10-27 09:47:42 -0600500 return prop->data + 4;
Kumar Galac8ab7052007-10-24 11:04:22 -0500501 return prop->data;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400502}
503
David Gibsonc5b130e2008-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 Galac8ab7052007-10-24 11:04:22 -0500510uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
511{
Kim Phillips38c38152013-01-16 13:59:50 +0000512 const fdt32_t *php;
Kumar Galac8ab7052007-10-24 11:04:22 -0500513 int len;
514
David Gibson3e31de92009-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 Galac8ab7052007-10-24 11:04:22 -0500523
Tom Rinibea42b72020-01-27 12:10:31 -0500524 return fdt32_to_cpu(*php);
Kumar Galac8ab7052007-10-24 11:04:22 -0500525}
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400526
David Gibsond6656ea2008-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 Galac8ab7052007-10-24 11:04:22 -0500544int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400545{
David Gibsond9d793c2008-02-12 11:58:31 +1100546 int pdepth = 0, p = 0;
547 int offset, depth, namelen;
Kumar Galac8ab7052007-10-24 11:04:22 -0500548 const char *name;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400549
Simon Glassa5e1a582019-10-27 09:47:42 -0600550 FDT_RO_PROBE(fdt);
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400551
Kumar Galac8ab7052007-10-24 11:04:22 -0500552 if (buflen < 2)
553 return -FDT_ERR_NOSPACE;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400554
David Gibsond9d793c2008-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 Gibsond9d793c2008-02-12 11:58:31 +1100558 while (pdepth > depth) {
559 do {
560 p--;
561 } while (buf[p-1] != '/');
562 pdepth--;
563 }
564
David Gibson8d2810d2008-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 Gibsond9d793c2008-02-12 11:58:31 +1100575 }
Kumar Galac8ab7052007-10-24 11:04:22 -0500576
David Gibsond9d793c2008-02-12 11:58:31 +1100577 if (offset == nodeoffset) {
578 if (pdepth < (depth + 1))
579 return -FDT_ERR_NOSPACE;
Kumar Galac8ab7052007-10-24 11:04:22 -0500580
David Gibsond9d793c2008-02-12 11:58:31 +1100581 if (p > 1) /* special case so that root path is "/", not "" */
582 p--;
583 buf[p] = '\0';
David Gibson8d2810d2008-08-29 14:19:13 +1000584 return 0;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400585 }
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400586 }
587
David Gibsond9d793c2008-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;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400592
David Gibsond9d793c2008-02-12 11:58:31 +1100593 return offset; /* error from fdt_next_node() */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400594}
Gerald Van Barenc077a132007-04-14 22:46:41 -0400595
Kumar Galac8ab7052007-10-24 11:04:22 -0500596int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
597 int supernodedepth, int *nodedepth)
Gerald Van Barenc077a132007-04-14 22:46:41 -0400598{
David Gibsond9d793c2008-02-12 11:58:31 +1100599 int offset, depth;
Kumar Galac8ab7052007-10-24 11:04:22 -0500600 int supernodeoffset = -FDT_ERR_INTERNAL;
Gerald Van Barenc077a132007-04-14 22:46:41 -0400601
Simon Glassa5e1a582019-10-27 09:47:42 -0600602 FDT_RO_PROBE(fdt);
Gerald Van Barenc077a132007-04-14 22:46:41 -0400603
Kumar Galac8ab7052007-10-24 11:04:22 -0500604 if (supernodedepth < 0)
605 return -FDT_ERR_NOTFOUND;
Gerald Van Barenc077a132007-04-14 22:46:41 -0400606
David Gibsond9d793c2008-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;
Gerald Van Barenc077a132007-04-14 22:46:41 -0400612
David Gibsond9d793c2008-02-12 11:58:31 +1100613 if (offset == nodeoffset) {
614 if (nodedepth)
615 *nodedepth = depth;
Gerald Van Barenc077a132007-04-14 22:46:41 -0400616
David Gibsond9d793c2008-02-12 11:58:31 +1100617 if (supernodedepth > depth)
618 return -FDT_ERR_NOTFOUND;
619 else
620 return supernodeoffset;
Gerald Van Barenc077a132007-04-14 22:46:41 -0400621 }
David Gibsond9d793c2008-02-12 11:58:31 +1100622 }
Kumar Galac8ab7052007-10-24 11:04:22 -0500623
Simon Glassa5e1a582019-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 Galac8ab7052007-10-24 11:04:22 -0500630
David Gibsond9d793c2008-02-12 11:58:31 +1100631 return offset; /* error from fdt_next_node() */
Kumar Galac8ab7052007-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 Glassa5e1a582019-10-27 09:47:42 -0600641 return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL;
Kumar Galac8ab7052007-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 Gibsond9d793c2008-02-12 11:58:31 +1100659 int offset;
Kumar Galac8ab7052007-10-24 11:04:22 -0500660 const void *val;
661 int len;
662
Simon Glassa5e1a582019-10-27 09:47:42 -0600663 FDT_RO_PROBE(fdt);
Kumar Galac8ab7052007-10-24 11:04:22 -0500664
Kumar Galac8ab7052007-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 Gibsond9d793c2008-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 Galac8ab7052007-10-24 11:04:22 -0500678
David Gibsond9d793c2008-02-12 11:58:31 +1100679 return offset; /* error from fdt_next_node() */
Gerald Van Barenc077a132007-04-14 22:46:41 -0400680}
681
Kumar Galac8ab7052007-10-24 11:04:22 -0500682int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
Gerald Van Barenc077a132007-04-14 22:46:41 -0400683{
David Gibson3e31de92009-11-26 15:37:13 +1100684 int offset;
685
Kumar Galac8ab7052007-10-24 11:04:22 -0500686 if ((phandle == 0) || (phandle == -1))
687 return -FDT_ERR_BADPHANDLE;
David Gibson3e31de92009-11-26 15:37:13 +1100688
Simon Glassa5e1a582019-10-27 09:47:42 -0600689 FDT_RO_PROBE(fdt);
David Gibson3e31de92009-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 Galac8ab7052007-10-24 11:04:22 -0500705}
Gerald Van Barenc077a132007-04-14 22:46:41 -0400706
Simon Glass73b47562013-01-21 12:59:18 -0800707int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
Kumar Galac8ab7052007-10-24 11:04:22 -0500708{
709 int len = strlen(str);
David Gibsonb4141b82008-07-07 10:10:48 +1000710 const char *p;
Gerald Van Barenc077a132007-04-14 22:46:41 -0400711
Kumar Galac8ab7052007-10-24 11:04:22 -0500712 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 Barenc077a132007-04-14 22:46:41 -0400720 }
721 return 0;
722}
Kumar Galac8ab7052007-10-24 11:04:22 -0500723
Simon Glassb0ea7402016-10-02 17:59:28 -0600724int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
Thierry Reding92ba09e2014-08-26 17:33:50 +0200725{
Simon Glassb0ea7402016-10-02 17:59:28 -0600726 const char *list, *end;
727 int length, count = 0;
Thierry Reding92ba09e2014-08-26 17:33:50 +0200728
Simon Glassb0ea7402016-10-02 17:59:28 -0600729 list = fdt_getprop(fdt, nodeoffset, property, &length);
Thierry Reding92ba09e2014-08-26 17:33:50 +0200730 if (!list)
Masahiro Yamada0a378262016-10-17 20:22:33 +0900731 return length;
Thierry Reding92ba09e2014-08-26 17:33:50 +0200732
Simon Glassb0ea7402016-10-02 17:59:28 -0600733 end = list + length;
734
735 while (list < end) {
736 length = strnlen(list, end - list) + 1;
Thierry Reding92ba09e2014-08-26 17:33:50 +0200737
Simon Glassb0ea7402016-10-02 17:59:28 -0600738 /* 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 Reding92ba09e2014-08-26 17:33:50 +0200743 count++;
744 }
745
746 return count;
747}
748
Simon Glassb0ea7402016-10-02 17:59:28 -0600749int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
750 const char *string)
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200751{
Simon Glassb0ea7402016-10-02 17:59:28 -0600752 int length, len, idx = 0;
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200753 const char *list, *end;
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200754
Simon Glassb0ea7402016-10-02 17:59:28 -0600755 list = fdt_getprop(fdt, nodeoffset, property, &length);
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200756 if (!list)
Masahiro Yamada48fd0f12016-10-17 20:22:34 +0900757 return length;
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200758
Simon Glassb0ea7402016-10-02 17:59:28 -0600759 len = strlen(string) + 1;
760 end = list + length;
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200761
762 while (list < end) {
Simon Glassb0ea7402016-10-02 17:59:28 -0600763 length = strnlen(list, end - list) + 1;
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200764
Simon Glassb0ea7402016-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 Redinga24f1ac2014-08-26 17:33:51 +0200768
Simon Glassb0ea7402016-10-02 17:59:28 -0600769 if (length == len && memcmp(list, string, length) == 0)
770 return idx;
771
772 list += length;
773 idx++;
Thierry Redinga24f1ac2014-08-26 17:33:51 +0200774 }
775
776 return -FDT_ERR_NOTFOUND;
777}
778
Simon Glassb0ea7402016-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 Reding5504b802014-08-26 17:33:52 +0200782{
Simon Glassb0ea7402016-10-02 17:59:28 -0600783 const char *list, *end;
784 int length;
785
786 list = fdt_getprop(fdt, nodeoffset, property, &length);
787 if (!list) {
788 if (lenp)
789 *lenp = length;
Thierry Reding5504b802014-08-26 17:33:52 +0200790
Simon Glassb0ea7402016-10-02 17:59:28 -0600791 return NULL;
792 }
Thierry Reding5504b802014-08-26 17:33:52 +0200793
Simon Glassb0ea7402016-10-02 17:59:28 -0600794 end = list + length;
Thierry Reding5504b802014-08-26 17:33:52 +0200795
Simon Glassb0ea7402016-10-02 17:59:28 -0600796 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;
805 }
806
807 if (idx == 0) {
808 if (lenp)
809 *lenp = length - 1;
810
811 return list;
Thierry Reding5504b802014-08-26 17:33:52 +0200812 }
813
Simon Glassb0ea7402016-10-02 17:59:28 -0600814 list += length;
815 idx--;
Thierry Reding5504b802014-08-26 17:33:52 +0200816 }
817
Simon Glassb0ea7402016-10-02 17:59:28 -0600818 if (lenp)
819 *lenp = -FDT_ERR_NOTFOUND;
Thierry Reding5504b802014-08-26 17:33:52 +0200820
Simon Glassb0ea7402016-10-02 17:59:28 -0600821 return NULL;
Thierry Reding5504b802014-08-26 17:33:52 +0200822}
823
Kumar Galac8ab7052007-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 Glassdfdcd6f2016-10-02 17:59:27 -0600833
834 return !fdt_stringlist_contains(prop, len, compatible);
Kumar Galac8ab7052007-10-24 11:04:22 -0500835}
836
837int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
838 const char *compatible)
839{
David Gibsone8f3f7f2008-02-18 18:09:04 +1100840 int offset, err;
Kumar Galac8ab7052007-10-24 11:04:22 -0500841
Simon Glassa5e1a582019-10-27 09:47:42 -0600842 FDT_RO_PROBE(fdt);
Kumar Galac8ab7052007-10-24 11:04:22 -0500843
Kumar Galac8ab7052007-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 Gibsond9d793c2008-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 Galac8ab7052007-10-24 11:04:22 -0500858
David Gibsond9d793c2008-02-12 11:58:31 +1100859 return offset; /* error from fdt_next_node() */
Kumar Galac8ab7052007-10-24 11:04:22 -0500860}
Simon Glassa5e1a582019-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