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