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