Merge tag 'u-boot-amlogic-20190423' of git://git.denx.de/u-boot-amlogic
[oweals/u-boot.git] / lib / libfdt / fdt_ro.c
1 // SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
2 /*
3  * libfdt - Flat Device Tree manipulation
4  * Copyright (C) 2006 David Gibson, IBM Corporation.
5  */
6 #include <linux/libfdt_env.h>
7
8 #ifndef USE_HOSTCC
9 #include <fdt.h>
10 #include <linux/libfdt.h>
11 #else
12 #include "fdt_host.h"
13 #endif
14
15 #include "libfdt_internal.h"
16
17 static int _fdt_nodename_eq(const void *fdt, int offset,
18                             const char *s, int len)
19 {
20         const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
21
22         if (!p)
23                 /* short match */
24                 return 0;
25
26         if (memcmp(p, s, len) != 0)
27                 return 0;
28
29         if (p[len] == '\0')
30                 return 1;
31         else if (!memchr(s, '@', len) && (p[len] == '@'))
32                 return 1;
33         else
34                 return 0;
35 }
36
37 const char *fdt_string(const void *fdt, int stroffset)
38 {
39         return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
40 }
41
42 static int _fdt_string_eq(const void *fdt, int stroffset,
43                           const char *s, int len)
44 {
45         const char *p = fdt_string(fdt, stroffset);
46
47         return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0);
48 }
49
50 uint32_t fdt_get_max_phandle(const void *fdt)
51 {
52         uint32_t max_phandle = 0;
53         int offset;
54
55         for (offset = fdt_next_node(fdt, -1, NULL);;
56              offset = fdt_next_node(fdt, offset, NULL)) {
57                 uint32_t phandle;
58
59                 if (offset == -FDT_ERR_NOTFOUND)
60                         return max_phandle;
61
62                 if (offset < 0)
63                         return (uint32_t)-1;
64
65                 phandle = fdt_get_phandle(fdt, offset);
66                 if (phandle == (uint32_t)-1)
67                         continue;
68
69                 if (phandle > max_phandle)
70                         max_phandle = phandle;
71         }
72
73         return 0;
74 }
75
76 int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
77 {
78         uint32_t max = 0;
79         int offset = -1;
80
81         while (true) {
82                 uint32_t value;
83
84                 offset = fdt_next_node(fdt, offset, NULL);
85                 if (offset < 0) {
86                         if (offset == -FDT_ERR_NOTFOUND)
87                                 break;
88
89                         return offset;
90                 }
91
92                 value = fdt_get_phandle(fdt, offset);
93
94                 if (value > max)
95                         max = value;
96         }
97
98         if (max == FDT_MAX_PHANDLE)
99                 return -FDT_ERR_NOPHANDLES;
100
101         if (phandle)
102                 *phandle = max + 1;
103
104         return 0;
105 }
106
107 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
108 {
109         FDT_CHECK_HEADER(fdt);
110         *address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address);
111         *size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size);
112         return 0;
113 }
114
115 int fdt_num_mem_rsv(const void *fdt)
116 {
117         int i = 0;
118
119         while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0)
120                 i++;
121         return i;
122 }
123
124 static int _nextprop(const void *fdt, int offset)
125 {
126         uint32_t tag;
127         int nextoffset;
128
129         do {
130                 tag = fdt_next_tag(fdt, offset, &nextoffset);
131
132                 switch (tag) {
133                 case FDT_END:
134                         if (nextoffset >= 0)
135                                 return -FDT_ERR_BADSTRUCTURE;
136                         else
137                                 return nextoffset;
138
139                 case FDT_PROP:
140                         return offset;
141                 }
142                 offset = nextoffset;
143         } while (tag == FDT_NOP);
144
145         return -FDT_ERR_NOTFOUND;
146 }
147
148 int fdt_subnode_offset_namelen(const void *fdt, int offset,
149                                const char *name, int namelen)
150 {
151         int depth;
152
153         FDT_CHECK_HEADER(fdt);
154
155         for (depth = 0;
156              (offset >= 0) && (depth >= 0);
157              offset = fdt_next_node(fdt, offset, &depth))
158                 if ((depth == 1)
159                     && _fdt_nodename_eq(fdt, offset, name, namelen))
160                         return offset;
161
162         if (depth < 0)
163                 return -FDT_ERR_NOTFOUND;
164         return offset; /* error */
165 }
166
167 int fdt_subnode_offset(const void *fdt, int parentoffset,
168                        const char *name)
169 {
170         return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
171 }
172
173 /*
174  * Find the next of path separator, note we need to search for both '/' and ':'
175  * and then take the first one so that we do the right thing for e.g.
176  * "foo/bar:option" and "bar:option/otheroption", both of which happen, so
177  * first searching for either ':' or '/' does not work.
178  */
179 static const char *fdt_path_next_separator(const char *path, int len)
180 {
181         const void *sep1 = memchr(path, '/', len);
182         const void *sep2 = memchr(path, ':', len);
183
184         if (sep1 && sep2)
185                 return (sep1 < sep2) ? sep1 : sep2;
186         else if (sep1)
187                 return sep1;
188         else
189                 return sep2;
190 }
191
192 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
193 {
194         const char *end = path + namelen;
195         const char *p = path;
196         int offset = 0;
197
198         FDT_CHECK_HEADER(fdt);
199
200         /* see if we have an alias */
201         if (*path != '/') {
202                 const char *q = fdt_path_next_separator(path, namelen);
203
204                 if (!q)
205                         q = end;
206
207                 p = fdt_get_alias_namelen(fdt, p, q - p);
208                 if (!p)
209                         return -FDT_ERR_BADPATH;
210                 offset = fdt_path_offset(fdt, p);
211
212                 p = q;
213         }
214
215         while (*p && (p < end)) {
216                 const char *q;
217
218                 while (*p == '/')
219                         p++;
220
221                 if (*p == '\0' || *p == ':')
222                         return offset;
223
224                 q = fdt_path_next_separator(p, end - p);
225                 if (!q)
226                         q = end;
227
228                 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
229                 if (offset < 0)
230                         return offset;
231
232                 p = q;
233         }
234
235         return offset;
236 }
237
238 int fdt_path_offset(const void *fdt, const char *path)
239 {
240         return fdt_path_offset_namelen(fdt, path, strlen(path));
241 }
242
243 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
244 {
245         const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
246         int err;
247
248         if (((err = fdt_check_header(fdt)) != 0)
249             || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
250                         goto fail;
251
252         if (len)
253                 *len = strlen(nh->name);
254
255         return nh->name;
256
257  fail:
258         if (len)
259                 *len = err;
260         return NULL;
261 }
262
263 int fdt_first_property_offset(const void *fdt, int nodeoffset)
264 {
265         int offset;
266
267         if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
268                 return offset;
269
270         return _nextprop(fdt, offset);
271 }
272
273 int fdt_next_property_offset(const void *fdt, int offset)
274 {
275         if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
276                 return offset;
277
278         return _nextprop(fdt, offset);
279 }
280
281 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
282                                                       int offset,
283                                                       int *lenp)
284 {
285         int err;
286         const struct fdt_property *prop;
287
288         if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
289                 if (lenp)
290                         *lenp = err;
291                 return NULL;
292         }
293
294         prop = fdt_offset_ptr_(fdt, offset);
295
296         if (lenp)
297                 *lenp = fdt32_to_cpu(prop->len);
298
299         return prop;
300 }
301
302 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
303                                                     int offset,
304                                                     const char *name,
305                                                     int namelen, int *lenp)
306 {
307         for (offset = fdt_first_property_offset(fdt, offset);
308              (offset >= 0);
309              (offset = fdt_next_property_offset(fdt, offset))) {
310                 const struct fdt_property *prop;
311
312                 if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
313                         offset = -FDT_ERR_INTERNAL;
314                         break;
315                 }
316                 if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
317                                    name, namelen))
318                         return prop;
319         }
320
321         if (lenp)
322                 *lenp = offset;
323         return NULL;
324 }
325
326 const struct fdt_property *fdt_get_property(const void *fdt,
327                                             int nodeoffset,
328                                             const char *name, int *lenp)
329 {
330         return fdt_get_property_namelen(fdt, nodeoffset, name,
331                                         strlen(name), lenp);
332 }
333
334 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
335                                 const char *name, int namelen, int *lenp)
336 {
337         const struct fdt_property *prop;
338
339         prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
340         if (!prop)
341                 return NULL;
342
343         return prop->data;
344 }
345
346 const void *fdt_getprop_by_offset(const void *fdt, int offset,
347                                   const char **namep, int *lenp)
348 {
349         const struct fdt_property *prop;
350
351         prop = fdt_get_property_by_offset(fdt, offset, lenp);
352         if (!prop)
353                 return NULL;
354         if (namep)
355                 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
356         return prop->data;
357 }
358
359 const void *fdt_getprop(const void *fdt, int nodeoffset,
360                         const char *name, int *lenp)
361 {
362         return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
363 }
364
365 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
366 {
367         const fdt32_t *php;
368         int len;
369
370         /* FIXME: This is a bit sub-optimal, since we potentially scan
371          * over all the properties twice. */
372         php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
373         if (!php || (len != sizeof(*php))) {
374                 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
375                 if (!php || (len != sizeof(*php)))
376                         return 0;
377         }
378
379         return fdt32_to_cpu(*php);
380 }
381
382 const char *fdt_get_alias_namelen(const void *fdt,
383                                   const char *name, int namelen)
384 {
385         int aliasoffset;
386
387         aliasoffset = fdt_path_offset(fdt, "/aliases");
388         if (aliasoffset < 0)
389                 return NULL;
390
391         return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
392 }
393
394 const char *fdt_get_alias(const void *fdt, const char *name)
395 {
396         return fdt_get_alias_namelen(fdt, name, strlen(name));
397 }
398
399 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
400 {
401         int pdepth = 0, p = 0;
402         int offset, depth, namelen;
403         const char *name;
404
405         FDT_CHECK_HEADER(fdt);
406
407         if (buflen < 2)
408                 return -FDT_ERR_NOSPACE;
409
410         for (offset = 0, depth = 0;
411              (offset >= 0) && (offset <= nodeoffset);
412              offset = fdt_next_node(fdt, offset, &depth)) {
413                 while (pdepth > depth) {
414                         do {
415                                 p--;
416                         } while (buf[p-1] != '/');
417                         pdepth--;
418                 }
419
420                 if (pdepth >= depth) {
421                         name = fdt_get_name(fdt, offset, &namelen);
422                         if (!name)
423                                 return namelen;
424                         if ((p + namelen + 1) <= buflen) {
425                                 memcpy(buf + p, name, namelen);
426                                 p += namelen;
427                                 buf[p++] = '/';
428                                 pdepth++;
429                         }
430                 }
431
432                 if (offset == nodeoffset) {
433                         if (pdepth < (depth + 1))
434                                 return -FDT_ERR_NOSPACE;
435
436                         if (p > 1) /* special case so that root path is "/", not "" */
437                                 p--;
438                         buf[p] = '\0';
439                         return 0;
440                 }
441         }
442
443         if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
444                 return -FDT_ERR_BADOFFSET;
445         else if (offset == -FDT_ERR_BADOFFSET)
446                 return -FDT_ERR_BADSTRUCTURE;
447
448         return offset; /* error from fdt_next_node() */
449 }
450
451 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
452                                  int supernodedepth, int *nodedepth)
453 {
454         int offset, depth;
455         int supernodeoffset = -FDT_ERR_INTERNAL;
456
457         FDT_CHECK_HEADER(fdt);
458
459         if (supernodedepth < 0)
460                 return -FDT_ERR_NOTFOUND;
461
462         for (offset = 0, depth = 0;
463              (offset >= 0) && (offset <= nodeoffset);
464              offset = fdt_next_node(fdt, offset, &depth)) {
465                 if (depth == supernodedepth)
466                         supernodeoffset = offset;
467
468                 if (offset == nodeoffset) {
469                         if (nodedepth)
470                                 *nodedepth = depth;
471
472                         if (supernodedepth > depth)
473                                 return -FDT_ERR_NOTFOUND;
474                         else
475                                 return supernodeoffset;
476                 }
477         }
478
479         if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
480                 return -FDT_ERR_BADOFFSET;
481         else if (offset == -FDT_ERR_BADOFFSET)
482                 return -FDT_ERR_BADSTRUCTURE;
483
484         return offset; /* error from fdt_next_node() */
485 }
486
487 int fdt_node_depth(const void *fdt, int nodeoffset)
488 {
489         int nodedepth;
490         int err;
491
492         err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
493         if (err)
494                 return (err < 0) ? err : -FDT_ERR_INTERNAL;
495         return nodedepth;
496 }
497
498 int fdt_parent_offset(const void *fdt, int nodeoffset)
499 {
500         int nodedepth = fdt_node_depth(fdt, nodeoffset);
501
502         if (nodedepth < 0)
503                 return nodedepth;
504         return fdt_supernode_atdepth_offset(fdt, nodeoffset,
505                                             nodedepth - 1, NULL);
506 }
507
508 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
509                                   const char *propname,
510                                   const void *propval, int proplen)
511 {
512         int offset;
513         const void *val;
514         int len;
515
516         FDT_CHECK_HEADER(fdt);
517
518         /* FIXME: The algorithm here is pretty horrible: we scan each
519          * property of a node in fdt_getprop(), then if that didn't
520          * find what we want, we scan over them again making our way
521          * to the next node.  Still it's the easiest to implement
522          * approach; performance can come later. */
523         for (offset = fdt_next_node(fdt, startoffset, NULL);
524              offset >= 0;
525              offset = fdt_next_node(fdt, offset, NULL)) {
526                 val = fdt_getprop(fdt, offset, propname, &len);
527                 if (val && (len == proplen)
528                     && (memcmp(val, propval, len) == 0))
529                         return offset;
530         }
531
532         return offset; /* error from fdt_next_node() */
533 }
534
535 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
536 {
537         int offset;
538
539         if ((phandle == 0) || (phandle == -1))
540                 return -FDT_ERR_BADPHANDLE;
541
542         FDT_CHECK_HEADER(fdt);
543
544         /* FIXME: The algorithm here is pretty horrible: we
545          * potentially scan each property of a node in
546          * fdt_get_phandle(), then if that didn't find what
547          * we want, we scan over them again making our way to the next
548          * node.  Still it's the easiest to implement approach;
549          * performance can come later. */
550         for (offset = fdt_next_node(fdt, -1, NULL);
551              offset >= 0;
552              offset = fdt_next_node(fdt, offset, NULL)) {
553                 if (fdt_get_phandle(fdt, offset) == phandle)
554                         return offset;
555         }
556
557         return offset; /* error from fdt_next_node() */
558 }
559
560 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
561 {
562         int len = strlen(str);
563         const char *p;
564
565         while (listlen >= len) {
566                 if (memcmp(str, strlist, len+1) == 0)
567                         return 1;
568                 p = memchr(strlist, '\0', listlen);
569                 if (!p)
570                         return 0; /* malformed strlist.. */
571                 listlen -= (p-strlist) + 1;
572                 strlist = p + 1;
573         }
574         return 0;
575 }
576
577 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
578 {
579         const char *list, *end;
580         int length, count = 0;
581
582         list = fdt_getprop(fdt, nodeoffset, property, &length);
583         if (!list)
584                 return length;
585
586         end = list + length;
587
588         while (list < end) {
589                 length = strnlen(list, end - list) + 1;
590
591                 /* Abort if the last string isn't properly NUL-terminated. */
592                 if (list + length > end)
593                         return -FDT_ERR_BADVALUE;
594
595                 list += length;
596                 count++;
597         }
598
599         return count;
600 }
601
602 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
603                           const char *string)
604 {
605         int length, len, idx = 0;
606         const char *list, *end;
607
608         list = fdt_getprop(fdt, nodeoffset, property, &length);
609         if (!list)
610                 return length;
611
612         len = strlen(string) + 1;
613         end = list + length;
614
615         while (list < end) {
616                 length = strnlen(list, end - list) + 1;
617
618                 /* Abort if the last string isn't properly NUL-terminated. */
619                 if (list + length > end)
620                         return -FDT_ERR_BADVALUE;
621
622                 if (length == len && memcmp(list, string, length) == 0)
623                         return idx;
624
625                 list += length;
626                 idx++;
627         }
628
629         return -FDT_ERR_NOTFOUND;
630 }
631
632 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
633                                const char *property, int idx,
634                                int *lenp)
635 {
636         const char *list, *end;
637         int length;
638
639         list = fdt_getprop(fdt, nodeoffset, property, &length);
640         if (!list) {
641                 if (lenp)
642                         *lenp = length;
643
644                 return NULL;
645         }
646
647         end = list + length;
648
649         while (list < end) {
650                 length = strnlen(list, end - list) + 1;
651
652                 /* Abort if the last string isn't properly NUL-terminated. */
653                 if (list + length > end) {
654                         if (lenp)
655                                 *lenp = -FDT_ERR_BADVALUE;
656
657                         return NULL;
658                 }
659
660                 if (idx == 0) {
661                         if (lenp)
662                                 *lenp = length - 1;
663
664                         return list;
665                 }
666
667                 list += length;
668                 idx--;
669         }
670
671         if (lenp)
672                 *lenp = -FDT_ERR_NOTFOUND;
673
674         return NULL;
675 }
676
677 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
678                               const char *compatible)
679 {
680         const void *prop;
681         int len;
682
683         prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
684         if (!prop)
685                 return len;
686
687         return !fdt_stringlist_contains(prop, len, compatible);
688 }
689
690 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
691                                   const char *compatible)
692 {
693         int offset, err;
694
695         FDT_CHECK_HEADER(fdt);
696
697         /* FIXME: The algorithm here is pretty horrible: we scan each
698          * property of a node in fdt_node_check_compatible(), then if
699          * that didn't find what we want, we scan over them again
700          * making our way to the next node.  Still it's the easiest to
701          * implement approach; performance can come later. */
702         for (offset = fdt_next_node(fdt, startoffset, NULL);
703              offset >= 0;
704              offset = fdt_next_node(fdt, offset, NULL)) {
705                 err = fdt_node_check_compatible(fdt, offset, compatible);
706                 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
707                         return err;
708                 else if (err == 0)
709                         return offset;
710         }
711
712         return offset; /* error from fdt_next_node() */
713 }