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"
7
8 #include <fdt.h>
9 #include <libfdt.h>
10
11 #include "libfdt_internal.h"
12
13 /* Check if a buffer contains a nul-terminated string.
14 * Used for checking property values which should be strings.
15 */
is_nul_string(const char * buf,const size_t buf_len)16 static bool is_nul_string(const char *buf, const size_t buf_len) {
17 return buf_len > 0 && buf[buf_len - 1] == '\0' &&
18 strnlen(buf, buf_len) == buf_len - 1;
19 }
20
fdt_nodename_eq_(const void * fdt,int offset,const char * s,int len)21 static int fdt_nodename_eq_(const void *fdt, int offset,
22 const char *s, int len)
23 {
24 int olen;
25 const char *p = fdt_get_name(fdt, offset, &olen);
26
27 if (!p || olen < len)
28 /* short match */
29 return 0;
30
31 if (memcmp(p, s, len) != 0)
32 return 0;
33
34 if (p[len] == '\0')
35 return 1;
36 else if (!memchr(s, '@', len) && (p[len] == '@'))
37 return 1;
38 else
39 return 0;
40 }
41
fdt_get_string(const void * fdt,int stroffset,int * lenp)42 const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
43 {
44 int32_t totalsize;
45 uint32_t absoffset;
46 size_t len;
47 int err;
48 const char *s, *n;
49
50 if (can_assume(VALID_INPUT)) {
51 s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
52
53 if (lenp)
54 *lenp = strlen(s);
55 return s;
56 }
57 totalsize = fdt_ro_probe_(fdt);
58 err = totalsize;
59 if (totalsize < 0)
60 goto fail;
61
62 err = -FDT_ERR_BADOFFSET;
63 absoffset = stroffset + fdt_off_dt_strings(fdt);
64 if (absoffset >= (unsigned)totalsize)
65 goto fail;
66 len = totalsize - absoffset;
67
68 if (fdt_magic(fdt) == FDT_MAGIC) {
69 if (stroffset < 0)
70 goto fail;
71 if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
72 if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
73 goto fail;
74 if ((fdt_size_dt_strings(fdt) - stroffset) < len)
75 len = fdt_size_dt_strings(fdt) - stroffset;
76 }
77 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
78 unsigned int sw_stroffset = -stroffset;
79
80 if ((stroffset >= 0) ||
81 (sw_stroffset > fdt_size_dt_strings(fdt)))
82 goto fail;
83 if (sw_stroffset < len)
84 len = sw_stroffset;
85 } else {
86 err = -FDT_ERR_INTERNAL;
87 goto fail;
88 }
89
90 s = (const char *)fdt + absoffset;
91 n = memchr(s, '\0', len);
92 if (!n) {
93 /* missing terminating NULL */
94 err = -FDT_ERR_TRUNCATED;
95 goto fail;
96 }
97
98 if (lenp)
99 *lenp = n - s;
100 return s;
101
102 fail:
103 if (lenp)
104 *lenp = err;
105 return NULL;
106 }
107
fdt_string(const void * fdt,int stroffset)108 const char *fdt_string(const void *fdt, int stroffset)
109 {
110 return fdt_get_string(fdt, stroffset, NULL);
111 }
112
fdt_string_eq_(const void * fdt,int stroffset,const char * s,int len)113 static int fdt_string_eq_(const void *fdt, int stroffset,
114 const char *s, int len)
115 {
116 int slen;
117 const char *p = fdt_get_string(fdt, stroffset, &slen);
118
119 return p && (slen == len) && (memcmp(p, s, len) == 0);
120 }
121
fdt_find_max_phandle(const void * fdt,uint32_t * phandle)122 int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
123 {
124 uint32_t max = 0;
125 int offset = -1;
126
127 while (true) {
128 uint32_t value;
129
130 offset = fdt_next_node(fdt, offset, NULL);
131 if (offset < 0) {
132 if (offset == -FDT_ERR_NOTFOUND)
133 break;
134
135 return offset;
136 }
137
138 value = fdt_get_phandle(fdt, offset);
139
140 if (value > max)
141 max = value;
142 }
143
144 if (phandle)
145 *phandle = max;
146
147 return 0;
148 }
149
fdt_generate_phandle(const void * fdt,uint32_t * phandle)150 int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
151 {
152 uint32_t max;
153 int err;
154
155 err = fdt_find_max_phandle(fdt, &max);
156 if (err < 0)
157 return err;
158
159 if (max == FDT_MAX_PHANDLE)
160 return -FDT_ERR_NOPHANDLES;
161
162 if (phandle)
163 *phandle = max + 1;
164
165 return 0;
166 }
167
fdt_mem_rsv(const void * fdt,int n)168 static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
169 {
170 unsigned int offset = n * sizeof(struct fdt_reserve_entry);
171 unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
172
173 if (!can_assume(VALID_INPUT)) {
174 if (absoffset < fdt_off_mem_rsvmap(fdt))
175 return NULL;
176 if (absoffset > fdt_totalsize(fdt) -
177 sizeof(struct fdt_reserve_entry))
178 return NULL;
179 }
180 return fdt_mem_rsv_(fdt, n);
181 }
182
fdt_get_mem_rsv(const void * fdt,int n,uint64_t * address,uint64_t * size)183 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
184 {
185 const struct fdt_reserve_entry *re;
186
187 FDT_RO_PROBE(fdt);
188 re = fdt_mem_rsv(fdt, n);
189 if (!can_assume(VALID_INPUT) && !re)
190 return -FDT_ERR_BADOFFSET;
191
192 *address = fdt64_ld_(&re->address);
193 *size = fdt64_ld_(&re->size);
194 return 0;
195 }
196
fdt_num_mem_rsv(const void * fdt)197 int fdt_num_mem_rsv(const void *fdt)
198 {
199 int i;
200 const struct fdt_reserve_entry *re;
201
202 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
203 if (fdt64_ld_(&re->size) == 0)
204 return i;
205 }
206 return -FDT_ERR_TRUNCATED;
207 }
208
nextprop_(const void * fdt,int offset)209 static int nextprop_(const void *fdt, int offset)
210 {
211 uint32_t tag;
212 int nextoffset;
213
214 do {
215 tag = fdt_next_tag(fdt, offset, &nextoffset);
216
217 switch (tag) {
218 case FDT_END:
219 if (nextoffset >= 0)
220 return -FDT_ERR_BADSTRUCTURE;
221 else
222 return nextoffset;
223
224 case FDT_PROP:
225 return offset;
226 }
227 offset = nextoffset;
228 } while (tag == FDT_NOP);
229
230 return -FDT_ERR_NOTFOUND;
231 }
232
fdt_subnode_offset_namelen(const void * fdt,int offset,const char * name,int namelen)233 int fdt_subnode_offset_namelen(const void *fdt, int offset,
234 const char *name, int namelen)
235 {
236 int depth;
237
238 FDT_RO_PROBE(fdt);
239
240 for (depth = 0;
241 (offset >= 0) && (depth >= 0);
242 offset = fdt_next_node(fdt, offset, &depth))
243 if ((depth == 1)
244 && fdt_nodename_eq_(fdt, offset, name, namelen))
245 return offset;
246
247 if (depth < 0)
248 return -FDT_ERR_NOTFOUND;
249 return offset; /* error */
250 }
251
fdt_subnode_offset(const void * fdt,int parentoffset,const char * name)252 int fdt_subnode_offset(const void *fdt, int parentoffset,
253 const char *name)
254 {
255 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
256 }
257
fdt_path_offset_namelen(const void * fdt,const char * path,int namelen)258 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
259 {
260 const char *end = path + namelen;
261 const char *p = path;
262 int offset = 0;
263
264 FDT_RO_PROBE(fdt);
265
266 if (namelen < 1)
267 return -FDT_ERR_BADPATH;
268
269 if (namelen < 1)
270 return -FDT_ERR_BADPATH;
271
272 /* see if we have an alias */
273 if (*path != '/') {
274 const char *q = memchr(path, '/', end - p);
275
276 if (!q)
277 q = end;
278
279 p = fdt_get_alias_namelen(fdt, p, q - p);
280 if (!p)
281 return -FDT_ERR_BADPATH;
282 offset = fdt_path_offset(fdt, p);
283
284 p = q;
285 }
286
287 while (p < end) {
288 const char *q;
289
290 while (*p == '/') {
291 p++;
292 if (p == end)
293 return offset;
294 }
295 q = memchr(p, '/', end - p);
296 if (! q)
297 q = end;
298
299 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
300 if (offset < 0)
301 return offset;
302
303 p = q;
304 }
305
306 return offset;
307 }
308
fdt_path_offset(const void * fdt,const char * path)309 int fdt_path_offset(const void *fdt, const char *path)
310 {
311 return fdt_path_offset_namelen(fdt, path, strlen(path));
312 }
313
fdt_get_name(const void * fdt,int nodeoffset,int * len)314 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
315 {
316 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
317 const char *nameptr;
318 int err;
319
320 if (((err = fdt_ro_probe_(fdt)) < 0)
321 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
322 goto fail;
323
324 nameptr = nh->name;
325
326 if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
327 /*
328 * For old FDT versions, match the naming conventions of V16:
329 * give only the leaf name (after all /). The actual tree
330 * contents are loosely checked.
331 */
332 const char *leaf;
333 leaf = strrchr(nameptr, '/');
334 if (leaf == NULL) {
335 err = -FDT_ERR_BADSTRUCTURE;
336 goto fail;
337 }
338 nameptr = leaf+1;
339 }
340
341 if (len)
342 *len = strlen(nameptr);
343
344 return nameptr;
345
346 fail:
347 if (len)
348 *len = err;
349 return NULL;
350 }
351
fdt_first_property_offset(const void * fdt,int nodeoffset)352 int fdt_first_property_offset(const void *fdt, int nodeoffset)
353 {
354 int offset;
355
356 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
357 return offset;
358
359 return nextprop_(fdt, offset);
360 }
361
fdt_next_property_offset(const void * fdt,int offset)362 int fdt_next_property_offset(const void *fdt, int offset)
363 {
364 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
365 return offset;
366
367 return nextprop_(fdt, offset);
368 }
369
fdt_get_property_by_offset_(const void * fdt,int offset,int * lenp)370 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
371 int offset,
372 int *lenp)
373 {
374 int err;
375 const struct fdt_property *prop;
376
377 if (!can_assume(VALID_INPUT) &&
378 (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
379 if (lenp)
380 *lenp = err;
381 return NULL;
382 }
383
384 prop = fdt_offset_ptr_(fdt, offset);
385
386 if (lenp)
387 *lenp = fdt32_ld_(&prop->len);
388
389 return prop;
390 }
391
fdt_get_property_by_offset(const void * fdt,int offset,int * lenp)392 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
393 int offset,
394 int *lenp)
395 {
396 /* Prior to version 16, properties may need realignment
397 * and this API does not work. fdt_getprop_*() will, however. */
398
399 if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
400 if (lenp)
401 *lenp = -FDT_ERR_BADVERSION;
402 return NULL;
403 }
404
405 return fdt_get_property_by_offset_(fdt, offset, lenp);
406 }
407
fdt_get_property_namelen_(const void * fdt,int offset,const char * name,int namelen,int * lenp,int * poffset)408 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
409 int offset,
410 const char *name,
411 int namelen,
412 int *lenp,
413 int *poffset)
414 {
415 for (offset = fdt_first_property_offset(fdt, offset);
416 (offset >= 0);
417 (offset = fdt_next_property_offset(fdt, offset))) {
418 const struct fdt_property *prop;
419
420 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
421 if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
422 offset = -FDT_ERR_INTERNAL;
423 break;
424 }
425 if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
426 name, namelen)) {
427 if (poffset)
428 *poffset = offset;
429 return prop;
430 }
431 }
432
433 if (lenp)
434 *lenp = offset;
435 return NULL;
436 }
437
438
fdt_get_property_namelen(const void * fdt,int offset,const char * name,int namelen,int * lenp)439 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
440 int offset,
441 const char *name,
442 int namelen, int *lenp)
443 {
444 /* Prior to version 16, properties may need realignment
445 * and this API does not work. fdt_getprop_*() will, however. */
446 if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
447 if (lenp)
448 *lenp = -FDT_ERR_BADVERSION;
449 return NULL;
450 }
451
452 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
453 NULL);
454 }
455
456
fdt_get_property(const void * fdt,int nodeoffset,const char * name,int * lenp)457 const struct fdt_property *fdt_get_property(const void *fdt,
458 int nodeoffset,
459 const char *name, int *lenp)
460 {
461 return fdt_get_property_namelen(fdt, nodeoffset, name,
462 strlen(name), lenp);
463 }
464
fdt_getprop_namelen(const void * fdt,int nodeoffset,const char * name,int namelen,int * lenp)465 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
466 const char *name, int namelen, int *lenp)
467 {
468 int poffset;
469 const struct fdt_property *prop;
470
471 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
472 &poffset);
473 if (!prop)
474 return NULL;
475
476 /* Handle realignment */
477 if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
478 (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
479 return prop->data + 4;
480 return prop->data;
481 }
482
fdt_getprop_by_offset(const void * fdt,int offset,const char ** namep,int * lenp)483 const void *fdt_getprop_by_offset(const void *fdt, int offset,
484 const char **namep, int *lenp)
485 {
486 const struct fdt_property *prop;
487
488 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
489 if (!prop)
490 return NULL;
491 if (namep) {
492 const char *name;
493 int namelen;
494
495 if (!can_assume(VALID_INPUT)) {
496 name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
497 &namelen);
498 *namep = name;
499 if (!name) {
500 if (lenp)
501 *lenp = namelen;
502 return NULL;
503 }
504 } else {
505 *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
506 }
507 }
508
509 /* Handle realignment */
510 if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
511 (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
512 return prop->data + 4;
513 return prop->data;
514 }
515
fdt_getprop(const void * fdt,int nodeoffset,const char * name,int * lenp)516 const void *fdt_getprop(const void *fdt, int nodeoffset,
517 const char *name, int *lenp)
518 {
519 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
520 }
521
fdt_get_phandle(const void * fdt,int nodeoffset)522 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
523 {
524 const fdt32_t *php;
525 int len;
526
527 /* FIXME: This is a bit sub-optimal, since we potentially scan
528 * over all the properties twice. */
529 php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
530 if (!php || (len != sizeof(*php))) {
531 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
532 if (!php || (len != sizeof(*php)))
533 return 0;
534 }
535
536 return fdt32_ld_(php);
537 }
538
fdt_get_alias_namelen(const void * fdt,const char * name,int namelen)539 const char *fdt_get_alias_namelen(const void *fdt,
540 const char *name, int namelen)
541 {
542 const char *prop;
543 int aliasoffset;
544 int prop_len;
545
546 aliasoffset = fdt_path_offset(fdt, "/aliases");
547 if (aliasoffset < 0)
548 return NULL;
549
550 prop = fdt_getprop_namelen(fdt, aliasoffset, name, namelen, &prop_len);
551 if (prop && !can_assume(VALID_INPUT)) {
552 /* Validate the alias value. From the devicetree spec v0.3:
553 * "An alias value is a device path and is encoded as a string.
554 * The value representes the full path to a node, ..."
555 * A full path must start at the root to prevent recursion.
556 */
557 if (prop_len == 0 || *prop != '/' || !is_nul_string(prop, prop_len)) {
558 prop = NULL;
559 }
560 }
561
562 return prop;
563 }
564
fdt_get_alias(const void * fdt,const char * name)565 const char *fdt_get_alias(const void *fdt, const char *name)
566 {
567 return fdt_get_alias_namelen(fdt, name, strlen(name));
568 }
569
fdt_get_path(const void * fdt,int nodeoffset,char * buf,int buflen)570 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
571 {
572 int pdepth = 0, p = 0;
573 int offset, depth, namelen;
574 const char *name;
575
576 FDT_RO_PROBE(fdt);
577
578 if (buflen < 2)
579 return -FDT_ERR_NOSPACE;
580
581 for (offset = 0, depth = 0;
582 (offset >= 0) && (offset <= nodeoffset);
583 offset = fdt_next_node(fdt, offset, &depth)) {
584 while (pdepth > depth) {
585 do {
586 p--;
587 } while (buf[p-1] != '/');
588 pdepth--;
589 }
590
591 if (pdepth >= depth) {
592 name = fdt_get_name(fdt, offset, &namelen);
593 if (!name)
594 return namelen;
595 if ((p + namelen + 1) <= buflen) {
596 memcpy(buf + p, name, namelen);
597 p += namelen;
598 buf[p++] = '/';
599 pdepth++;
600 }
601 }
602
603 if (offset == nodeoffset) {
604 if (pdepth < (depth + 1))
605 return -FDT_ERR_NOSPACE;
606
607 if (p > 1) /* special case so that root path is "/", not "" */
608 p--;
609 buf[p] = '\0';
610 return 0;
611 }
612 }
613
614 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
615 return -FDT_ERR_BADOFFSET;
616 else if (offset == -FDT_ERR_BADOFFSET)
617 return -FDT_ERR_BADSTRUCTURE;
618
619 return offset; /* error from fdt_next_node() */
620 }
621
fdt_supernode_atdepth_offset(const void * fdt,int nodeoffset,int supernodedepth,int * nodedepth)622 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
623 int supernodedepth, int *nodedepth)
624 {
625 int offset, depth;
626 int supernodeoffset = -FDT_ERR_INTERNAL;
627
628 FDT_RO_PROBE(fdt);
629
630 if (supernodedepth < 0)
631 return -FDT_ERR_NOTFOUND;
632
633 for (offset = 0, depth = 0;
634 (offset >= 0) && (offset <= nodeoffset);
635 offset = fdt_next_node(fdt, offset, &depth)) {
636 if (depth == supernodedepth)
637 supernodeoffset = offset;
638
639 if (offset == nodeoffset) {
640 if (nodedepth)
641 *nodedepth = depth;
642
643 if (supernodedepth > depth)
644 return -FDT_ERR_NOTFOUND;
645 else
646 return supernodeoffset;
647 }
648 }
649
650 if (!can_assume(VALID_INPUT)) {
651 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
652 return -FDT_ERR_BADOFFSET;
653 else if (offset == -FDT_ERR_BADOFFSET)
654 return -FDT_ERR_BADSTRUCTURE;
655 }
656
657 return offset; /* error from fdt_next_node() */
658 }
659
fdt_node_depth(const void * fdt,int nodeoffset)660 int fdt_node_depth(const void *fdt, int nodeoffset)
661 {
662 int nodedepth;
663 int err;
664
665 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
666 if (err)
667 return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
668 -FDT_ERR_INTERNAL;
669 return nodedepth;
670 }
671
fdt_parent_offset(const void * fdt,int nodeoffset)672 int fdt_parent_offset(const void *fdt, int nodeoffset)
673 {
674 int nodedepth = fdt_node_depth(fdt, nodeoffset);
675
676 if (nodedepth < 0)
677 return nodedepth;
678 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
679 nodedepth - 1, NULL);
680 }
681
fdt_node_offset_by_prop_value(const void * fdt,int startoffset,const char * propname,const void * propval,int proplen)682 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
683 const char *propname,
684 const void *propval, int proplen)
685 {
686 int offset;
687 const void *val;
688 int len;
689
690 FDT_RO_PROBE(fdt);
691
692 /* FIXME: The algorithm here is pretty horrible: we scan each
693 * property of a node in fdt_getprop(), then if that didn't
694 * find what we want, we scan over them again making our way
695 * to the next node. Still it's the easiest to implement
696 * approach; performance can come later. */
697 for (offset = fdt_next_node(fdt, startoffset, NULL);
698 offset >= 0;
699 offset = fdt_next_node(fdt, offset, NULL)) {
700 val = fdt_getprop(fdt, offset, propname, &len);
701 if (val && (len == proplen)
702 && (memcmp(val, propval, len) == 0))
703 return offset;
704 }
705
706 return offset; /* error from fdt_next_node() */
707 }
708
fdt_node_offset_by_phandle(const void * fdt,uint32_t phandle)709 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
710 {
711 int offset;
712
713 if ((phandle == 0) || (phandle == ~0U))
714 return -FDT_ERR_BADPHANDLE;
715
716 FDT_RO_PROBE(fdt);
717
718 /* FIXME: The algorithm here is pretty horrible: we
719 * potentially scan each property of a node in
720 * fdt_get_phandle(), then if that didn't find what
721 * we want, we scan over them again making our way to the next
722 * node. Still it's the easiest to implement approach;
723 * performance can come later. */
724 for (offset = fdt_next_node(fdt, -1, NULL);
725 offset >= 0;
726 offset = fdt_next_node(fdt, offset, NULL)) {
727 if (fdt_get_phandle(fdt, offset) == phandle)
728 return offset;
729 }
730
731 return offset; /* error from fdt_next_node() */
732 }
733
fdt_stringlist_contains(const char * strlist,int listlen,const char * str)734 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
735 {
736 int len = strlen(str);
737 const char *p;
738
739 while (listlen >= len) {
740 if (memcmp(str, strlist, len+1) == 0)
741 return 1;
742 p = memchr(strlist, '\0', listlen);
743 if (!p)
744 return 0; /* malformed strlist.. */
745 listlen -= (p-strlist) + 1;
746 strlist = p + 1;
747 }
748 return 0;
749 }
750
fdt_stringlist_count(const void * fdt,int nodeoffset,const char * property)751 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
752 {
753 const char *list, *end;
754 int length, count = 0;
755
756 list = fdt_getprop(fdt, nodeoffset, property, &length);
757 if (!list)
758 return length;
759
760 end = list + length;
761
762 while (list < end) {
763 length = strnlen(list, end - list) + 1;
764
765 /* Abort if the last string isn't properly NUL-terminated. */
766 if (list + length > end)
767 return -FDT_ERR_BADVALUE;
768
769 list += length;
770 count++;
771 }
772
773 return count;
774 }
775
fdt_stringlist_search(const void * fdt,int nodeoffset,const char * property,const char * string)776 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
777 const char *string)
778 {
779 int length, len, idx = 0;
780 const char *list, *end;
781
782 list = fdt_getprop(fdt, nodeoffset, property, &length);
783 if (!list)
784 return length;
785
786 len = strlen(string) + 1;
787 end = list + length;
788
789 while (list < end) {
790 length = strnlen(list, end - list) + 1;
791
792 /* Abort if the last string isn't properly NUL-terminated. */
793 if (list + length > end)
794 return -FDT_ERR_BADVALUE;
795
796 if (length == len && memcmp(list, string, length) == 0)
797 return idx;
798
799 list += length;
800 idx++;
801 }
802
803 return -FDT_ERR_NOTFOUND;
804 }
805
fdt_stringlist_get(const void * fdt,int nodeoffset,const char * property,int idx,int * lenp)806 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
807 const char *property, int idx,
808 int *lenp)
809 {
810 const char *list, *end;
811 int length;
812
813 list = fdt_getprop(fdt, nodeoffset, property, &length);
814 if (!list) {
815 if (lenp)
816 *lenp = length;
817
818 return NULL;
819 }
820
821 end = list + length;
822
823 while (list < end) {
824 length = strnlen(list, end - list) + 1;
825
826 /* Abort if the last string isn't properly NUL-terminated. */
827 if (list + length > end) {
828 if (lenp)
829 *lenp = -FDT_ERR_BADVALUE;
830
831 return NULL;
832 }
833
834 if (idx == 0) {
835 if (lenp)
836 *lenp = length - 1;
837
838 return list;
839 }
840
841 list += length;
842 idx--;
843 }
844
845 if (lenp)
846 *lenp = -FDT_ERR_NOTFOUND;
847
848 return NULL;
849 }
850
fdt_node_check_compatible(const void * fdt,int nodeoffset,const char * compatible)851 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
852 const char *compatible)
853 {
854 const void *prop;
855 int len;
856
857 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
858 if (!prop)
859 return len;
860
861 return !fdt_stringlist_contains(prop, len, compatible);
862 }
863
fdt_node_offset_by_compatible(const void * fdt,int startoffset,const char * compatible)864 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
865 const char *compatible)
866 {
867 int offset, err;
868
869 FDT_RO_PROBE(fdt);
870
871 /* FIXME: The algorithm here is pretty horrible: we scan each
872 * property of a node in fdt_node_check_compatible(), then if
873 * that didn't find what we want, we scan over them again
874 * making our way to the next node. Still it's the easiest to
875 * implement approach; performance can come later. */
876 for (offset = fdt_next_node(fdt, startoffset, NULL);
877 offset >= 0;
878 offset = fdt_next_node(fdt, offset, NULL)) {
879 err = fdt_node_check_compatible(fdt, offset, compatible);
880 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
881 return err;
882 else if (err == 0)
883 return offset;
884 }
885
886 return offset; /* error from fdt_next_node() */
887 }
888