xref: /netbsd/sys/external/bsd/libfdt/dist/fdt_ro.c (revision b9ee18de)
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 
fdt_nodename_eq_(const void * fdt,int offset,const char * s,int len)15 static 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 
fdt_get_string(const void * fdt,int stroffset,int * lenp)36 const 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 
85 fail:
86 	if (lenp)
87 		*lenp = err;
88 	return NULL;
89 }
90 
fdt_string(const void * fdt,int stroffset)91 const char *fdt_string(const void *fdt, int stroffset)
92 {
93 	return fdt_get_string(fdt, stroffset, NULL);
94 }
95 
fdt_string_eq_(const void * fdt,int stroffset,const char * s,int len)96 static 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 
fdt_find_max_phandle(const void * fdt,uint32_t * phandle)105 int 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 
fdt_generate_phandle(const void * fdt,uint32_t * phandle)133 int 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 
fdt_mem_rsv(const void * fdt,int n)151 static 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 
fdt_get_mem_rsv(const void * fdt,int n,uint64_t * address,uint64_t * size)163 int 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 
fdt_num_mem_rsv(const void * fdt)177 int 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 
nextprop_(const void * fdt,int offset)189 static 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 
fdt_subnode_offset_namelen(const void * fdt,int offset,const char * name,int namelen)213 int 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 
fdt_subnode_offset(const void * fdt,int parentoffset,const char * name)232 int 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 
fdt_path_offset_namelen(const void * fdt,const char * path,int namelen)238 int 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 
fdt_path_offset(const void * fdt,const char * path)283 int fdt_path_offset(const void *fdt, const char *path)
284 {
285 	return fdt_path_offset_namelen(fdt, path, strlen(path));
286 }
287 
fdt_get_name(const void * fdt,int nodeoffset,int * len)288 const 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 
fdt_first_property_offset(const void * fdt,int nodeoffset)326 int 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 
fdt_next_property_offset(const void * fdt,int offset)336 int 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 
fdt_get_property_by_offset_(const void * fdt,int offset,int * lenp)344 static 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 
fdt_get_property_by_offset(const void * fdt,int offset,int * lenp)365 const 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 
fdt_get_property_namelen_(const void * fdt,int offset,const char * name,int namelen,int * lenp,int * poffset)381 static 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 
fdt_get_property_namelen(const void * fdt,int offset,const char * name,int namelen,int * lenp)411 const 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 
fdt_get_property(const void * fdt,int nodeoffset,const char * name,int * lenp)429 const 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 
fdt_getprop_namelen(const void * fdt,int nodeoffset,const char * name,int namelen,int * lenp)437 const 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 
fdt_getprop_by_offset(const void * fdt,int offset,const char ** namep,int * lenp)455 const 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 
fdt_getprop(const void * fdt,int nodeoffset,const char * name,int * lenp)483 const 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 
fdt_get_phandle(const void * fdt,int nodeoffset)489 uint32_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 
fdt_get_alias_namelen(const void * fdt,const char * name,int namelen)506 const 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 
fdt_get_alias(const void * fdt,const char * name)518 const char *fdt_get_alias(const void *fdt, const char *name)
519 {
520 	return fdt_get_alias_namelen(fdt, name, strlen(name));
521 }
522 
fdt_get_path(const void * fdt,int nodeoffset,char * buf,int buflen)523 int 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 
fdt_supernode_atdepth_offset(const void * fdt,int nodeoffset,int supernodedepth,int * nodedepth)575 int 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 
fdt_node_depth(const void * fdt,int nodeoffset)611 int 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 
fdt_parent_offset(const void * fdt,int nodeoffset)622 int 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 
fdt_node_offset_by_prop_value(const void * fdt,int startoffset,const char * propname,const void * propval,int proplen)632 int 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 
fdt_node_offset_by_phandle(const void * fdt,uint32_t phandle)659 int 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 
fdt_stringlist_contains(const char * strlist,int listlen,const char * str)684 int 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 
fdt_stringlist_count(const void * fdt,int nodeoffset,const char * property)701 int 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 
fdt_stringlist_search(const void * fdt,int nodeoffset,const char * property,const char * string)726 int 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 
fdt_stringlist_get(const void * fdt,int nodeoffset,const char * property,int idx,int * lenp)756 const 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 
fdt_node_check_compatible(const void * fdt,int nodeoffset,const char * compatible)801 int 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 
fdt_node_offset_by_compatible(const void * fdt,int startoffset,const char * compatible)814 int 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 
fdt_check_full(const void * fdt,size_t bufsize)839 int 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