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