xref: /openbmc/u-boot/lib/libfdt/fdt_ro.c (revision 3eceff64)
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 	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
21 
22 	if (!p)
23 		/* short match */
24 		return 0;
25 
26 	if (memcmp(p, s, len) != 0)
27 		return 0;
28 
29 	if (p[len] == '\0')
30 		return 1;
31 	else if (!memchr(s, '@', len) && (p[len] == '@'))
32 		return 1;
33 	else
34 		return 0;
35 }
36 
fdt_string(const void * fdt,int stroffset)37 const char *fdt_string(const void *fdt, int stroffset)
38 {
39 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
40 }
41 
_fdt_string_eq(const void * fdt,int stroffset,const char * s,int len)42 static int _fdt_string_eq(const void *fdt, int stroffset,
43 			  const char *s, int len)
44 {
45 	const char *p = fdt_string(fdt, stroffset);
46 
47 	return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0);
48 }
49 
fdt_get_max_phandle(const void * fdt)50 uint32_t fdt_get_max_phandle(const void *fdt)
51 {
52 	uint32_t max_phandle = 0;
53 	int offset;
54 
55 	for (offset = fdt_next_node(fdt, -1, NULL);;
56 	     offset = fdt_next_node(fdt, offset, NULL)) {
57 		uint32_t phandle;
58 
59 		if (offset == -FDT_ERR_NOTFOUND)
60 			return max_phandle;
61 
62 		if (offset < 0)
63 			return (uint32_t)-1;
64 
65 		phandle = fdt_get_phandle(fdt, offset);
66 		if (phandle == (uint32_t)-1)
67 			continue;
68 
69 		if (phandle > max_phandle)
70 			max_phandle = phandle;
71 	}
72 
73 	return 0;
74 }
75 
fdt_get_mem_rsv(const void * fdt,int n,uint64_t * address,uint64_t * size)76 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
77 {
78 	FDT_CHECK_HEADER(fdt);
79 	*address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address);
80 	*size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size);
81 	return 0;
82 }
83 
fdt_num_mem_rsv(const void * fdt)84 int fdt_num_mem_rsv(const void *fdt)
85 {
86 	int i = 0;
87 
88 	while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0)
89 		i++;
90 	return i;
91 }
92 
_nextprop(const void * fdt,int offset)93 static int _nextprop(const void *fdt, int offset)
94 {
95 	uint32_t tag;
96 	int nextoffset;
97 
98 	do {
99 		tag = fdt_next_tag(fdt, offset, &nextoffset);
100 
101 		switch (tag) {
102 		case FDT_END:
103 			if (nextoffset >= 0)
104 				return -FDT_ERR_BADSTRUCTURE;
105 			else
106 				return nextoffset;
107 
108 		case FDT_PROP:
109 			return offset;
110 		}
111 		offset = nextoffset;
112 	} while (tag == FDT_NOP);
113 
114 	return -FDT_ERR_NOTFOUND;
115 }
116 
fdt_subnode_offset_namelen(const void * fdt,int offset,const char * name,int namelen)117 int fdt_subnode_offset_namelen(const void *fdt, int offset,
118 			       const char *name, int namelen)
119 {
120 	int depth;
121 
122 	FDT_CHECK_HEADER(fdt);
123 
124 	for (depth = 0;
125 	     (offset >= 0) && (depth >= 0);
126 	     offset = fdt_next_node(fdt, offset, &depth))
127 		if ((depth == 1)
128 		    && _fdt_nodename_eq(fdt, offset, name, namelen))
129 			return offset;
130 
131 	if (depth < 0)
132 		return -FDT_ERR_NOTFOUND;
133 	return offset; /* error */
134 }
135 
fdt_subnode_offset(const void * fdt,int parentoffset,const char * name)136 int fdt_subnode_offset(const void *fdt, int parentoffset,
137 		       const char *name)
138 {
139 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
140 }
141 
142 /*
143  * Find the next of path separator, note we need to search for both '/' and ':'
144  * and then take the first one so that we do the right thing for e.g.
145  * "foo/bar:option" and "bar:option/otheroption", both of which happen, so
146  * first searching for either ':' or '/' does not work.
147  */
fdt_path_next_separator(const char * path,int len)148 static const char *fdt_path_next_separator(const char *path, int len)
149 {
150 	const void *sep1 = memchr(path, '/', len);
151 	const void *sep2 = memchr(path, ':', len);
152 
153 	if (sep1 && sep2)
154 		return (sep1 < sep2) ? sep1 : sep2;
155 	else if (sep1)
156 		return sep1;
157 	else
158 		return sep2;
159 }
160 
fdt_path_offset_namelen(const void * fdt,const char * path,int namelen)161 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
162 {
163 	const char *end = path + namelen;
164 	const char *p = path;
165 	int offset = 0;
166 
167 	FDT_CHECK_HEADER(fdt);
168 
169 	/* see if we have an alias */
170 	if (*path != '/') {
171 		const char *q = fdt_path_next_separator(path, namelen);
172 
173 		if (!q)
174 			q = end;
175 
176 		p = fdt_get_alias_namelen(fdt, p, q - p);
177 		if (!p)
178 			return -FDT_ERR_BADPATH;
179 		offset = fdt_path_offset(fdt, p);
180 
181 		p = q;
182 	}
183 
184 	while (*p && (p < end)) {
185 		const char *q;
186 
187 		while (*p == '/')
188 			p++;
189 
190 		if (*p == '\0' || *p == ':')
191 			return offset;
192 
193 		q = fdt_path_next_separator(p, end - p);
194 		if (!q)
195 			q = end;
196 
197 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
198 		if (offset < 0)
199 			return offset;
200 
201 		p = q;
202 	}
203 
204 	return offset;
205 }
206 
fdt_path_offset(const void * fdt,const char * path)207 int fdt_path_offset(const void *fdt, const char *path)
208 {
209 	return fdt_path_offset_namelen(fdt, path, strlen(path));
210 }
211 
fdt_get_name(const void * fdt,int nodeoffset,int * len)212 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
213 {
214 	const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
215 	int err;
216 
217 	if (((err = fdt_check_header(fdt)) != 0)
218 	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
219 			goto fail;
220 
221 	if (len)
222 		*len = strlen(nh->name);
223 
224 	return nh->name;
225 
226  fail:
227 	if (len)
228 		*len = err;
229 	return NULL;
230 }
231 
fdt_first_property_offset(const void * fdt,int nodeoffset)232 int fdt_first_property_offset(const void *fdt, int nodeoffset)
233 {
234 	int offset;
235 
236 	if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
237 		return offset;
238 
239 	return _nextprop(fdt, offset);
240 }
241 
fdt_next_property_offset(const void * fdt,int offset)242 int fdt_next_property_offset(const void *fdt, int offset)
243 {
244 	if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
245 		return offset;
246 
247 	return _nextprop(fdt, offset);
248 }
249 
fdt_get_property_by_offset(const void * fdt,int offset,int * lenp)250 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
251 						      int offset,
252 						      int *lenp)
253 {
254 	int err;
255 	const struct fdt_property *prop;
256 
257 	if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
258 		if (lenp)
259 			*lenp = err;
260 		return NULL;
261 	}
262 
263 	prop = fdt_offset_ptr_(fdt, offset);
264 
265 	if (lenp)
266 		*lenp = fdt32_to_cpu(prop->len);
267 
268 	return prop;
269 }
270 
fdt_get_property_namelen(const void * fdt,int offset,const char * name,int namelen,int * lenp)271 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
272 						    int offset,
273 						    const char *name,
274 						    int namelen, int *lenp)
275 {
276 	for (offset = fdt_first_property_offset(fdt, offset);
277 	     (offset >= 0);
278 	     (offset = fdt_next_property_offset(fdt, offset))) {
279 		const struct fdt_property *prop;
280 
281 		if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
282 			offset = -FDT_ERR_INTERNAL;
283 			break;
284 		}
285 		if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
286 				   name, namelen))
287 			return prop;
288 	}
289 
290 	if (lenp)
291 		*lenp = offset;
292 	return NULL;
293 }
294 
fdt_get_property(const void * fdt,int nodeoffset,const char * name,int * lenp)295 const struct fdt_property *fdt_get_property(const void *fdt,
296 					    int nodeoffset,
297 					    const char *name, int *lenp)
298 {
299 	return fdt_get_property_namelen(fdt, nodeoffset, name,
300 					strlen(name), lenp);
301 }
302 
fdt_getprop_namelen(const void * fdt,int nodeoffset,const char * name,int namelen,int * lenp)303 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
304 				const char *name, int namelen, int *lenp)
305 {
306 	const struct fdt_property *prop;
307 
308 	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
309 	if (!prop)
310 		return NULL;
311 
312 	return prop->data;
313 }
314 
fdt_getprop_by_offset(const void * fdt,int offset,const char ** namep,int * lenp)315 const void *fdt_getprop_by_offset(const void *fdt, int offset,
316 				  const char **namep, int *lenp)
317 {
318 	const struct fdt_property *prop;
319 
320 	prop = fdt_get_property_by_offset(fdt, offset, lenp);
321 	if (!prop)
322 		return NULL;
323 	if (namep)
324 		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
325 	return prop->data;
326 }
327 
fdt_getprop(const void * fdt,int nodeoffset,const char * name,int * lenp)328 const void *fdt_getprop(const void *fdt, int nodeoffset,
329 			const char *name, int *lenp)
330 {
331 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
332 }
333 
fdt_get_phandle(const void * fdt,int nodeoffset)334 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
335 {
336 	const fdt32_t *php;
337 	int len;
338 
339 	/* FIXME: This is a bit sub-optimal, since we potentially scan
340 	 * over all the properties twice. */
341 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
342 	if (!php || (len != sizeof(*php))) {
343 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
344 		if (!php || (len != sizeof(*php)))
345 			return 0;
346 	}
347 
348 	return fdt32_to_cpu(*php);
349 }
350 
fdt_get_alias_namelen(const void * fdt,const char * name,int namelen)351 const char *fdt_get_alias_namelen(const void *fdt,
352 				  const char *name, int namelen)
353 {
354 	int aliasoffset;
355 
356 	aliasoffset = fdt_path_offset(fdt, "/aliases");
357 	if (aliasoffset < 0)
358 		return NULL;
359 
360 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
361 }
362 
fdt_get_alias(const void * fdt,const char * name)363 const char *fdt_get_alias(const void *fdt, const char *name)
364 {
365 	return fdt_get_alias_namelen(fdt, name, strlen(name));
366 }
367 
fdt_get_path(const void * fdt,int nodeoffset,char * buf,int buflen)368 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
369 {
370 	int pdepth = 0, p = 0;
371 	int offset, depth, namelen;
372 	const char *name;
373 
374 	FDT_CHECK_HEADER(fdt);
375 
376 	if (buflen < 2)
377 		return -FDT_ERR_NOSPACE;
378 
379 	for (offset = 0, depth = 0;
380 	     (offset >= 0) && (offset <= nodeoffset);
381 	     offset = fdt_next_node(fdt, offset, &depth)) {
382 		while (pdepth > depth) {
383 			do {
384 				p--;
385 			} while (buf[p-1] != '/');
386 			pdepth--;
387 		}
388 
389 		if (pdepth >= depth) {
390 			name = fdt_get_name(fdt, offset, &namelen);
391 			if (!name)
392 				return namelen;
393 			if ((p + namelen + 1) <= buflen) {
394 				memcpy(buf + p, name, namelen);
395 				p += namelen;
396 				buf[p++] = '/';
397 				pdepth++;
398 			}
399 		}
400 
401 		if (offset == nodeoffset) {
402 			if (pdepth < (depth + 1))
403 				return -FDT_ERR_NOSPACE;
404 
405 			if (p > 1) /* special case so that root path is "/", not "" */
406 				p--;
407 			buf[p] = '\0';
408 			return 0;
409 		}
410 	}
411 
412 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
413 		return -FDT_ERR_BADOFFSET;
414 	else if (offset == -FDT_ERR_BADOFFSET)
415 		return -FDT_ERR_BADSTRUCTURE;
416 
417 	return offset; /* error from fdt_next_node() */
418 }
419 
fdt_supernode_atdepth_offset(const void * fdt,int nodeoffset,int supernodedepth,int * nodedepth)420 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
421 				 int supernodedepth, int *nodedepth)
422 {
423 	int offset, depth;
424 	int supernodeoffset = -FDT_ERR_INTERNAL;
425 
426 	FDT_CHECK_HEADER(fdt);
427 
428 	if (supernodedepth < 0)
429 		return -FDT_ERR_NOTFOUND;
430 
431 	for (offset = 0, depth = 0;
432 	     (offset >= 0) && (offset <= nodeoffset);
433 	     offset = fdt_next_node(fdt, offset, &depth)) {
434 		if (depth == supernodedepth)
435 			supernodeoffset = offset;
436 
437 		if (offset == nodeoffset) {
438 			if (nodedepth)
439 				*nodedepth = depth;
440 
441 			if (supernodedepth > depth)
442 				return -FDT_ERR_NOTFOUND;
443 			else
444 				return supernodeoffset;
445 		}
446 	}
447 
448 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
449 		return -FDT_ERR_BADOFFSET;
450 	else if (offset == -FDT_ERR_BADOFFSET)
451 		return -FDT_ERR_BADSTRUCTURE;
452 
453 	return offset; /* error from fdt_next_node() */
454 }
455 
fdt_node_depth(const void * fdt,int nodeoffset)456 int fdt_node_depth(const void *fdt, int nodeoffset)
457 {
458 	int nodedepth;
459 	int err;
460 
461 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
462 	if (err)
463 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
464 	return nodedepth;
465 }
466 
fdt_parent_offset(const void * fdt,int nodeoffset)467 int fdt_parent_offset(const void *fdt, int nodeoffset)
468 {
469 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
470 
471 	if (nodedepth < 0)
472 		return nodedepth;
473 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
474 					    nodedepth - 1, NULL);
475 }
476 
fdt_node_offset_by_prop_value(const void * fdt,int startoffset,const char * propname,const void * propval,int proplen)477 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
478 				  const char *propname,
479 				  const void *propval, int proplen)
480 {
481 	int offset;
482 	const void *val;
483 	int len;
484 
485 	FDT_CHECK_HEADER(fdt);
486 
487 	/* FIXME: The algorithm here is pretty horrible: we scan each
488 	 * property of a node in fdt_getprop(), then if that didn't
489 	 * find what we want, we scan over them again making our way
490 	 * to the next node.  Still it's the easiest to implement
491 	 * approach; performance can come later. */
492 	for (offset = fdt_next_node(fdt, startoffset, NULL);
493 	     offset >= 0;
494 	     offset = fdt_next_node(fdt, offset, NULL)) {
495 		val = fdt_getprop(fdt, offset, propname, &len);
496 		if (val && (len == proplen)
497 		    && (memcmp(val, propval, len) == 0))
498 			return offset;
499 	}
500 
501 	return offset; /* error from fdt_next_node() */
502 }
503 
fdt_node_offset_by_phandle(const void * fdt,uint32_t phandle)504 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
505 {
506 	int offset;
507 
508 	if ((phandle == 0) || (phandle == -1))
509 		return -FDT_ERR_BADPHANDLE;
510 
511 	FDT_CHECK_HEADER(fdt);
512 
513 	/* FIXME: The algorithm here is pretty horrible: we
514 	 * potentially scan each property of a node in
515 	 * fdt_get_phandle(), then if that didn't find what
516 	 * we want, we scan over them again making our way to the next
517 	 * node.  Still it's the easiest to implement approach;
518 	 * performance can come later. */
519 	for (offset = fdt_next_node(fdt, -1, NULL);
520 	     offset >= 0;
521 	     offset = fdt_next_node(fdt, offset, NULL)) {
522 		if (fdt_get_phandle(fdt, offset) == phandle)
523 			return offset;
524 	}
525 
526 	return offset; /* error from fdt_next_node() */
527 }
528 
fdt_stringlist_contains(const char * strlist,int listlen,const char * str)529 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
530 {
531 	int len = strlen(str);
532 	const char *p;
533 
534 	while (listlen >= len) {
535 		if (memcmp(str, strlist, len+1) == 0)
536 			return 1;
537 		p = memchr(strlist, '\0', listlen);
538 		if (!p)
539 			return 0; /* malformed strlist.. */
540 		listlen -= (p-strlist) + 1;
541 		strlist = p + 1;
542 	}
543 	return 0;
544 }
545 
fdt_stringlist_count(const void * fdt,int nodeoffset,const char * property)546 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
547 {
548 	const char *list, *end;
549 	int length, count = 0;
550 
551 	list = fdt_getprop(fdt, nodeoffset, property, &length);
552 	if (!list)
553 		return length;
554 
555 	end = list + length;
556 
557 	while (list < end) {
558 		length = strnlen(list, end - list) + 1;
559 
560 		/* Abort if the last string isn't properly NUL-terminated. */
561 		if (list + length > end)
562 			return -FDT_ERR_BADVALUE;
563 
564 		list += length;
565 		count++;
566 	}
567 
568 	return count;
569 }
570 
fdt_stringlist_search(const void * fdt,int nodeoffset,const char * property,const char * string)571 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
572 			  const char *string)
573 {
574 	int length, len, idx = 0;
575 	const char *list, *end;
576 
577 	list = fdt_getprop(fdt, nodeoffset, property, &length);
578 	if (!list)
579 		return length;
580 
581 	len = strlen(string) + 1;
582 	end = list + length;
583 
584 	while (list < end) {
585 		length = strnlen(list, end - list) + 1;
586 
587 		/* Abort if the last string isn't properly NUL-terminated. */
588 		if (list + length > end)
589 			return -FDT_ERR_BADVALUE;
590 
591 		if (length == len && memcmp(list, string, length) == 0)
592 			return idx;
593 
594 		list += length;
595 		idx++;
596 	}
597 
598 	return -FDT_ERR_NOTFOUND;
599 }
600 
fdt_stringlist_get(const void * fdt,int nodeoffset,const char * property,int idx,int * lenp)601 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
602 			       const char *property, int idx,
603 			       int *lenp)
604 {
605 	const char *list, *end;
606 	int length;
607 
608 	list = fdt_getprop(fdt, nodeoffset, property, &length);
609 	if (!list) {
610 		if (lenp)
611 			*lenp = length;
612 
613 		return NULL;
614 	}
615 
616 	end = list + length;
617 
618 	while (list < end) {
619 		length = strnlen(list, end - list) + 1;
620 
621 		/* Abort if the last string isn't properly NUL-terminated. */
622 		if (list + length > end) {
623 			if (lenp)
624 				*lenp = -FDT_ERR_BADVALUE;
625 
626 			return NULL;
627 		}
628 
629 		if (idx == 0) {
630 			if (lenp)
631 				*lenp = length - 1;
632 
633 			return list;
634 		}
635 
636 		list += length;
637 		idx--;
638 	}
639 
640 	if (lenp)
641 		*lenp = -FDT_ERR_NOTFOUND;
642 
643 	return NULL;
644 }
645 
fdt_node_check_compatible(const void * fdt,int nodeoffset,const char * compatible)646 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
647 			      const char *compatible)
648 {
649 	const void *prop;
650 	int len;
651 
652 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
653 	if (!prop)
654 		return len;
655 
656 	return !fdt_stringlist_contains(prop, len, compatible);
657 }
658 
fdt_node_offset_by_compatible(const void * fdt,int startoffset,const char * compatible)659 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
660 				  const char *compatible)
661 {
662 	int offset, err;
663 
664 	FDT_CHECK_HEADER(fdt);
665 
666 	/* FIXME: The algorithm here is pretty horrible: we scan each
667 	 * property of a node in fdt_node_check_compatible(), then if
668 	 * that didn't find what we want, we scan over them again
669 	 * making our way to the next node.  Still it's the easiest to
670 	 * implement approach; performance can come later. */
671 	for (offset = fdt_next_node(fdt, startoffset, NULL);
672 	     offset >= 0;
673 	     offset = fdt_next_node(fdt, offset, NULL)) {
674 		err = fdt_node_check_compatible(fdt, offset, compatible);
675 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
676 			return err;
677 		else if (err == 0)
678 			return offset;
679 	}
680 
681 	return offset; /* error from fdt_next_node() */
682 }
683