xref: /openbmc/u-boot/lib/libfdt/fdt_ro.c (revision 68968901)
1 /*
2  * libfdt - Flat Device Tree manipulation
3  * Copyright (C) 2006 David Gibson, IBM Corporation.
4  * SPDX-License-Identifier:	GPL-2.0+ BSD-2-Clause
5  */
6 #include "libfdt_env.h"
7 
8 #ifndef USE_HOSTCC
9 #include <fdt.h>
10 #include <libfdt.h>
11 #else
12 #include "fdt_host.h"
13 #endif
14 
15 #include "libfdt_internal.h"
16 
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 
37 const char *fdt_string(const void *fdt, int stroffset)
38 {
39 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
40 }
41 
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 
50 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
51 {
52 	FDT_CHECK_HEADER(fdt);
53 	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
54 	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
55 	return 0;
56 }
57 
58 int fdt_num_mem_rsv(const void *fdt)
59 {
60 	int i = 0;
61 
62 	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
63 		i++;
64 	return i;
65 }
66 
67 static int _nextprop(const void *fdt, int offset)
68 {
69 	uint32_t tag;
70 	int nextoffset;
71 
72 	do {
73 		tag = fdt_next_tag(fdt, offset, &nextoffset);
74 
75 		switch (tag) {
76 		case FDT_END:
77 			if (nextoffset >= 0)
78 				return -FDT_ERR_BADSTRUCTURE;
79 			else
80 				return nextoffset;
81 
82 		case FDT_PROP:
83 			return offset;
84 		}
85 		offset = nextoffset;
86 	} while (tag == FDT_NOP);
87 
88 	return -FDT_ERR_NOTFOUND;
89 }
90 
91 int fdt_subnode_offset_namelen(const void *fdt, int offset,
92 			       const char *name, int namelen)
93 {
94 	int depth;
95 
96 	FDT_CHECK_HEADER(fdt);
97 
98 	for (depth = 0;
99 	     (offset >= 0) && (depth >= 0);
100 	     offset = fdt_next_node(fdt, offset, &depth))
101 		if ((depth == 1)
102 		    && _fdt_nodename_eq(fdt, offset, name, namelen))
103 			return offset;
104 
105 	if (depth < 0)
106 		return -FDT_ERR_NOTFOUND;
107 	return offset; /* error */
108 }
109 
110 int fdt_subnode_offset(const void *fdt, int parentoffset,
111 		       const char *name)
112 {
113 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
114 }
115 
116 int fdt_path_offset(const void *fdt, const char *path)
117 {
118 	const char *end = path + strlen(path);
119 	const char *p = path;
120 	int offset = 0;
121 
122 	FDT_CHECK_HEADER(fdt);
123 
124 	/* see if we have an alias */
125 	if (*path != '/') {
126 		const char *q = strchr(path, '/');
127 
128 		if (!q)
129 			q = end;
130 
131 		p = fdt_get_alias_namelen(fdt, p, q - p);
132 		if (!p)
133 			return -FDT_ERR_BADPATH;
134 		offset = fdt_path_offset(fdt, p);
135 
136 		p = q;
137 	}
138 
139 	while (*p) {
140 		const char *q;
141 
142 		while (*p == '/')
143 			p++;
144 		if (! *p)
145 			return offset;
146 		q = strchr(p, '/');
147 		if (! q)
148 			q = end;
149 
150 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
151 		if (offset < 0)
152 			return offset;
153 
154 		p = q;
155 	}
156 
157 	return offset;
158 }
159 
160 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
161 {
162 	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
163 	int err;
164 
165 	if (((err = fdt_check_header(fdt)) != 0)
166 	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
167 			goto fail;
168 
169 	if (len)
170 		*len = strlen(nh->name);
171 
172 	return nh->name;
173 
174  fail:
175 	if (len)
176 		*len = err;
177 	return NULL;
178 }
179 
180 int fdt_first_property_offset(const void *fdt, int nodeoffset)
181 {
182 	int offset;
183 
184 	if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
185 		return offset;
186 
187 	return _nextprop(fdt, offset);
188 }
189 
190 int fdt_next_property_offset(const void *fdt, int offset)
191 {
192 	if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
193 		return offset;
194 
195 	return _nextprop(fdt, offset);
196 }
197 
198 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
199 						      int offset,
200 						      int *lenp)
201 {
202 	int err;
203 	const struct fdt_property *prop;
204 
205 	if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
206 		if (lenp)
207 			*lenp = err;
208 		return NULL;
209 	}
210 
211 	prop = _fdt_offset_ptr(fdt, offset);
212 
213 	if (lenp)
214 		*lenp = fdt32_to_cpu(prop->len);
215 
216 	return prop;
217 }
218 
219 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
220 						    int offset,
221 						    const char *name,
222 						    int namelen, int *lenp)
223 {
224 	for (offset = fdt_first_property_offset(fdt, offset);
225 	     (offset >= 0);
226 	     (offset = fdt_next_property_offset(fdt, offset))) {
227 		const struct fdt_property *prop;
228 
229 		if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
230 			offset = -FDT_ERR_INTERNAL;
231 			break;
232 		}
233 		if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
234 				   name, namelen))
235 			return prop;
236 	}
237 
238 	if (lenp)
239 		*lenp = offset;
240 	return NULL;
241 }
242 
243 const struct fdt_property *fdt_get_property(const void *fdt,
244 					    int nodeoffset,
245 					    const char *name, int *lenp)
246 {
247 	return fdt_get_property_namelen(fdt, nodeoffset, name,
248 					strlen(name), lenp);
249 }
250 
251 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
252 				const char *name, int namelen, int *lenp)
253 {
254 	const struct fdt_property *prop;
255 
256 	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
257 	if (! prop)
258 		return NULL;
259 
260 	return prop->data;
261 }
262 
263 const void *fdt_getprop_by_offset(const void *fdt, int offset,
264 				  const char **namep, int *lenp)
265 {
266 	const struct fdt_property *prop;
267 
268 	prop = fdt_get_property_by_offset(fdt, offset, lenp);
269 	if (!prop)
270 		return NULL;
271 	if (namep)
272 		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
273 	return prop->data;
274 }
275 
276 const void *fdt_getprop(const void *fdt, int nodeoffset,
277 			const char *name, int *lenp)
278 {
279 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
280 }
281 
282 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
283 {
284 	const fdt32_t *php;
285 	int len;
286 
287 	/* FIXME: This is a bit sub-optimal, since we potentially scan
288 	 * over all the properties twice. */
289 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
290 	if (!php || (len != sizeof(*php))) {
291 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
292 		if (!php || (len != sizeof(*php)))
293 			return 0;
294 	}
295 
296 	return fdt32_to_cpu(*php);
297 }
298 
299 const char *fdt_get_alias_namelen(const void *fdt,
300 				  const char *name, int namelen)
301 {
302 	int aliasoffset;
303 
304 	aliasoffset = fdt_path_offset(fdt, "/aliases");
305 	if (aliasoffset < 0)
306 		return NULL;
307 
308 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
309 }
310 
311 const char *fdt_get_alias(const void *fdt, const char *name)
312 {
313 	return fdt_get_alias_namelen(fdt, name, strlen(name));
314 }
315 
316 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
317 {
318 	int pdepth = 0, p = 0;
319 	int offset, depth, namelen;
320 	const char *name;
321 
322 	FDT_CHECK_HEADER(fdt);
323 
324 	if (buflen < 2)
325 		return -FDT_ERR_NOSPACE;
326 
327 	for (offset = 0, depth = 0;
328 	     (offset >= 0) && (offset <= nodeoffset);
329 	     offset = fdt_next_node(fdt, offset, &depth)) {
330 		while (pdepth > depth) {
331 			do {
332 				p--;
333 			} while (buf[p-1] != '/');
334 			pdepth--;
335 		}
336 
337 		if (pdepth >= depth) {
338 			name = fdt_get_name(fdt, offset, &namelen);
339 			if (!name)
340 				return namelen;
341 			if ((p + namelen + 1) <= buflen) {
342 				memcpy(buf + p, name, namelen);
343 				p += namelen;
344 				buf[p++] = '/';
345 				pdepth++;
346 			}
347 		}
348 
349 		if (offset == nodeoffset) {
350 			if (pdepth < (depth + 1))
351 				return -FDT_ERR_NOSPACE;
352 
353 			if (p > 1) /* special case so that root path is "/", not "" */
354 				p--;
355 			buf[p] = '\0';
356 			return 0;
357 		}
358 	}
359 
360 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
361 		return -FDT_ERR_BADOFFSET;
362 	else if (offset == -FDT_ERR_BADOFFSET)
363 		return -FDT_ERR_BADSTRUCTURE;
364 
365 	return offset; /* error from fdt_next_node() */
366 }
367 
368 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
369 				 int supernodedepth, int *nodedepth)
370 {
371 	int offset, depth;
372 	int supernodeoffset = -FDT_ERR_INTERNAL;
373 
374 	FDT_CHECK_HEADER(fdt);
375 
376 	if (supernodedepth < 0)
377 		return -FDT_ERR_NOTFOUND;
378 
379 	for (offset = 0, depth = 0;
380 	     (offset >= 0) && (offset <= nodeoffset);
381 	     offset = fdt_next_node(fdt, offset, &depth)) {
382 		if (depth == supernodedepth)
383 			supernodeoffset = offset;
384 
385 		if (offset == nodeoffset) {
386 			if (nodedepth)
387 				*nodedepth = depth;
388 
389 			if (supernodedepth > depth)
390 				return -FDT_ERR_NOTFOUND;
391 			else
392 				return supernodeoffset;
393 		}
394 	}
395 
396 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
397 		return -FDT_ERR_BADOFFSET;
398 	else if (offset == -FDT_ERR_BADOFFSET)
399 		return -FDT_ERR_BADSTRUCTURE;
400 
401 	return offset; /* error from fdt_next_node() */
402 }
403 
404 int fdt_node_depth(const void *fdt, int nodeoffset)
405 {
406 	int nodedepth;
407 	int err;
408 
409 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
410 	if (err)
411 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
412 	return nodedepth;
413 }
414 
415 int fdt_parent_offset(const void *fdt, int nodeoffset)
416 {
417 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
418 
419 	if (nodedepth < 0)
420 		return nodedepth;
421 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
422 					    nodedepth - 1, NULL);
423 }
424 
425 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
426 				  const char *propname,
427 				  const void *propval, int proplen)
428 {
429 	int offset;
430 	const void *val;
431 	int len;
432 
433 	FDT_CHECK_HEADER(fdt);
434 
435 	/* FIXME: The algorithm here is pretty horrible: we scan each
436 	 * property of a node in fdt_getprop(), then if that didn't
437 	 * find what we want, we scan over them again making our way
438 	 * to the next node.  Still it's the easiest to implement
439 	 * approach; performance can come later. */
440 	for (offset = fdt_next_node(fdt, startoffset, NULL);
441 	     offset >= 0;
442 	     offset = fdt_next_node(fdt, offset, NULL)) {
443 		val = fdt_getprop(fdt, offset, propname, &len);
444 		if (val && (len == proplen)
445 		    && (memcmp(val, propval, len) == 0))
446 			return offset;
447 	}
448 
449 	return offset; /* error from fdt_next_node() */
450 }
451 
452 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
453 {
454 	int offset;
455 
456 	if ((phandle == 0) || (phandle == -1))
457 		return -FDT_ERR_BADPHANDLE;
458 
459 	FDT_CHECK_HEADER(fdt);
460 
461 	/* FIXME: The algorithm here is pretty horrible: we
462 	 * potentially scan each property of a node in
463 	 * fdt_get_phandle(), then if that didn't find what
464 	 * we want, we scan over them again making our way to the next
465 	 * node.  Still it's the easiest to implement approach;
466 	 * performance can come later. */
467 	for (offset = fdt_next_node(fdt, -1, NULL);
468 	     offset >= 0;
469 	     offset = fdt_next_node(fdt, offset, NULL)) {
470 		if (fdt_get_phandle(fdt, offset) == phandle)
471 			return offset;
472 	}
473 
474 	return offset; /* error from fdt_next_node() */
475 }
476 
477 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
478 {
479 	int len = strlen(str);
480 	const char *p;
481 
482 	while (listlen >= len) {
483 		if (memcmp(str, strlist, len+1) == 0)
484 			return 1;
485 		p = memchr(strlist, '\0', listlen);
486 		if (!p)
487 			return 0; /* malformed strlist.. */
488 		listlen -= (p-strlist) + 1;
489 		strlist = p + 1;
490 	}
491 	return 0;
492 }
493 
494 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
495 			      const char *compatible)
496 {
497 	const void *prop;
498 	int len;
499 
500 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
501 	if (!prop)
502 		return len;
503 	if (fdt_stringlist_contains(prop, len, compatible))
504 		return 0;
505 	else
506 		return 1;
507 }
508 
509 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
510 				  const char *compatible)
511 {
512 	int offset, err;
513 
514 	FDT_CHECK_HEADER(fdt);
515 
516 	/* FIXME: The algorithm here is pretty horrible: we scan each
517 	 * property of a node in fdt_node_check_compatible(), then if
518 	 * that didn't find what we want, we scan over them again
519 	 * making our way to the next node.  Still it's the easiest to
520 	 * implement approach; performance can come later. */
521 	for (offset = fdt_next_node(fdt, startoffset, NULL);
522 	     offset >= 0;
523 	     offset = fdt_next_node(fdt, offset, NULL)) {
524 		err = fdt_node_check_compatible(fdt, offset, compatible);
525 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
526 			return err;
527 		else if (err == 0)
528 			return offset;
529 	}
530 
531 	return offset; /* error from fdt_next_node() */
532 }
533