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