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