xref: /openbsd/sys/dev/ofw/fdt.c (revision 3fe5dae2)
1 /*	$OpenBSD: fdt.c,v 1.35 2024/03/27 23:05:27 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
5  * Copyright (c) 2009 Mark Kettenis <kettenis@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/systm.h>
22 #include <sys/malloc.h>
23 
24 #include <dev/ofw/fdt.h>
25 #include <dev/ofw/openfirm.h>
26 
27 unsigned int fdt_check_head(void *);
28 char	*fdt_get_str(u_int32_t);
29 void	*skip_property(u_int32_t *);
30 void	*skip_props(u_int32_t *);
31 void	*skip_node_name(u_int32_t *);
32 void	*skip_node(void *);
33 void	*skip_nops(u_int32_t *);
34 void	*fdt_parent_node_recurse(void *, void *);
35 void	*fdt_find_phandle_recurse(void *, uint32_t);
36 int	 fdt_node_property_int(void *, char *, int *);
37 int	 fdt_node_property_ints(void *, char *, int *, int);
38 int	 fdt_translate_reg(void *, struct fdt_reg *);
39 #ifdef DEBUG
40 void 	 fdt_print_node_recurse(void *, int);
41 #endif
42 
43 static int tree_inited = 0;
44 static struct fdt tree;
45 
46 unsigned int
fdt_check_head(void * fdt)47 fdt_check_head(void *fdt)
48 {
49 	struct fdt_head *fh;
50 	u_int32_t *ptr, *tok;
51 
52 	fh = fdt;
53 	ptr = (u_int32_t *)fdt;
54 
55 	if (betoh32(fh->fh_magic) != FDT_MAGIC)
56 		return 0;
57 
58 	if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
59 		return 0;
60 
61 	tok = skip_nops(ptr + (betoh32(fh->fh_struct_off) / 4));
62 	if (betoh32(*tok) != FDT_NODE_BEGIN)
63 		return 0;
64 
65 	/* check for end signature on version 17 blob */
66 	if ((betoh32(fh->fh_version) >= 17) &&
67 	    (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
68 	    (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
69 		return 0;
70 
71 	return betoh32(fh->fh_version);
72 }
73 
74 /*
75  * Initializes internal structures of module.
76  * Has to be called once, preferably in machdep.c.
77  */
78 int
fdt_init(void * fdt)79 fdt_init(void *fdt)
80 {
81 	int version;
82 
83 	bzero(&tree, sizeof(struct fdt));
84 	tree_inited = 0;
85 
86 	if (!fdt)
87 		return 0;
88 
89 	if (!(version = fdt_check_head(fdt)))
90 		return 0;
91 
92 	tree.header = (struct fdt_head *)fdt;
93 	tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off);
94 	tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
95 	tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
96 	tree.end = (char *)fdt + betoh32(tree.header->fh_size);
97 	tree.version = version;
98 	tree.strings_size = betoh32(tree.header->fh_strings_size);
99 	if (tree.version >= 17)
100 		tree.struct_size = betoh32(tree.header->fh_struct_size);
101 	tree_inited = 1;
102 
103 	return version;
104 }
105 
106 void
fdt_finalize(void)107 fdt_finalize(void)
108 {
109 	char *start = (char *)tree.header;
110 
111 	tree.header->fh_size = htobe32(tree.end - start);
112 	tree.header->fh_struct_off = htobe32(tree.tree - start);
113 	tree.header->fh_strings_off = htobe32(tree.strings - start);
114 	tree.header->fh_reserve_off = htobe32(tree.memory - start);
115 	tree.header->fh_strings_size = htobe32(tree.strings_size);
116 	if (tree.version >= 17)
117 		tree.header->fh_struct_size = htobe32(tree.struct_size);
118 }
119 
120 /*
121  * Return the size of the FDT.
122  */
123 size_t
fdt_get_size(void * fdt)124 fdt_get_size(void *fdt)
125 {
126 	if (!fdt)
127 		return 0;
128 
129 	if (!fdt_check_head(fdt))
130 		return 0;
131 
132 	return betoh32(((struct fdt_head *)fdt)->fh_size);
133 }
134 
135 /*
136  * Retrieve string pointer from strings table.
137  */
138 char *
fdt_get_str(u_int32_t num)139 fdt_get_str(u_int32_t num)
140 {
141 	if (num > tree.strings_size)
142 		return NULL;
143 	return (tree.strings) ? (tree.strings + num) : NULL;
144 }
145 
146 int
fdt_add_str(char * name)147 fdt_add_str(char *name)
148 {
149 	size_t len = roundup(strlen(name) + 1, sizeof(uint32_t));
150 	char *end = tree.strings + tree.strings_size;
151 
152 	memmove(end + len, end, tree.end - end);
153 	tree.strings_size += len;
154 	if (tree.tree > tree.strings)
155 		tree.tree += len;
156 	if (tree.memory > tree.strings)
157 		tree.memory += len;
158 	tree.end += len;
159 	memset(end, 0, len);
160 	memcpy(end, name, strlen(name));
161 
162 	return (end - tree.strings);
163 }
164 
165 /*
166  * Utility functions for skipping parts of tree.
167  */
168 
169 void *
skip_nops(u_int32_t * ptr)170 skip_nops(u_int32_t *ptr)
171 {
172 	while (betoh32(*ptr) == FDT_NOP)
173 		ptr++;
174 
175 	return ptr;
176 }
177 
178 void *
skip_property(u_int32_t * ptr)179 skip_property(u_int32_t *ptr)
180 {
181 	u_int32_t size;
182 
183 	size = betoh32(*(ptr + 1));
184 	/* move forward by magic + size + nameid + rounded up property size */
185 	ptr += 3 + roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
186 
187 	return skip_nops(ptr);
188 }
189 
190 void *
skip_props(u_int32_t * ptr)191 skip_props(u_int32_t *ptr)
192 {
193 	while (betoh32(*ptr) == FDT_PROPERTY) {
194 		ptr = skip_property(ptr);
195 	}
196 	return ptr;
197 }
198 
199 void *
skip_node_name(u_int32_t * ptr)200 skip_node_name(u_int32_t *ptr)
201 {
202 	/* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
203 	ptr += roundup(strlen((char *)ptr) + 1,
204 	    sizeof(u_int32_t)) / sizeof(u_int32_t);
205 
206 	return skip_nops(ptr);
207 }
208 
209 /*
210  * Retrieves node property, the returned pointer is inside the fdt tree,
211  * so we should not modify content pointed by it directly.
212  */
213 int
fdt_node_property(void * node,char * name,char ** out)214 fdt_node_property(void *node, char *name, char **out)
215 {
216 	u_int32_t *ptr;
217 	u_int32_t nameid;
218 	char *tmp;
219 
220 	if (!tree_inited)
221 		return -1;
222 
223 	ptr = (u_int32_t *)node;
224 
225 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
226 		return -1;
227 
228 	ptr = skip_node_name(ptr + 1);
229 
230 	while (betoh32(*ptr) == FDT_PROPERTY) {
231 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
232 		tmp = fdt_get_str(nameid);
233 		if (!strcmp(name, tmp)) {
234 			*out = (char *)(ptr + 3); /* beginning of the value */
235 			return betoh32(*(ptr + 1)); /* size of value */
236 		}
237 		ptr = skip_property(ptr);
238 	}
239 	return -1;
240 }
241 
242 int
fdt_node_set_property(void * node,char * name,void * data,int len)243 fdt_node_set_property(void *node, char *name, void *data, int len)
244 {
245 	uint32_t *ptr, *next;
246 	uint32_t nameid;
247 	uint32_t curlen;
248 	size_t delta;
249 	char *tmp;
250 
251 	if (!tree_inited)
252 		return 0;
253 
254 	ptr = (uint32_t *)node;
255 
256 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
257 		return 0;
258 
259 	ptr = skip_node_name(ptr + 1);
260 
261 	while (betoh32(*ptr) == FDT_PROPERTY) {
262 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
263 		tmp = fdt_get_str(nameid);
264 		next = skip_property(ptr);
265 		if (!strcmp(name, tmp)) {
266 			curlen = betoh32(*(ptr + 1));
267 			delta = roundup(len, sizeof(uint32_t)) -
268 			    roundup(curlen, sizeof(uint32_t));
269 			memmove((char *)next + delta, next,
270 			    tree.end - (char *)next);
271 			tree.struct_size += delta;
272 			if (tree.strings > tree.tree)
273 				tree.strings += delta;
274 			if (tree.memory > tree.tree)
275 				tree.memory += delta;
276 			tree.end += delta;
277 			*(ptr + 1) = htobe32(len);
278 			memcpy(ptr + 3, data, len);
279 			return 1;
280 		}
281 		ptr = next;
282 	}
283 	return 0;
284 }
285 
286 int
fdt_node_add_property(void * node,char * name,void * data,int len)287 fdt_node_add_property(void *node, char *name, void *data, int len)
288 {
289 	char *dummy;
290 
291 	if (!tree_inited)
292 		return 0;
293 
294 	if (fdt_node_property(node, name, &dummy) == -1) {
295 		uint32_t *ptr = (uint32_t *)node;
296 
297 		if (betoh32(*ptr) != FDT_NODE_BEGIN)
298 			return 0;
299 
300 		ptr = skip_node_name(ptr + 1);
301 
302 		memmove(ptr + 3, ptr, tree.end - (char *)ptr);
303 		tree.struct_size += 3 * sizeof(uint32_t);
304 		if (tree.strings > tree.tree)
305 			tree.strings += 3 * sizeof(uint32_t);
306 		if (tree.memory > tree.tree)
307 			tree.memory += 3 * sizeof(uint32_t);
308 		tree.end += 3 * sizeof(uint32_t);
309 		*ptr++ = htobe32(FDT_PROPERTY);
310 		*ptr++ = htobe32(0);
311 		*ptr++ = htobe32(fdt_add_str(name));
312 	}
313 
314 	return fdt_node_set_property(node, name, data, len);
315 }
316 
317 /*
318  * Retrieves next node, skipping all the children nodes of the pointed node,
319  * returns pointer to next node, no matter if it exists or not.
320  */
321 void *
skip_node(void * node)322 skip_node(void *node)
323 {
324 	u_int32_t *ptr = node;
325 
326 	ptr++;
327 
328 	ptr = skip_node_name(ptr);
329 	ptr = skip_props(ptr);
330 
331 	/* skip children */
332 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
333 		ptr = skip_node(ptr);
334 
335 	return skip_nops(ptr + 1);
336 }
337 
338 /*
339  * Retrieves next node, skipping all the children nodes of the pointed node,
340  * returns pointer to next node if exists, otherwise returns NULL.
341  * If passed 0 will return first node of the tree (root).
342  */
343 void *
fdt_next_node(void * node)344 fdt_next_node(void *node)
345 {
346 	u_int32_t *ptr;
347 
348 	if (!tree_inited)
349 		return NULL;
350 
351 	ptr = node;
352 
353 	if (node == NULL) {
354 		ptr = skip_nops((uint32_t *)tree.tree);
355 		return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
356 	}
357 
358 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
359 		return NULL;
360 
361 	ptr++;
362 
363 	ptr = skip_node_name(ptr);
364 	ptr = skip_props(ptr);
365 
366 	/* skip children */
367 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
368 		ptr = skip_node(ptr);
369 
370 	if (betoh32(*ptr) != FDT_NODE_END)
371 		return NULL;
372 
373 	ptr = skip_nops(ptr + 1);
374 
375 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
376 		return NULL;
377 
378 	return ptr;
379 }
380 
381 int
fdt_next_property(void * node,char * name,char ** nextname)382 fdt_next_property(void *node, char *name, char **nextname)
383 {
384 	u_int32_t *ptr;
385 	u_int32_t nameid;
386 
387 	if (!tree_inited)
388 		return 0;
389 
390 	ptr = (u_int32_t *)node;
391 
392 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
393 		return 0;
394 
395 	ptr = skip_node_name(ptr + 1);
396 
397 	while (betoh32(*ptr) == FDT_PROPERTY) {
398 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
399 		if (strcmp(name, "") == 0) {
400 			*nextname = fdt_get_str(nameid);
401 			return 1;
402 		}
403 		if (strcmp(name, fdt_get_str(nameid)) == 0) {
404 			ptr = skip_property(ptr);
405 			if (betoh32(*ptr) != FDT_PROPERTY)
406 				break;
407 			nameid = betoh32(*(ptr + 2));
408 			*nextname = fdt_get_str(nameid);
409 			return 1;
410 		}
411 		ptr = skip_property(ptr);
412 	}
413 	*nextname = "";
414 	return 1;
415 }
416 
417 /*
418  * Retrieves node property as integers and puts them in the given
419  * integer array.
420  */
421 int
fdt_node_property_ints(void * node,char * name,int * out,int outlen)422 fdt_node_property_ints(void *node, char *name, int *out, int outlen)
423 {
424 	int *data;
425 	int i, inlen;
426 
427 	inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int);
428 	if (inlen <= 0)
429 		return -1;
430 
431 	for (i = 0; i < inlen && i < outlen; i++)
432 		out[i] = betoh32(data[i]);
433 
434 	return i;
435 }
436 
437 /*
438  * Retrieves node property as an integer.
439  */
440 int
fdt_node_property_int(void * node,char * name,int * out)441 fdt_node_property_int(void *node, char *name, int *out)
442 {
443 	return fdt_node_property_ints(node, name, out, 1);
444 }
445 
446 /*
447  * Retrieves next node, skipping all the children nodes of the pointed node
448  */
449 void *
fdt_child_node(void * node)450 fdt_child_node(void *node)
451 {
452 	u_int32_t *ptr;
453 
454 	if (!tree_inited)
455 		return NULL;
456 
457 	ptr = node;
458 
459 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
460 		return NULL;
461 
462 	ptr++;
463 
464 	ptr = skip_node_name(ptr);
465 	ptr = skip_props(ptr);
466 	/* check if there is a child node */
467 	return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
468 }
469 
470 /*
471  * Retrieves node name.
472  */
473 char *
fdt_node_name(void * node)474 fdt_node_name(void *node)
475 {
476 	u_int32_t *ptr;
477 
478 	if (!tree_inited)
479 		return NULL;
480 
481 	ptr = node;
482 
483 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
484 		return NULL;
485 
486 	return (char *)(ptr + 1);
487 }
488 
489 void *
fdt_find_node(char * name)490 fdt_find_node(char *name)
491 {
492 	void *node = fdt_next_node(0);
493 	const char *p = name;
494 
495 	if (!tree_inited)
496 		return NULL;
497 
498 	if (*p != '/')
499 		return NULL;
500 
501 	while (*p) {
502 		void *child;
503 		const char *q;
504 		const char *s;
505 
506 		while (*p == '/')
507 			p++;
508 		if (*p == 0)
509 			return node;
510 		q = strchr(p, '/');
511 		if (q == NULL)
512 			q = p + strlen(p);
513 
514 		/* Check for a complete match. */
515 		for (child = fdt_child_node(node); child;
516 		     child = fdt_next_node(child)) {
517 			s = fdt_node_name(child);
518 			if (strncmp(p, s, q - p) == 0 && s[q - p] == '\0')
519 				break;
520 		}
521 		if (child) {
522 			node = child;
523 			p = q;
524 			continue;
525 		}
526 
527 		/* Check for a match without the unit name. */
528 		for (child = fdt_child_node(node); child;
529 		     child = fdt_next_node(child)) {
530 			s = fdt_node_name(child);
531 			if (strncmp(p, s, q - p) == 0 && s[q - p] == '@')
532 				break;
533 		}
534 		if (child) {
535 			node = child;
536 			p = q;
537 			continue;
538 		}
539 
540 		return NULL;	/* No match found. */
541 	}
542 
543 	return node;
544 }
545 
546 void *
fdt_parent_node_recurse(void * pnode,void * child)547 fdt_parent_node_recurse(void *pnode, void *child)
548 {
549 	void *node = fdt_child_node(pnode);
550 	void *tmp;
551 
552 	while (node && (node != child)) {
553 		if ((tmp = fdt_parent_node_recurse(node, child)))
554 			return tmp;
555 		node = fdt_next_node(node);
556 	}
557 	return (node) ? pnode : NULL;
558 }
559 
560 void *
fdt_parent_node(void * node)561 fdt_parent_node(void *node)
562 {
563 	void *pnode = fdt_next_node(0);
564 
565 	if (!tree_inited)
566 		return NULL;
567 
568 	if (node == pnode)
569 		return NULL;
570 
571 	return fdt_parent_node_recurse(pnode, node);
572 }
573 
574 void *
fdt_find_phandle_recurse(void * node,uint32_t phandle)575 fdt_find_phandle_recurse(void *node, uint32_t phandle)
576 {
577 	void *child;
578 	char *data;
579 	void *tmp;
580 	int len;
581 
582 	len = fdt_node_property(node, "phandle", &data);
583 	if (len < 0)
584 		len = fdt_node_property(node, "linux,phandle", &data);
585 
586 	if (len == sizeof(uint32_t) && bemtoh32(data) == phandle)
587 		return node;
588 
589 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
590 		if ((tmp = fdt_find_phandle_recurse(child, phandle)))
591 			return tmp;
592 
593 	return NULL;
594 }
595 
596 void *
fdt_find_phandle(uint32_t phandle)597 fdt_find_phandle(uint32_t phandle)
598 {
599 	return fdt_find_phandle_recurse(fdt_next_node(0), phandle);
600 }
601 
602 void
fdt_get_cells(void * node,int * ac,int * sc)603 fdt_get_cells(void *node, int *ac, int *sc)
604 {
605 	void *parent;
606 
607 	parent = fdt_parent_node(node);
608 	if (parent == NULL)
609 		*ac = *sc = 1;
610 	else
611 		fdt_get_cells(parent, ac, sc);
612 
613 	fdt_node_property_int(node, "#address-cells", ac);
614 	fdt_node_property_int(node, "#size-cells", sc);
615 }
616 
617 /*
618  * Translate memory address depending on parent's range.
619  *
620  * Ranges are a way of mapping one address to another.  This ranges attribute
621  * is set on a node's parent.  This means if a node does not have a parent,
622  * there's nothing to translate.  If it does have a parent and the parent does
623  * not have a ranges attribute, there's nothing to translate either.
624  *
625  * If the parent has a ranges attribute and the attribute is not empty, the
626  * node's memory address has to be in one of the given ranges.  This range is
627  * then used to translate the memory address.
628  *
629  * If the parent has a ranges attribute, but the attribute is empty, there's
630  * nothing to translate.  But it's not a translation barrier.  It can be treated
631  * as a simple 1:1 mapping.
632  *
633  * Translation does not end here.  We need to check if the parent's parent also
634  * has a ranges attribute and ask the same questions again.
635  */
636 int
fdt_translate_reg(void * node,struct fdt_reg * reg)637 fdt_translate_reg(void *node, struct fdt_reg *reg)
638 {
639 	void *parent;
640 	int pac, psc, ac, sc, rlen, rone, *range;
641 	uint64_t from, to, size;
642 
643 	/* No parent, no translation. */
644 	parent = fdt_parent_node(node);
645 	if (parent == NULL)
646 		return 0;
647 
648 	/* Extract ranges property from node. */
649 	rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int);
650 
651 	/* No ranges means translation barrier. Translation stops here. */
652 	if (range == NULL)
653 		return 0;
654 
655 	/* Empty ranges means 1:1 mapping. Continue translation on parent. */
656 	if (rlen <= 0)
657 		return fdt_translate_reg(parent, reg);
658 
659 	/*
660 	 * Get parent address/size width.  We only support 32-bit (1)
661 	 * and 64-bit (2) wide addresses and sizes here.
662 	 */
663 	fdt_get_cells(parent, &pac, &psc);
664 	if (pac <= 0 || pac > 2 || psc <= 0 || psc > 2)
665 		return EINVAL;
666 
667 	/*
668 	 * Get our own address/size width.  Again, we only support
669 	 * 32-bit (1) and 64-bit (2) wide addresses and sizes here.
670 	 */
671 	fdt_get_cells(node, &ac, &sc);
672 	if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2)
673 		return EINVAL;
674 
675 	/* Must have at least one range. */
676 	rone = pac + ac + sc;
677 	if (rlen < rone)
678 		return ESRCH;
679 
680 	/* For each range. */
681 	for (; rlen >= rone; rlen -= rone, range += rone) {
682 		/* Extract from and size, so we can see if we fit. */
683 		from = betoh32(range[0]);
684 		if (ac == 2)
685 			from = (from << 32) + betoh32(range[1]);
686 		size = betoh32(range[ac + pac]);
687 		if (sc == 2)
688 			size = (size << 32) + betoh32(range[ac + pac + 1]);
689 
690 		/* Try next, if we're not in the range. */
691 		if (reg->addr < from || (reg->addr + reg->size) > (from + size))
692 			continue;
693 
694 		/* All good, extract to address and translate. */
695 		to = betoh32(range[ac]);
696 		if (pac == 2)
697 			to = (to << 32) + betoh32(range[ac + 1]);
698 
699 		reg->addr -= from;
700 		reg->addr += to;
701 		return fdt_translate_reg(parent, reg);
702 	}
703 
704 	/* To be successful, we must have returned in the for-loop. */
705 	return ESRCH;
706 }
707 
708 /*
709  * Parse the memory address and size of a node.
710  */
711 int
fdt_get_reg(void * node,int idx,struct fdt_reg * reg)712 fdt_get_reg(void *node, int idx, struct fdt_reg *reg)
713 {
714 	void *parent;
715 	int ac, sc, off, *in, inlen;
716 
717 	if (node == NULL || reg == NULL)
718 		return EINVAL;
719 
720 	parent = fdt_parent_node(node);
721 	if (parent == NULL)
722 		return EINVAL;
723 
724 	/*
725 	 * Get parent address/size width.  We only support 32-bit (1)
726 	 * and 64-bit (2) wide addresses and sizes here.
727 	 */
728 	fdt_get_cells(parent, &ac, &sc);
729 	if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2)
730 		return EINVAL;
731 
732 	inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int);
733 	if (inlen < ((idx + 1) * (ac + sc)))
734 		return EINVAL;
735 
736 	off = idx * (ac + sc);
737 
738 	reg->addr = betoh32(in[off]);
739 	if (ac == 2)
740 		reg->addr = (reg->addr << 32) + betoh32(in[off + 1]);
741 
742 	reg->size = betoh32(in[off + ac]);
743 	if (sc == 2)
744 		reg->size = (reg->size << 32) + betoh32(in[off + ac + 1]);
745 
746 	return fdt_translate_reg(parent, reg);
747 }
748 
749 int
fdt_is_compatible(void * node,const char * name)750 fdt_is_compatible(void *node, const char *name)
751 {
752 	char *data;
753 	int len;
754 
755 	len = fdt_node_property(node, "compatible", &data);
756 	while (len > 0) {
757 		if (strcmp(data, name) == 0)
758 			return 1;
759 		len -= strlen(data) + 1;
760 		data += strlen(data) + 1;
761 	}
762 
763 	return 0;
764 }
765 
766 #ifdef DEBUG
767 /*
768  * Debug methods for printing whole tree, particular nodes and properties
769  */
770 void *
fdt_print_property(void * node,int level)771 fdt_print_property(void *node, int level)
772 {
773 	u_int32_t *ptr;
774 	char *tmp, *value;
775 	int cnt;
776 	u_int32_t nameid, size;
777 
778 	ptr = (u_int32_t *)node;
779 
780 	if (!tree_inited)
781 		return NULL;
782 
783 	if (betoh32(*ptr) != FDT_PROPERTY)
784 		return ptr; /* should never happen */
785 
786 	/* extract property name_id and size */
787 	size = betoh32(*++ptr);
788 	nameid = betoh32(*++ptr);
789 
790 	for (cnt = 0; cnt < level; cnt++)
791 		printf("\t");
792 
793 	tmp = fdt_get_str(nameid);
794 	printf("\t%s : ", tmp ? tmp : "NO_NAME");
795 
796 	ptr++;
797 	value = (char *)ptr;
798 
799 	if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
800 	    !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
801 	    !strcmp(tmp, "linux,stdout-path")) {
802 		printf("%s", value);
803 	} else if (!strcmp(tmp, "clock-frequency") ||
804 	    !strcmp(tmp, "timebase-frequency")) {
805 		printf("%d", betoh32(*((unsigned int *)value)));
806 	} else {
807 		for (cnt = 0; cnt < size; cnt++) {
808 			if ((cnt % sizeof(u_int32_t)) == 0)
809 				printf(" ");
810 			printf("%02x", value[cnt]);
811 		}
812 	}
813 	ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
814 	printf("\n");
815 
816 	return ptr;
817 }
818 
819 void
fdt_print_node(void * node,int level)820 fdt_print_node(void *node, int level)
821 {
822 	u_int32_t *ptr;
823 	int cnt;
824 
825 	ptr = (u_int32_t *)node;
826 
827 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
828 		return;
829 
830 	ptr++;
831 
832 	for (cnt = 0; cnt < level; cnt++)
833 		printf("\t");
834 	printf("%s :\n", fdt_node_name(node));
835 	ptr = skip_node_name(ptr);
836 
837 	while (betoh32(*ptr) == FDT_PROPERTY)
838 		ptr = fdt_print_property(ptr, level);
839 }
840 
841 void
fdt_print_node_recurse(void * node,int level)842 fdt_print_node_recurse(void *node, int level)
843 {
844 	void *child;
845 
846 	fdt_print_node(node, level);
847 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
848 		fdt_print_node_recurse(child, level + 1);
849 }
850 
851 void
fdt_print_tree(void)852 fdt_print_tree(void)
853 {
854 	fdt_print_node_recurse(fdt_next_node(0), 0);
855 }
856 #endif
857 
858 int
OF_peer(int handle)859 OF_peer(int handle)
860 {
861 	void *node = (char *)tree.header + handle;
862 
863 	if (handle == 0)
864 		node = fdt_find_node("/");
865 	else
866 		node = fdt_next_node(node);
867 	return node ? ((char *)node - (char *)tree.header) : 0;
868 }
869 
870 int
OF_child(int handle)871 OF_child(int handle)
872 {
873 	void *node = (char *)tree.header + handle;
874 
875 	node = fdt_child_node(node);
876 	return node ? ((char *)node - (char *)tree.header) : 0;
877 }
878 
879 int
OF_parent(int handle)880 OF_parent(int handle)
881 {
882 	void *node = (char *)tree.header + handle;
883 
884 	node = fdt_parent_node(node);
885 	return node ? ((char *)node - (char *)tree.header) : 0;
886 }
887 
888 int
OF_finddevice(char * name)889 OF_finddevice(char *name)
890 {
891 	void *node;
892 
893 	node = fdt_find_node(name);
894 	return node ? ((char *)node - (char *)tree.header) : -1;
895 }
896 
897 int
OF_getnodebyname(int handle,const char * name)898 OF_getnodebyname(int handle, const char *name)
899 {
900 	void *node = (char *)tree.header + handle;
901 	void *child;
902 	char *data;
903 	int len;
904 
905 	if (handle == 0)
906 		node = fdt_find_node("/");
907 
908 	for (child = fdt_child_node(node); child;
909 	     child = fdt_next_node(child)) {
910 		if (strcmp(name, fdt_node_name(child)) == 0)
911 			break;
912 	}
913 	if (child)
914 		return (char *)child - (char *)tree.header;
915 
916 	len = strlen(name);
917 	for (child = fdt_child_node(node); child;
918 	     child = fdt_next_node(child)) {
919 		data = fdt_node_name(child);
920 		if (strncmp(name, data, len) == 0 &&
921 		    strlen(data) > len && data[len] == '@')
922 			break;
923 	}
924 	if (child)
925 		return (char *)child - (char *)tree.header;
926 
927 	return 0;
928 }
929 
930 int
OF_getnodebyphandle(uint32_t phandle)931 OF_getnodebyphandle(uint32_t phandle)
932 {
933 	void *node;
934 
935 	node = fdt_find_phandle(phandle);
936 	return node ? ((char *)node - (char *)tree.header) : 0;
937 }
938 
939 int
OF_getproplen(int handle,char * prop)940 OF_getproplen(int handle, char *prop)
941 {
942 	void *node = (char *)tree.header + handle;
943 	char *data, *name;
944 	int len;
945 
946 	len = fdt_node_property(node, prop, &data);
947 
948 	/*
949 	 * The "name" property is optional since version 16 of the
950 	 * flattened device tree specification, so we synthesize one
951 	 * from the unit name of the node if it is missing.
952 	 */
953 	if (len < 0 && strcmp(prop, "name") == 0) {
954 		name = fdt_node_name(node);
955 		data = strchr(name, '@');
956 		if (data)
957 			len = data - name;
958 		else
959 			len = strlen(name);
960 		return len + 1;
961 	}
962 
963 	return len;
964 }
965 
966 int
OF_getprop(int handle,char * prop,void * buf,int buflen)967 OF_getprop(int handle, char *prop, void *buf, int buflen)
968 {
969 	void *node = (char *)tree.header + handle;
970 	char *data;
971 	int len;
972 
973 	len = fdt_node_property(node, prop, &data);
974 
975 	/*
976 	 * The "name" property is optional since version 16 of the
977 	 * flattened device tree specification, so we synthesize one
978 	 * from the unit name of the node if it is missing.
979 	 */
980 	if (len < 0 && strcmp(prop, "name") == 0) {
981 		data = fdt_node_name(node);
982 		if (data) {
983 			len = strlcpy(buf, data, buflen);
984 			data = strchr(buf, '@');
985 			if (data) {
986 				*data = 0;
987 				len = data - (char *)buf;
988 			}
989 			return len + 1;
990 		}
991 	}
992 
993 	if (len > 0)
994 		memcpy(buf, data, min(len, buflen));
995 	return len;
996 }
997 
998 int
OF_getpropbool(int handle,char * prop)999 OF_getpropbool(int handle, char *prop)
1000 {
1001 	void *node = (char *)tree.header + handle;
1002 	char *data;
1003 
1004 	return (fdt_node_property(node, prop, &data) >= 0);
1005 }
1006 
1007 uint32_t
OF_getpropint(int handle,char * prop,uint32_t defval)1008 OF_getpropint(int handle, char *prop, uint32_t defval)
1009 {
1010 	uint32_t val;
1011 	int len;
1012 
1013 	len = OF_getprop(handle, prop, &val, sizeof(val));
1014 	if (len != sizeof(val))
1015 		return defval;
1016 
1017 	return betoh32(val);
1018 }
1019 
1020 int
OF_getpropintarray(int handle,char * prop,uint32_t * buf,int buflen)1021 OF_getpropintarray(int handle, char *prop, uint32_t *buf, int buflen)
1022 {
1023 	int len;
1024 	int i;
1025 
1026 	len = OF_getprop(handle, prop, buf, buflen);
1027 	if (len < 0 || (len % sizeof(uint32_t)))
1028 		return -1;
1029 
1030 	for (i = 0; i < min(len, buflen) / sizeof(uint32_t); i++)
1031 		buf[i] = betoh32(buf[i]);
1032 
1033 	return len;
1034 }
1035 
1036 uint64_t
OF_getpropint64(int handle,char * prop,uint64_t defval)1037 OF_getpropint64(int handle, char *prop, uint64_t defval)
1038 {
1039 	uint64_t val;
1040 	int len;
1041 
1042 	len = OF_getprop(handle, prop, &val, sizeof(val));
1043 	if (len != sizeof(val))
1044 		return defval;
1045 
1046 	return betoh64(val);
1047 }
1048 
1049 int
OF_getpropint64array(int handle,char * prop,uint64_t * buf,int buflen)1050 OF_getpropint64array(int handle, char *prop, uint64_t *buf, int buflen)
1051 {
1052 	int len;
1053 	int i;
1054 
1055 	len = OF_getprop(handle, prop, buf, buflen);
1056 	if (len < 0 || (len % sizeof(uint64_t)))
1057 		return -1;
1058 
1059 	for (i = 0; i < min(len, buflen) / sizeof(uint64_t); i++)
1060 		buf[i] = betoh64(buf[i]);
1061 
1062 	return len;
1063 }
1064 
1065 int
OF_nextprop(int handle,char * prop,void * nextprop)1066 OF_nextprop(int handle, char *prop, void *nextprop)
1067 {
1068 	void *node = (char *)tree.header + handle;
1069 	char *data;
1070 
1071 	if (fdt_node_property(node, "name", &data) == -1) {
1072 		if (strcmp(prop, "") == 0)
1073 			return strlcpy(nextprop, "name", OFMAXPARAM);
1074 		if (strcmp(prop, "name") == 0)
1075 			prop = "";
1076 	}
1077 
1078 	if (fdt_next_property(node, prop, &data))
1079 		return strlcpy(nextprop, data, OFMAXPARAM);
1080 	return -1;
1081 }
1082 
1083 int
OF_is_compatible(int handle,const char * name)1084 OF_is_compatible(int handle, const char *name)
1085 {
1086 	void *node = (char *)tree.header + handle;
1087 	return (fdt_is_compatible(node, name));
1088 }
1089 
1090 int
OF_getindex(int handle,const char * entry,const char * prop)1091 OF_getindex(int handle, const char *entry, const char *prop)
1092 {
1093 	char *names;
1094 	char *name;
1095 	char *end;
1096 	int idx = 0;
1097 	int len;
1098 
1099 	if (entry == NULL)
1100 		return 0;
1101 
1102 	len = OF_getproplen(handle, (char *)prop);
1103 	if (len <= 0)
1104 		return -1;
1105 
1106 	names = malloc(len, M_TEMP, M_WAITOK);
1107 	OF_getprop(handle, (char *)prop, names, len);
1108 	end = names + len;
1109 	name = names;
1110 	while (name < end) {
1111 		if (strcmp(name, entry) == 0) {
1112 			free(names, M_TEMP, len);
1113 			return idx;
1114 		}
1115 		name += strlen(name) + 1;
1116 		idx++;
1117 	}
1118 	free(names, M_TEMP, len);
1119 	return -1;
1120 }
1121