xref: /netbsd-src/sys/external/bsd/libfdt/dist/fdt_ro.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1 /*	$NetBSD: fdt_ro.c,v 1.1.1.2 2017/06/08 15:53:12 skrll Exp $	*/
2 
3 /*
4  * libfdt - Flat Device Tree manipulation
5  * Copyright (C) 2006 David Gibson, IBM Corporation.
6  *
7  * libfdt is dual licensed: you can use it either under the terms of
8  * the GPL, or the BSD license, at your option.
9  *
10  *  a) This library is free software; you can redistribute it and/or
11  *     modify it under the terms of the GNU General Public License as
12  *     published by the Free Software Foundation; either version 2 of the
13  *     License, or (at your option) any later version.
14  *
15  *     This library is distributed in the hope that it will be useful,
16  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *     GNU General Public License for more details.
19  *
20  *     You should have received a copy of the GNU General Public
21  *     License along with this library; if not, write to the Free
22  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
23  *     MA 02110-1301 USA
24  *
25  * Alternatively,
26  *
27  *  b) Redistribution and use in source and binary forms, with or
28  *     without modification, are permitted provided that the following
29  *     conditions are met:
30  *
31  *     1. Redistributions of source code must retain the above
32  *        copyright notice, this list of conditions and the following
33  *        disclaimer.
34  *     2. Redistributions in binary form must reproduce the above
35  *        copyright notice, this list of conditions and the following
36  *        disclaimer in the documentation and/or other materials
37  *        provided with the distribution.
38  *
39  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
40  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
41  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
42  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
44  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
50  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
51  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52  */
53 #include "libfdt_env.h"
54 
55 #include <fdt.h>
56 #include <libfdt.h>
57 
58 #include "libfdt_internal.h"
59 
60 static int _fdt_nodename_eq(const void *fdt, int offset,
61 			    const char *s, int len)
62 {
63 	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
64 
65 	if (! p)
66 		/* short match */
67 		return 0;
68 
69 	if (memcmp(p, s, len) != 0)
70 		return 0;
71 
72 	if (p[len] == '\0')
73 		return 1;
74 	else if (!memchr(s, '@', len) && (p[len] == '@'))
75 		return 1;
76 	else
77 		return 0;
78 }
79 
80 const char *fdt_string(const void *fdt, int stroffset)
81 {
82 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
83 }
84 
85 static int _fdt_string_eq(const void *fdt, int stroffset,
86 			  const char *s, int len)
87 {
88 	const char *p = fdt_string(fdt, stroffset);
89 
90 	return (strlen(p) == len) && (memcmp(p, s, len) == 0);
91 }
92 
93 uint32_t fdt_get_max_phandle(const void *fdt)
94 {
95 	uint32_t max_phandle = 0;
96 	int offset;
97 
98 	for (offset = fdt_next_node(fdt, -1, NULL);;
99 	     offset = fdt_next_node(fdt, offset, NULL)) {
100 		uint32_t phandle;
101 
102 		if (offset == -FDT_ERR_NOTFOUND)
103 			return max_phandle;
104 
105 		if (offset < 0)
106 			return (uint32_t)-1;
107 
108 		phandle = fdt_get_phandle(fdt, offset);
109 		if (phandle == (uint32_t)-1)
110 			continue;
111 
112 		if (phandle > max_phandle)
113 			max_phandle = phandle;
114 	}
115 
116 	return 0;
117 }
118 
119 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
120 {
121 	FDT_CHECK_HEADER(fdt);
122 	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
123 	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
124 	return 0;
125 }
126 
127 int fdt_num_mem_rsv(const void *fdt)
128 {
129 	int i = 0;
130 
131 	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
132 		i++;
133 	return i;
134 }
135 
136 static int _nextprop(const void *fdt, int offset)
137 {
138 	uint32_t tag;
139 	int nextoffset;
140 
141 	do {
142 		tag = fdt_next_tag(fdt, offset, &nextoffset);
143 
144 		switch (tag) {
145 		case FDT_END:
146 			if (nextoffset >= 0)
147 				return -FDT_ERR_BADSTRUCTURE;
148 			else
149 				return nextoffset;
150 
151 		case FDT_PROP:
152 			return offset;
153 		}
154 		offset = nextoffset;
155 	} while (tag == FDT_NOP);
156 
157 	return -FDT_ERR_NOTFOUND;
158 }
159 
160 int fdt_subnode_offset_namelen(const void *fdt, int offset,
161 			       const char *name, int namelen)
162 {
163 	int depth;
164 
165 	FDT_CHECK_HEADER(fdt);
166 
167 	for (depth = 0;
168 	     (offset >= 0) && (depth >= 0);
169 	     offset = fdt_next_node(fdt, offset, &depth))
170 		if ((depth == 1)
171 		    && _fdt_nodename_eq(fdt, offset, name, namelen))
172 			return offset;
173 
174 	if (depth < 0)
175 		return -FDT_ERR_NOTFOUND;
176 	return offset; /* error */
177 }
178 
179 int fdt_subnode_offset(const void *fdt, int parentoffset,
180 		       const char *name)
181 {
182 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
183 }
184 
185 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
186 {
187 	const char *end = path + namelen;
188 	const char *p = path;
189 	int offset = 0;
190 
191 	FDT_CHECK_HEADER(fdt);
192 
193 	/* see if we have an alias */
194 	if (*path != '/') {
195 		const char *q = memchr(path, '/', end - p);
196 
197 		if (!q)
198 			q = end;
199 
200 		p = fdt_get_alias_namelen(fdt, p, q - p);
201 		if (!p)
202 			return -FDT_ERR_BADPATH;
203 		offset = fdt_path_offset(fdt, p);
204 
205 		p = q;
206 	}
207 
208 	while (p < end) {
209 		const char *q;
210 
211 		while (*p == '/') {
212 			p++;
213 			if (p == end)
214 				return offset;
215 		}
216 		q = memchr(p, '/', end - p);
217 		if (! q)
218 			q = end;
219 
220 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
221 		if (offset < 0)
222 			return offset;
223 
224 		p = q;
225 	}
226 
227 	return offset;
228 }
229 
230 int fdt_path_offset(const void *fdt, const char *path)
231 {
232 	return fdt_path_offset_namelen(fdt, path, strlen(path));
233 }
234 
235 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
236 {
237 	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
238 	int err;
239 
240 	if (((err = fdt_check_header(fdt)) != 0)
241 	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
242 			goto fail;
243 
244 	if (len)
245 		*len = strlen(nh->name);
246 
247 	return nh->name;
248 
249  fail:
250 	if (len)
251 		*len = err;
252 	return NULL;
253 }
254 
255 int fdt_first_property_offset(const void *fdt, int nodeoffset)
256 {
257 	int offset;
258 
259 	if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
260 		return offset;
261 
262 	return _nextprop(fdt, offset);
263 }
264 
265 int fdt_next_property_offset(const void *fdt, int offset)
266 {
267 	if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
268 		return offset;
269 
270 	return _nextprop(fdt, offset);
271 }
272 
273 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
274 						      int offset,
275 						      int *lenp)
276 {
277 	int err;
278 	const struct fdt_property *prop;
279 
280 	if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
281 		if (lenp)
282 			*lenp = err;
283 		return NULL;
284 	}
285 
286 	prop = _fdt_offset_ptr(fdt, offset);
287 
288 	if (lenp)
289 		*lenp = fdt32_to_cpu(prop->len);
290 
291 	return prop;
292 }
293 
294 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
295 						    int offset,
296 						    const char *name,
297 						    int namelen, int *lenp)
298 {
299 	for (offset = fdt_first_property_offset(fdt, offset);
300 	     (offset >= 0);
301 	     (offset = fdt_next_property_offset(fdt, offset))) {
302 		const struct fdt_property *prop;
303 
304 		if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
305 			offset = -FDT_ERR_INTERNAL;
306 			break;
307 		}
308 		if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
309 				   name, namelen))
310 			return prop;
311 	}
312 
313 	if (lenp)
314 		*lenp = offset;
315 	return NULL;
316 }
317 
318 const struct fdt_property *fdt_get_property(const void *fdt,
319 					    int nodeoffset,
320 					    const char *name, int *lenp)
321 {
322 	return fdt_get_property_namelen(fdt, nodeoffset, name,
323 					strlen(name), lenp);
324 }
325 
326 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
327 				const char *name, int namelen, int *lenp)
328 {
329 	const struct fdt_property *prop;
330 
331 	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
332 	if (! prop)
333 		return NULL;
334 
335 	return prop->data;
336 }
337 
338 const void *fdt_getprop_by_offset(const void *fdt, int offset,
339 				  const char **namep, int *lenp)
340 {
341 	const struct fdt_property *prop;
342 
343 	prop = fdt_get_property_by_offset(fdt, offset, lenp);
344 	if (!prop)
345 		return NULL;
346 	if (namep)
347 		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
348 	return prop->data;
349 }
350 
351 const void *fdt_getprop(const void *fdt, int nodeoffset,
352 			const char *name, int *lenp)
353 {
354 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
355 }
356 
357 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
358 {
359 	const fdt32_t *php;
360 	int len;
361 
362 	/* FIXME: This is a bit sub-optimal, since we potentially scan
363 	 * over all the properties twice. */
364 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
365 	if (!php || (len != sizeof(*php))) {
366 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
367 		if (!php || (len != sizeof(*php)))
368 			return 0;
369 	}
370 
371 	return fdt32_to_cpu(*php);
372 }
373 
374 const char *fdt_get_alias_namelen(const void *fdt,
375 				  const char *name, int namelen)
376 {
377 	int aliasoffset;
378 
379 	aliasoffset = fdt_path_offset(fdt, "/aliases");
380 	if (aliasoffset < 0)
381 		return NULL;
382 
383 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
384 }
385 
386 const char *fdt_get_alias(const void *fdt, const char *name)
387 {
388 	return fdt_get_alias_namelen(fdt, name, strlen(name));
389 }
390 
391 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
392 {
393 	int pdepth = 0, p = 0;
394 	int offset, depth, namelen;
395 	const char *name;
396 
397 	FDT_CHECK_HEADER(fdt);
398 
399 	if (buflen < 2)
400 		return -FDT_ERR_NOSPACE;
401 
402 	for (offset = 0, depth = 0;
403 	     (offset >= 0) && (offset <= nodeoffset);
404 	     offset = fdt_next_node(fdt, offset, &depth)) {
405 		while (pdepth > depth) {
406 			do {
407 				p--;
408 			} while (buf[p-1] != '/');
409 			pdepth--;
410 		}
411 
412 		if (pdepth >= depth) {
413 			name = fdt_get_name(fdt, offset, &namelen);
414 			if (!name)
415 				return namelen;
416 			if ((p + namelen + 1) <= buflen) {
417 				memcpy(buf + p, name, namelen);
418 				p += namelen;
419 				buf[p++] = '/';
420 				pdepth++;
421 			}
422 		}
423 
424 		if (offset == nodeoffset) {
425 			if (pdepth < (depth + 1))
426 				return -FDT_ERR_NOSPACE;
427 
428 			if (p > 1) /* special case so that root path is "/", not "" */
429 				p--;
430 			buf[p] = '\0';
431 			return 0;
432 		}
433 	}
434 
435 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
436 		return -FDT_ERR_BADOFFSET;
437 	else if (offset == -FDT_ERR_BADOFFSET)
438 		return -FDT_ERR_BADSTRUCTURE;
439 
440 	return offset; /* error from fdt_next_node() */
441 }
442 
443 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
444 				 int supernodedepth, int *nodedepth)
445 {
446 	int offset, depth;
447 	int supernodeoffset = -FDT_ERR_INTERNAL;
448 
449 	FDT_CHECK_HEADER(fdt);
450 
451 	if (supernodedepth < 0)
452 		return -FDT_ERR_NOTFOUND;
453 
454 	for (offset = 0, depth = 0;
455 	     (offset >= 0) && (offset <= nodeoffset);
456 	     offset = fdt_next_node(fdt, offset, &depth)) {
457 		if (depth == supernodedepth)
458 			supernodeoffset = offset;
459 
460 		if (offset == nodeoffset) {
461 			if (nodedepth)
462 				*nodedepth = depth;
463 
464 			if (supernodedepth > depth)
465 				return -FDT_ERR_NOTFOUND;
466 			else
467 				return supernodeoffset;
468 		}
469 	}
470 
471 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
472 		return -FDT_ERR_BADOFFSET;
473 	else if (offset == -FDT_ERR_BADOFFSET)
474 		return -FDT_ERR_BADSTRUCTURE;
475 
476 	return offset; /* error from fdt_next_node() */
477 }
478 
479 int fdt_node_depth(const void *fdt, int nodeoffset)
480 {
481 	int nodedepth;
482 	int err;
483 
484 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
485 	if (err)
486 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
487 	return nodedepth;
488 }
489 
490 int fdt_parent_offset(const void *fdt, int nodeoffset)
491 {
492 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
493 
494 	if (nodedepth < 0)
495 		return nodedepth;
496 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
497 					    nodedepth - 1, NULL);
498 }
499 
500 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
501 				  const char *propname,
502 				  const void *propval, int proplen)
503 {
504 	int offset;
505 	const void *val;
506 	int len;
507 
508 	FDT_CHECK_HEADER(fdt);
509 
510 	/* FIXME: The algorithm here is pretty horrible: we scan each
511 	 * property of a node in fdt_getprop(), then if that didn't
512 	 * find what we want, we scan over them again making our way
513 	 * to the next node.  Still it's the easiest to implement
514 	 * approach; performance can come later. */
515 	for (offset = fdt_next_node(fdt, startoffset, NULL);
516 	     offset >= 0;
517 	     offset = fdt_next_node(fdt, offset, NULL)) {
518 		val = fdt_getprop(fdt, offset, propname, &len);
519 		if (val && (len == proplen)
520 		    && (memcmp(val, propval, len) == 0))
521 			return offset;
522 	}
523 
524 	return offset; /* error from fdt_next_node() */
525 }
526 
527 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
528 {
529 	int offset;
530 
531 	if ((phandle == 0) || (phandle == -1))
532 		return -FDT_ERR_BADPHANDLE;
533 
534 	FDT_CHECK_HEADER(fdt);
535 
536 	/* FIXME: The algorithm here is pretty horrible: we
537 	 * potentially scan each property of a node in
538 	 * fdt_get_phandle(), then if that didn't find what
539 	 * we want, we scan over them again making our way to the next
540 	 * node.  Still it's the easiest to implement approach;
541 	 * performance can come later. */
542 	for (offset = fdt_next_node(fdt, -1, NULL);
543 	     offset >= 0;
544 	     offset = fdt_next_node(fdt, offset, NULL)) {
545 		if (fdt_get_phandle(fdt, offset) == phandle)
546 			return offset;
547 	}
548 
549 	return offset; /* error from fdt_next_node() */
550 }
551 
552 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
553 {
554 	int len = strlen(str);
555 	const char *p;
556 
557 	while (listlen >= len) {
558 		if (memcmp(str, strlist, len+1) == 0)
559 			return 1;
560 		p = memchr(strlist, '\0', listlen);
561 		if (!p)
562 			return 0; /* malformed strlist.. */
563 		listlen -= (p-strlist) + 1;
564 		strlist = p + 1;
565 	}
566 	return 0;
567 }
568 
569 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
570 {
571 	const char *list, *end;
572 	int length, count = 0;
573 
574 	list = fdt_getprop(fdt, nodeoffset, property, &length);
575 	if (!list)
576 		return length;
577 
578 	end = list + length;
579 
580 	while (list < end) {
581 		length = strnlen(list, end - list) + 1;
582 
583 		/* Abort if the last string isn't properly NUL-terminated. */
584 		if (list + length > end)
585 			return -FDT_ERR_BADVALUE;
586 
587 		list += length;
588 		count++;
589 	}
590 
591 	return count;
592 }
593 
594 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
595 			  const char *string)
596 {
597 	int length, len, idx = 0;
598 	const char *list, *end;
599 
600 	list = fdt_getprop(fdt, nodeoffset, property, &length);
601 	if (!list)
602 		return length;
603 
604 	len = strlen(string) + 1;
605 	end = list + length;
606 
607 	while (list < end) {
608 		length = strnlen(list, end - list) + 1;
609 
610 		/* Abort if the last string isn't properly NUL-terminated. */
611 		if (list + length > end)
612 			return -FDT_ERR_BADVALUE;
613 
614 		if (length == len && memcmp(list, string, length) == 0)
615 			return idx;
616 
617 		list += length;
618 		idx++;
619 	}
620 
621 	return -FDT_ERR_NOTFOUND;
622 }
623 
624 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
625 			       const char *property, int idx,
626 			       int *lenp)
627 {
628 	const char *list, *end;
629 	int length;
630 
631 	list = fdt_getprop(fdt, nodeoffset, property, &length);
632 	if (!list) {
633 		if (lenp)
634 			*lenp = length;
635 
636 		return NULL;
637 	}
638 
639 	end = list + length;
640 
641 	while (list < end) {
642 		length = strnlen(list, end - list) + 1;
643 
644 		/* Abort if the last string isn't properly NUL-terminated. */
645 		if (list + length > end) {
646 			if (lenp)
647 				*lenp = -FDT_ERR_BADVALUE;
648 
649 			return NULL;
650 		}
651 
652 		if (idx == 0) {
653 			if (lenp)
654 				*lenp = length - 1;
655 
656 			return list;
657 		}
658 
659 		list += length;
660 		idx--;
661 	}
662 
663 	if (lenp)
664 		*lenp = -FDT_ERR_NOTFOUND;
665 
666 	return NULL;
667 }
668 
669 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
670 			      const char *compatible)
671 {
672 	const void *prop;
673 	int len;
674 
675 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
676 	if (!prop)
677 		return len;
678 
679 	return !fdt_stringlist_contains(prop, len, compatible);
680 }
681 
682 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
683 				  const char *compatible)
684 {
685 	int offset, err;
686 
687 	FDT_CHECK_HEADER(fdt);
688 
689 	/* FIXME: The algorithm here is pretty horrible: we scan each
690 	 * property of a node in fdt_node_check_compatible(), then if
691 	 * that didn't find what we want, we scan over them again
692 	 * making our way to the next node.  Still it's the easiest to
693 	 * implement approach; performance can come later. */
694 	for (offset = fdt_next_node(fdt, startoffset, NULL);
695 	     offset >= 0;
696 	     offset = fdt_next_node(fdt, offset, NULL)) {
697 		err = fdt_node_check_compatible(fdt, offset, compatible);
698 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
699 			return err;
700 		else if (err == 0)
701 			return offset;
702 	}
703 
704 	return offset; /* error from fdt_next_node() */
705 }
706