treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / scripts / dtc / libfdt / fdt_ro.c
bloba5c2797cde6586595bc64c3c26e79dd26012bff7
1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 /*
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
5 */
6 #include "libfdt_env.h"
8 #include <fdt.h>
9 #include <libfdt.h>
11 #include "libfdt_internal.h"
13 static int fdt_nodename_eq_(const void *fdt, int offset,
14 const char *s, int len)
16 int olen;
17 const char *p = fdt_get_name(fdt, offset, &olen);
19 if (!p || olen < len)
20 /* short match */
21 return 0;
23 if (memcmp(p, s, len) != 0)
24 return 0;
26 if (p[len] == '\0')
27 return 1;
28 else if (!memchr(s, '@', len) && (p[len] == '@'))
29 return 1;
30 else
31 return 0;
34 const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
36 int32_t totalsize = fdt_ro_probe_(fdt);
37 uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt);
38 size_t len;
39 int err;
40 const char *s, *n;
42 err = totalsize;
43 if (totalsize < 0)
44 goto fail;
46 err = -FDT_ERR_BADOFFSET;
47 if (absoffset >= totalsize)
48 goto fail;
49 len = totalsize - absoffset;
51 if (fdt_magic(fdt) == FDT_MAGIC) {
52 if (stroffset < 0)
53 goto fail;
54 if (fdt_version(fdt) >= 17) {
55 if (stroffset >= fdt_size_dt_strings(fdt))
56 goto fail;
57 if ((fdt_size_dt_strings(fdt) - stroffset) < len)
58 len = fdt_size_dt_strings(fdt) - stroffset;
60 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
61 if ((stroffset >= 0)
62 || (stroffset < -fdt_size_dt_strings(fdt)))
63 goto fail;
64 if ((-stroffset) < len)
65 len = -stroffset;
66 } else {
67 err = -FDT_ERR_INTERNAL;
68 goto fail;
71 s = (const char *)fdt + absoffset;
72 n = memchr(s, '\0', len);
73 if (!n) {
74 /* missing terminating NULL */
75 err = -FDT_ERR_TRUNCATED;
76 goto fail;
79 if (lenp)
80 *lenp = n - s;
81 return s;
83 fail:
84 if (lenp)
85 *lenp = err;
86 return NULL;
89 const char *fdt_string(const void *fdt, int stroffset)
91 return fdt_get_string(fdt, stroffset, NULL);
94 static int fdt_string_eq_(const void *fdt, int stroffset,
95 const char *s, int len)
97 int slen;
98 const char *p = fdt_get_string(fdt, stroffset, &slen);
100 return p && (slen == len) && (memcmp(p, s, len) == 0);
103 int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
105 uint32_t max = 0;
106 int offset = -1;
108 while (true) {
109 uint32_t value;
111 offset = fdt_next_node(fdt, offset, NULL);
112 if (offset < 0) {
113 if (offset == -FDT_ERR_NOTFOUND)
114 break;
116 return offset;
119 value = fdt_get_phandle(fdt, offset);
121 if (value > max)
122 max = value;
125 if (phandle)
126 *phandle = max;
128 return 0;
131 int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
133 uint32_t max;
134 int err;
136 err = fdt_find_max_phandle(fdt, &max);
137 if (err < 0)
138 return err;
140 if (max == FDT_MAX_PHANDLE)
141 return -FDT_ERR_NOPHANDLES;
143 if (phandle)
144 *phandle = max + 1;
146 return 0;
149 static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
151 int offset = n * sizeof(struct fdt_reserve_entry);
152 int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
154 if (absoffset < fdt_off_mem_rsvmap(fdt))
155 return NULL;
156 if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry))
157 return NULL;
158 return fdt_mem_rsv_(fdt, n);
161 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
163 const struct fdt_reserve_entry *re;
165 FDT_RO_PROBE(fdt);
166 re = fdt_mem_rsv(fdt, n);
167 if (!re)
168 return -FDT_ERR_BADOFFSET;
170 *address = fdt64_ld(&re->address);
171 *size = fdt64_ld(&re->size);
172 return 0;
175 int fdt_num_mem_rsv(const void *fdt)
177 int i;
178 const struct fdt_reserve_entry *re;
180 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
181 if (fdt64_ld(&re->size) == 0)
182 return i;
184 return -FDT_ERR_TRUNCATED;
187 static int nextprop_(const void *fdt, int offset)
189 uint32_t tag;
190 int nextoffset;
192 do {
193 tag = fdt_next_tag(fdt, offset, &nextoffset);
195 switch (tag) {
196 case FDT_END:
197 if (nextoffset >= 0)
198 return -FDT_ERR_BADSTRUCTURE;
199 else
200 return nextoffset;
202 case FDT_PROP:
203 return offset;
205 offset = nextoffset;
206 } while (tag == FDT_NOP);
208 return -FDT_ERR_NOTFOUND;
211 int fdt_subnode_offset_namelen(const void *fdt, int offset,
212 const char *name, int namelen)
214 int depth;
216 FDT_RO_PROBE(fdt);
218 for (depth = 0;
219 (offset >= 0) && (depth >= 0);
220 offset = fdt_next_node(fdt, offset, &depth))
221 if ((depth == 1)
222 && fdt_nodename_eq_(fdt, offset, name, namelen))
223 return offset;
225 if (depth < 0)
226 return -FDT_ERR_NOTFOUND;
227 return offset; /* error */
230 int fdt_subnode_offset(const void *fdt, int parentoffset,
231 const char *name)
233 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
236 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
238 const char *end = path + namelen;
239 const char *p = path;
240 int offset = 0;
242 FDT_RO_PROBE(fdt);
244 /* see if we have an alias */
245 if (*path != '/') {
246 const char *q = memchr(path, '/', end - p);
248 if (!q)
249 q = end;
251 p = fdt_get_alias_namelen(fdt, p, q - p);
252 if (!p)
253 return -FDT_ERR_BADPATH;
254 offset = fdt_path_offset(fdt, p);
256 p = q;
259 while (p < end) {
260 const char *q;
262 while (*p == '/') {
263 p++;
264 if (p == end)
265 return offset;
267 q = memchr(p, '/', end - p);
268 if (! q)
269 q = end;
271 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
272 if (offset < 0)
273 return offset;
275 p = q;
278 return offset;
281 int fdt_path_offset(const void *fdt, const char *path)
283 return fdt_path_offset_namelen(fdt, path, strlen(path));
286 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
288 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
289 const char *nameptr;
290 int err;
292 if (((err = fdt_ro_probe_(fdt)) < 0)
293 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
294 goto fail;
296 nameptr = nh->name;
298 if (fdt_version(fdt) < 0x10) {
300 * For old FDT versions, match the naming conventions of V16:
301 * give only the leaf name (after all /). The actual tree
302 * contents are loosely checked.
304 const char *leaf;
305 leaf = strrchr(nameptr, '/');
306 if (leaf == NULL) {
307 err = -FDT_ERR_BADSTRUCTURE;
308 goto fail;
310 nameptr = leaf+1;
313 if (len)
314 *len = strlen(nameptr);
316 return nameptr;
318 fail:
319 if (len)
320 *len = err;
321 return NULL;
324 int fdt_first_property_offset(const void *fdt, int nodeoffset)
326 int offset;
328 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
329 return offset;
331 return nextprop_(fdt, offset);
334 int fdt_next_property_offset(const void *fdt, int offset)
336 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
337 return offset;
339 return nextprop_(fdt, offset);
342 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
343 int offset,
344 int *lenp)
346 int err;
347 const struct fdt_property *prop;
349 if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
350 if (lenp)
351 *lenp = err;
352 return NULL;
355 prop = fdt_offset_ptr_(fdt, offset);
357 if (lenp)
358 *lenp = fdt32_ld(&prop->len);
360 return prop;
363 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
364 int offset,
365 int *lenp)
367 /* Prior to version 16, properties may need realignment
368 * and this API does not work. fdt_getprop_*() will, however. */
370 if (fdt_version(fdt) < 0x10) {
371 if (lenp)
372 *lenp = -FDT_ERR_BADVERSION;
373 return NULL;
376 return fdt_get_property_by_offset_(fdt, offset, lenp);
379 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
380 int offset,
381 const char *name,
382 int namelen,
383 int *lenp,
384 int *poffset)
386 for (offset = fdt_first_property_offset(fdt, offset);
387 (offset >= 0);
388 (offset = fdt_next_property_offset(fdt, offset))) {
389 const struct fdt_property *prop;
391 if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
392 offset = -FDT_ERR_INTERNAL;
393 break;
395 if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
396 name, namelen)) {
397 if (poffset)
398 *poffset = offset;
399 return prop;
403 if (lenp)
404 *lenp = offset;
405 return NULL;
409 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
410 int offset,
411 const char *name,
412 int namelen, int *lenp)
414 /* Prior to version 16, properties may need realignment
415 * and this API does not work. fdt_getprop_*() will, however. */
416 if (fdt_version(fdt) < 0x10) {
417 if (lenp)
418 *lenp = -FDT_ERR_BADVERSION;
419 return NULL;
422 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
423 NULL);
427 const struct fdt_property *fdt_get_property(const void *fdt,
428 int nodeoffset,
429 const char *name, int *lenp)
431 return fdt_get_property_namelen(fdt, nodeoffset, name,
432 strlen(name), lenp);
435 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
436 const char *name, int namelen, int *lenp)
438 int poffset;
439 const struct fdt_property *prop;
441 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
442 &poffset);
443 if (!prop)
444 return NULL;
446 /* Handle realignment */
447 if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
448 fdt32_ld(&prop->len) >= 8)
449 return prop->data + 4;
450 return prop->data;
453 const void *fdt_getprop_by_offset(const void *fdt, int offset,
454 const char **namep, int *lenp)
456 const struct fdt_property *prop;
458 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
459 if (!prop)
460 return NULL;
461 if (namep) {
462 const char *name;
463 int namelen;
464 name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
465 &namelen);
466 if (!name) {
467 if (lenp)
468 *lenp = namelen;
469 return NULL;
471 *namep = name;
474 /* Handle realignment */
475 if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
476 fdt32_ld(&prop->len) >= 8)
477 return prop->data + 4;
478 return prop->data;
481 const void *fdt_getprop(const void *fdt, int nodeoffset,
482 const char *name, int *lenp)
484 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
487 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
489 const fdt32_t *php;
490 int len;
492 /* FIXME: This is a bit sub-optimal, since we potentially scan
493 * over all the properties twice. */
494 php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
495 if (!php || (len != sizeof(*php))) {
496 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
497 if (!php || (len != sizeof(*php)))
498 return 0;
501 return fdt32_ld(php);
504 const char *fdt_get_alias_namelen(const void *fdt,
505 const char *name, int namelen)
507 int aliasoffset;
509 aliasoffset = fdt_path_offset(fdt, "/aliases");
510 if (aliasoffset < 0)
511 return NULL;
513 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
516 const char *fdt_get_alias(const void *fdt, const char *name)
518 return fdt_get_alias_namelen(fdt, name, strlen(name));
521 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
523 int pdepth = 0, p = 0;
524 int offset, depth, namelen;
525 const char *name;
527 FDT_RO_PROBE(fdt);
529 if (buflen < 2)
530 return -FDT_ERR_NOSPACE;
532 for (offset = 0, depth = 0;
533 (offset >= 0) && (offset <= nodeoffset);
534 offset = fdt_next_node(fdt, offset, &depth)) {
535 while (pdepth > depth) {
536 do {
537 p--;
538 } while (buf[p-1] != '/');
539 pdepth--;
542 if (pdepth >= depth) {
543 name = fdt_get_name(fdt, offset, &namelen);
544 if (!name)
545 return namelen;
546 if ((p + namelen + 1) <= buflen) {
547 memcpy(buf + p, name, namelen);
548 p += namelen;
549 buf[p++] = '/';
550 pdepth++;
554 if (offset == nodeoffset) {
555 if (pdepth < (depth + 1))
556 return -FDT_ERR_NOSPACE;
558 if (p > 1) /* special case so that root path is "/", not "" */
559 p--;
560 buf[p] = '\0';
561 return 0;
565 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
566 return -FDT_ERR_BADOFFSET;
567 else if (offset == -FDT_ERR_BADOFFSET)
568 return -FDT_ERR_BADSTRUCTURE;
570 return offset; /* error from fdt_next_node() */
573 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
574 int supernodedepth, int *nodedepth)
576 int offset, depth;
577 int supernodeoffset = -FDT_ERR_INTERNAL;
579 FDT_RO_PROBE(fdt);
581 if (supernodedepth < 0)
582 return -FDT_ERR_NOTFOUND;
584 for (offset = 0, depth = 0;
585 (offset >= 0) && (offset <= nodeoffset);
586 offset = fdt_next_node(fdt, offset, &depth)) {
587 if (depth == supernodedepth)
588 supernodeoffset = offset;
590 if (offset == nodeoffset) {
591 if (nodedepth)
592 *nodedepth = depth;
594 if (supernodedepth > depth)
595 return -FDT_ERR_NOTFOUND;
596 else
597 return supernodeoffset;
601 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
602 return -FDT_ERR_BADOFFSET;
603 else if (offset == -FDT_ERR_BADOFFSET)
604 return -FDT_ERR_BADSTRUCTURE;
606 return offset; /* error from fdt_next_node() */
609 int fdt_node_depth(const void *fdt, int nodeoffset)
611 int nodedepth;
612 int err;
614 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
615 if (err)
616 return (err < 0) ? err : -FDT_ERR_INTERNAL;
617 return nodedepth;
620 int fdt_parent_offset(const void *fdt, int nodeoffset)
622 int nodedepth = fdt_node_depth(fdt, nodeoffset);
624 if (nodedepth < 0)
625 return nodedepth;
626 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
627 nodedepth - 1, NULL);
630 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
631 const char *propname,
632 const void *propval, int proplen)
634 int offset;
635 const void *val;
636 int len;
638 FDT_RO_PROBE(fdt);
640 /* FIXME: The algorithm here is pretty horrible: we scan each
641 * property of a node in fdt_getprop(), then if that didn't
642 * find what we want, we scan over them again making our way
643 * to the next node. Still it's the easiest to implement
644 * approach; performance can come later. */
645 for (offset = fdt_next_node(fdt, startoffset, NULL);
646 offset >= 0;
647 offset = fdt_next_node(fdt, offset, NULL)) {
648 val = fdt_getprop(fdt, offset, propname, &len);
649 if (val && (len == proplen)
650 && (memcmp(val, propval, len) == 0))
651 return offset;
654 return offset; /* error from fdt_next_node() */
657 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
659 int offset;
661 if ((phandle == 0) || (phandle == -1))
662 return -FDT_ERR_BADPHANDLE;
664 FDT_RO_PROBE(fdt);
666 /* FIXME: The algorithm here is pretty horrible: we
667 * potentially scan each property of a node in
668 * fdt_get_phandle(), then if that didn't find what
669 * we want, we scan over them again making our way to the next
670 * node. Still it's the easiest to implement approach;
671 * performance can come later. */
672 for (offset = fdt_next_node(fdt, -1, NULL);
673 offset >= 0;
674 offset = fdt_next_node(fdt, offset, NULL)) {
675 if (fdt_get_phandle(fdt, offset) == phandle)
676 return offset;
679 return offset; /* error from fdt_next_node() */
682 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
684 int len = strlen(str);
685 const char *p;
687 while (listlen >= len) {
688 if (memcmp(str, strlist, len+1) == 0)
689 return 1;
690 p = memchr(strlist, '\0', listlen);
691 if (!p)
692 return 0; /* malformed strlist.. */
693 listlen -= (p-strlist) + 1;
694 strlist = p + 1;
696 return 0;
699 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
701 const char *list, *end;
702 int length, count = 0;
704 list = fdt_getprop(fdt, nodeoffset, property, &length);
705 if (!list)
706 return length;
708 end = list + length;
710 while (list < end) {
711 length = strnlen(list, end - list) + 1;
713 /* Abort if the last string isn't properly NUL-terminated. */
714 if (list + length > end)
715 return -FDT_ERR_BADVALUE;
717 list += length;
718 count++;
721 return count;
724 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
725 const char *string)
727 int length, len, idx = 0;
728 const char *list, *end;
730 list = fdt_getprop(fdt, nodeoffset, property, &length);
731 if (!list)
732 return length;
734 len = strlen(string) + 1;
735 end = list + length;
737 while (list < end) {
738 length = strnlen(list, end - list) + 1;
740 /* Abort if the last string isn't properly NUL-terminated. */
741 if (list + length > end)
742 return -FDT_ERR_BADVALUE;
744 if (length == len && memcmp(list, string, length) == 0)
745 return idx;
747 list += length;
748 idx++;
751 return -FDT_ERR_NOTFOUND;
754 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
755 const char *property, int idx,
756 int *lenp)
758 const char *list, *end;
759 int length;
761 list = fdt_getprop(fdt, nodeoffset, property, &length);
762 if (!list) {
763 if (lenp)
764 *lenp = length;
766 return NULL;
769 end = list + length;
771 while (list < end) {
772 length = strnlen(list, end - list) + 1;
774 /* Abort if the last string isn't properly NUL-terminated. */
775 if (list + length > end) {
776 if (lenp)
777 *lenp = -FDT_ERR_BADVALUE;
779 return NULL;
782 if (idx == 0) {
783 if (lenp)
784 *lenp = length - 1;
786 return list;
789 list += length;
790 idx--;
793 if (lenp)
794 *lenp = -FDT_ERR_NOTFOUND;
796 return NULL;
799 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
800 const char *compatible)
802 const void *prop;
803 int len;
805 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
806 if (!prop)
807 return len;
809 return !fdt_stringlist_contains(prop, len, compatible);
812 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
813 const char *compatible)
815 int offset, err;
817 FDT_RO_PROBE(fdt);
819 /* FIXME: The algorithm here is pretty horrible: we scan each
820 * property of a node in fdt_node_check_compatible(), then if
821 * that didn't find what we want, we scan over them again
822 * making our way to the next node. Still it's the easiest to
823 * implement approach; performance can come later. */
824 for (offset = fdt_next_node(fdt, startoffset, NULL);
825 offset >= 0;
826 offset = fdt_next_node(fdt, offset, NULL)) {
827 err = fdt_node_check_compatible(fdt, offset, compatible);
828 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
829 return err;
830 else if (err == 0)
831 return offset;
834 return offset; /* error from fdt_next_node() */
837 int fdt_check_full(const void *fdt, size_t bufsize)
839 int err;
840 int num_memrsv;
841 int offset, nextoffset = 0;
842 uint32_t tag;
843 unsigned depth = 0;
844 const void *prop;
845 const char *propname;
847 if (bufsize < FDT_V1_SIZE)
848 return -FDT_ERR_TRUNCATED;
849 err = fdt_check_header(fdt);
850 if (err != 0)
851 return err;
852 if (bufsize < fdt_totalsize(fdt))
853 return -FDT_ERR_TRUNCATED;
855 num_memrsv = fdt_num_mem_rsv(fdt);
856 if (num_memrsv < 0)
857 return num_memrsv;
859 while (1) {
860 offset = nextoffset;
861 tag = fdt_next_tag(fdt, offset, &nextoffset);
863 if (nextoffset < 0)
864 return nextoffset;
866 switch (tag) {
867 case FDT_NOP:
868 break;
870 case FDT_END:
871 if (depth != 0)
872 return -FDT_ERR_BADSTRUCTURE;
873 return 0;
875 case FDT_BEGIN_NODE:
876 depth++;
877 if (depth > INT_MAX)
878 return -FDT_ERR_BADSTRUCTURE;
879 break;
881 case FDT_END_NODE:
882 if (depth == 0)
883 return -FDT_ERR_BADSTRUCTURE;
884 depth--;
885 break;
887 case FDT_PROP:
888 prop = fdt_getprop_by_offset(fdt, offset, &propname,
889 &err);
890 if (!prop)
891 return err;
892 break;
894 default:
895 return -FDT_ERR_INTERNAL;