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