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