blob: f39785fd2fc6d379804a83c128f2c4d42b4ccb32 [file] [log] [blame]
David Gibson3da0f9a2006-11-27 16:21:28 +11001/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
David Gibson94816052007-06-13 16:30:48 +10005 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
David Gibson3da0f9a2006-11-27 16:21:28 +11007 *
David Gibson94816052007-06-13 16:30:48 +10008 * a) This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
David Gibson3da0f9a2006-11-27 16:21:28 +110012 *
David Gibson94816052007-06-13 16:30:48 +100013 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 * MA 02110-1301 USA
22 *
23 * Alternatively,
24 *
25 * b) Redistribution and use in source and binary forms, with or
26 * without modification, are permitted provided that the following
27 * conditions are met:
28 *
29 * 1. Redistributions of source code must retain the above
30 * copyright notice, this list of conditions and the following
31 * disclaimer.
32 * 2. Redistributions in binary form must reproduce the above
33 * copyright notice, this list of conditions and the following
34 * disclaimer in the documentation and/or other materials
35 * provided with the distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
David Gibson3da0f9a2006-11-27 16:21:28 +110050 */
51#include "libfdt_env.h"
52
53#include <fdt.h>
54#include <libfdt.h>
55
56#include "libfdt_internal.h"
57
David Gibson9a9fdf52006-12-15 15:12:51 +110058#define CHECK_HEADER(fdt) \
David Gibson3da0f9a2006-11-27 16:21:28 +110059 { \
60 int err; \
David Gibson96b5fad2007-10-24 10:28:52 +100061 if ((err = fdt_check_header(fdt)) != 0) \
David Gibson9a9fdf52006-12-15 15:12:51 +110062 return err; \
David Gibson3da0f9a2006-11-27 16:21:28 +110063 }
64
David Gibsond2a9da02007-09-28 15:51:04 +100065static int nodename_eq(const void *fdt, int offset,
66 const char *s, int len)
David Gibson3da0f9a2006-11-27 16:21:28 +110067{
68 const char *p = fdt_offset_ptr(fdt, offset, len+1);
69
70 if (! p)
71 /* short match */
72 return 0;
73
74 if (memcmp(p, s, len) != 0)
75 return 0;
76
David Gibsond2a9da02007-09-28 15:51:04 +100077 if (p[len] == '\0')
78 return 1;
79 else if (!memchr(s, '@', len) && (p[len] == '@'))
80 return 1;
81 else
David Gibson3da0f9a2006-11-27 16:21:28 +110082 return 0;
David Gibson3da0f9a2006-11-27 16:21:28 +110083}
84
David Gibson11d53022007-10-18 12:10:42 +100085const char *fdt_string(const void *fdt, int stroffset)
David Gibson3aa4cfd2006-11-29 16:34:30 +110086{
David Gibsonede25de2006-12-01 15:02:10 +110087 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
David Gibson3aa4cfd2006-11-29 16:34:30 +110088}
89
David Gibsonfd1bf3a2007-10-10 17:12:12 +100090int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
91{
92 CHECK_HEADER(fdt);
93 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
94 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
95 return 0;
96}
97
98int fdt_num_mem_rsv(const void *fdt)
99{
100 int i = 0;
101
102 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
103 i++;
104 return i;
105}
106
David Gibson73d60922006-12-15 15:12:47 +1100107int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100108 const char *name, int namelen)
109{
110 int level = 0;
111 uint32_t tag;
112 int offset, nextoffset;
113
David Gibson9a9fdf52006-12-15 15:12:51 +1100114 CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +1100115
116 tag = _fdt_next_tag(fdt, parentoffset, &nextoffset);
117 if (tag != FDT_BEGIN_NODE)
David Gibson9a9fdf52006-12-15 15:12:51 +1100118 return -FDT_ERR_BADOFFSET;
David Gibson3da0f9a2006-11-27 16:21:28 +1100119
120 do {
121 offset = nextoffset;
122 tag = _fdt_next_tag(fdt, offset, &nextoffset);
123
124 switch (tag) {
125 case FDT_END:
David Gibson9a9fdf52006-12-15 15:12:51 +1100126 return -FDT_ERR_TRUNCATED;
David Gibson3da0f9a2006-11-27 16:21:28 +1100127
128 case FDT_BEGIN_NODE:
129 level++;
130 if (level != 1)
131 continue;
David Gibsond2a9da02007-09-28 15:51:04 +1000132 if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
David Gibson3da0f9a2006-11-27 16:21:28 +1100133 /* Found it! */
134 return offset;
135 break;
136
137 case FDT_END_NODE:
138 level--;
139 break;
140
141 case FDT_PROP:
142 case FDT_NOP:
143 break;
144
145 default:
David Gibson9a9fdf52006-12-15 15:12:51 +1100146 return -FDT_ERR_BADSTRUCTURE;
David Gibson3da0f9a2006-11-27 16:21:28 +1100147 }
148 } while (level >= 0);
149
David Gibson9a9fdf52006-12-15 15:12:51 +1100150 return -FDT_ERR_NOTFOUND;
David Gibson3da0f9a2006-11-27 16:21:28 +1100151}
152
David Gibson73d60922006-12-15 15:12:47 +1100153int fdt_subnode_offset(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100154 const char *name)
155{
156 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
157}
158
David Gibson73d60922006-12-15 15:12:47 +1100159int fdt_path_offset(const void *fdt, const char *path)
David Gibson3da0f9a2006-11-27 16:21:28 +1100160{
161 const char *end = path + strlen(path);
162 const char *p = path;
163 int offset = 0;
164
David Gibson9a9fdf52006-12-15 15:12:51 +1100165 CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +1100166
167 if (*path != '/')
David Gibson9a9fdf52006-12-15 15:12:51 +1100168 return -FDT_ERR_BADPATH;
David Gibson3da0f9a2006-11-27 16:21:28 +1100169
170 while (*p) {
171 const char *q;
172
173 while (*p == '/')
174 p++;
175 if (! *p)
David Gibsonbd2ae2f2007-08-29 12:22:50 +1000176 return offset;
David Gibson3da0f9a2006-11-27 16:21:28 +1100177 q = strchr(p, '/');
178 if (! q)
179 q = end;
180
181 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
David Gibson9a9fdf52006-12-15 15:12:51 +1100182 if (offset < 0)
David Gibson3da0f9a2006-11-27 16:21:28 +1100183 return offset;
184
185 p = q;
186 }
187
David Gibson63dc9c72007-09-18 11:44:04 +1000188 return offset;
David Gibson3da0f9a2006-11-27 16:21:28 +1100189}
190
David Gibson9d26eab2007-08-30 14:54:04 +1000191const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
192{
193 const struct fdt_node_header *nh;
194 int err;
195
David Gibson96b5fad2007-10-24 10:28:52 +1000196 if ((err = fdt_check_header(fdt)) != 0)
David Gibson9d26eab2007-08-30 14:54:04 +1000197 goto fail;
198
199 err = -FDT_ERR_BADOFFSET;
200 nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
201 if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
202 goto fail;
203
204 if (len)
205 *len = strlen(nh->name);
206
207 return nh->name;
208
209 fail:
210 if (len)
211 *len = err;
212 return NULL;
213}
214
David Gibsona6c76f92007-06-13 14:18:10 +1000215const struct fdt_property *fdt_get_property(const void *fdt,
216 int nodeoffset,
217 const char *name, int *lenp)
David Gibson3da0f9a2006-11-27 16:21:28 +1100218{
David Gibson94993f42006-12-11 16:15:34 +1100219 uint32_t tag;
David Gibsona6c76f92007-06-13 14:18:10 +1000220 const struct fdt_property *prop;
David Gibson94993f42006-12-11 16:15:34 +1100221 int namestroff;
222 int offset, nextoffset;
David Gibsona7ee95d2006-12-15 15:12:49 +1100223 int err;
David Gibson3da0f9a2006-11-27 16:21:28 +1100224
David Gibson96b5fad2007-10-24 10:28:52 +1000225 if ((err = fdt_check_header(fdt)) != 0)
David Gibsona7ee95d2006-12-15 15:12:49 +1100226 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100227
David Gibson9a9fdf52006-12-15 15:12:51 +1100228 err = -FDT_ERR_BADOFFSET;
David Gibson94993f42006-12-11 16:15:34 +1100229 if (nodeoffset % FDT_TAGSIZE)
David Gibsona7ee95d2006-12-15 15:12:49 +1100230 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100231
David Gibson94993f42006-12-11 16:15:34 +1100232 tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset);
233 if (tag != FDT_BEGIN_NODE)
David Gibsona7ee95d2006-12-15 15:12:49 +1100234 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100235
David Gibson94993f42006-12-11 16:15:34 +1100236 do {
237 offset = nextoffset;
David Gibson94993f42006-12-11 16:15:34 +1100238
239 tag = _fdt_next_tag(fdt, offset, &nextoffset);
240 switch (tag) {
241 case FDT_END:
David Gibson9a9fdf52006-12-15 15:12:51 +1100242 err = -FDT_ERR_TRUNCATED;
David Gibsona7ee95d2006-12-15 15:12:49 +1100243 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100244
245 case FDT_BEGIN_NODE:
David Gibson94993f42006-12-11 16:15:34 +1100246 case FDT_END_NODE:
David Gibson592ea582007-09-04 10:43:03 +1000247 case FDT_NOP:
David Gibson94993f42006-12-11 16:15:34 +1100248 break;
249
250 case FDT_PROP:
David Gibson9a9fdf52006-12-15 15:12:51 +1100251 err = -FDT_ERR_BADSTRUCTURE;
David Gibson94993f42006-12-11 16:15:34 +1100252 prop = fdt_offset_ptr_typed(fdt, offset, prop);
253 if (! prop)
David Gibsona7ee95d2006-12-15 15:12:49 +1100254 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100255 namestroff = fdt32_to_cpu(prop->nameoff);
256 if (streq(fdt_string(fdt, namestroff), name)) {
257 /* Found it! */
258 int len = fdt32_to_cpu(prop->len);
259 prop = fdt_offset_ptr(fdt, offset,
David Gibson9825f822006-12-14 15:29:25 +1100260 sizeof(*prop)+len);
David Gibson94993f42006-12-11 16:15:34 +1100261 if (! prop)
David Gibsona7ee95d2006-12-15 15:12:49 +1100262 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100263
264 if (lenp)
265 *lenp = len;
David Gibson63dc9c72007-09-18 11:44:04 +1000266
David Gibson94993f42006-12-11 16:15:34 +1100267 return prop;
268 }
269 break;
270
David Gibson94993f42006-12-11 16:15:34 +1100271 default:
David Gibson9a9fdf52006-12-15 15:12:51 +1100272 err = -FDT_ERR_BADSTRUCTURE;
David Gibsona7ee95d2006-12-15 15:12:49 +1100273 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100274 }
David Gibson592ea582007-09-04 10:43:03 +1000275 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
David Gibson94993f42006-12-11 16:15:34 +1100276
David Gibson9a9fdf52006-12-15 15:12:51 +1100277 err = -FDT_ERR_NOTFOUND;
David Gibsona7ee95d2006-12-15 15:12:49 +1100278 fail:
279 if (lenp)
David Gibson9a9fdf52006-12-15 15:12:51 +1100280 *lenp = err;
David Gibsona7ee95d2006-12-15 15:12:49 +1100281 return NULL;
David Gibson3da0f9a2006-11-27 16:21:28 +1100282}
283
David Gibsona6c76f92007-06-13 14:18:10 +1000284const void *fdt_getprop(const void *fdt, int nodeoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100285 const char *name, int *lenp)
286{
287 const struct fdt_property *prop;
David Gibson3da0f9a2006-11-27 16:21:28 +1100288
David Gibson94993f42006-12-11 16:15:34 +1100289 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
David Gibsona7ee95d2006-12-15 15:12:49 +1100290 if (! prop)
291 return NULL;
David Gibson3da0f9a2006-11-27 16:21:28 +1100292
293 return prop->data;
294}
David Gibson037db262007-08-30 14:54:04 +1000295
296int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
297{
298 uint32_t tag;
299 int p = 0, overflow = 0;
300 int offset, nextoffset, namelen;
301 const char *name;
302
303 CHECK_HEADER(fdt);
304
305 tag = _fdt_next_tag(fdt, 0, &nextoffset);
306 if (tag != FDT_BEGIN_NODE)
307 return -FDT_ERR_BADSTRUCTURE;
308
309 if (buflen < 2)
310 return -FDT_ERR_NOSPACE;
311 buf[0] = '/';
312 p = 1;
313
David Gibsone2b3bb32007-08-31 14:30:16 +1000314 while (nextoffset <= nodeoffset) {
David Gibson037db262007-08-30 14:54:04 +1000315 offset = nextoffset;
316 tag = _fdt_next_tag(fdt, offset, &nextoffset);
317 switch (tag) {
318 case FDT_END:
319 return -FDT_ERR_BADOFFSET;
320
321 case FDT_BEGIN_NODE:
322 name = fdt_get_name(fdt, offset, &namelen);
323 if (!name)
324 return namelen;
325 if (overflow || ((p + namelen + 1) > buflen)) {
326 overflow++;
327 break;
328 }
329 memcpy(buf + p, name, namelen);
330 p += namelen;
331 buf[p++] = '/';
332 break;
333
334 case FDT_END_NODE:
335 if (overflow) {
336 overflow--;
337 break;
338 }
339 do {
340 p--;
341 } while (buf[p-1] != '/');
342 break;
343
344 case FDT_PROP:
345 case FDT_NOP:
346 break;
347
348 default:
349 return -FDT_ERR_BADSTRUCTURE;
350 }
351 }
352
353 if (overflow)
354 return -FDT_ERR_NOSPACE;
355
356 if (p > 1) /* special case so that root path is "/", not "" */
357 p--;
358 buf[p] = '\0';
359 return p;
360}
David Gibson12482372007-08-30 14:54:04 +1000361
362int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
363 int supernodedepth, int *nodedepth)
364{
365 int level = -1;
366 uint32_t tag;
367 int offset, nextoffset = 0;
368 int supernodeoffset = -FDT_ERR_INTERNAL;
369
370 CHECK_HEADER(fdt);
371
372 if (supernodedepth < 0)
373 return -FDT_ERR_NOTFOUND;
374
375 do {
376 offset = nextoffset;
377 tag = _fdt_next_tag(fdt, offset, &nextoffset);
378 switch (tag) {
379 case FDT_END:
380 return -FDT_ERR_BADOFFSET;
381
382 case FDT_BEGIN_NODE:
383 level++;
384 if (level == supernodedepth)
385 supernodeoffset = offset;
386 break;
387
388 case FDT_END_NODE:
389 level--;
390 break;
391
392 case FDT_PROP:
393 case FDT_NOP:
394 break;
395
396 default:
397 return -FDT_ERR_BADSTRUCTURE;
398 }
399 } while (offset < nodeoffset);
400
401 if (nodedepth)
402 *nodedepth = level;
403
404 if (supernodedepth > level)
405 return -FDT_ERR_NOTFOUND;
406 return supernodeoffset;
407}
408
409int fdt_node_depth(const void *fdt, int nodeoffset)
410{
411 int nodedepth;
412 int err;
413
414 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
415 if (err)
416 return (err < 0) ? err : -FDT_ERR_INTERNAL;
417 return nodedepth;
418}
419
420int fdt_parent_offset(const void *fdt, int nodeoffset)
421{
422 int nodedepth = fdt_node_depth(fdt, nodeoffset);
423
424 if (nodedepth < 0)
425 return nodedepth;
426 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
427 nodedepth - 1, NULL);
428}
David Gibsonae1454b2007-09-17 14:28:34 +1000429
430int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
431 const char *propname,
432 const void *propval, int proplen)
433{
434 uint32_t tag;
435 int offset, nextoffset;
436 const void *val;
437 int len;
438
439 CHECK_HEADER(fdt);
440
441 if (startoffset >= 0) {
442 tag = _fdt_next_tag(fdt, startoffset, &nextoffset);
443 if (tag != FDT_BEGIN_NODE)
444 return -FDT_ERR_BADOFFSET;
445 } else {
446 nextoffset = 0;
447 }
448
449 /* FIXME: The algorithm here is pretty horrible: we scan each
450 * property of a node in fdt_getprop(), then if that didn't
451 * find what we want, we scan over them again making our way
452 * to the next node. Still it's the easiest to implement
453 * approach; performance can come later. */
454 do {
455 offset = nextoffset;
456 tag = _fdt_next_tag(fdt, offset, &nextoffset);
457
458 switch (tag) {
459 case FDT_BEGIN_NODE:
460 val = fdt_getprop(fdt, offset, propname, &len);
461 if (val
462 && (len == proplen)
463 && (memcmp(val, propval, len) == 0))
464 return offset;
465 break;
466
467 case FDT_PROP:
468 case FDT_END:
469 case FDT_END_NODE:
470 case FDT_NOP:
471 break;
472
473 default:
474 return -FDT_ERR_BADSTRUCTURE;
475 }
476 } while (tag != FDT_END);
477
478 return -FDT_ERR_NOTFOUND;
479}
David Gibson333542f2007-10-16 13:58:25 +1000480
481int _stringlist_contains(const void *strlist, int listlen, const char *str)
482{
483 int len = strlen(str);
484 const void *p;
485
486 while (listlen >= len) {
487 if (memcmp(str, strlist, len+1) == 0)
488 return 1;
489 p = memchr(strlist, '\0', listlen);
490 if (!p)
491 return 0; /* malformed strlist.. */
492 listlen -= (p-strlist) + 1;
493 strlist = p + 1;
494 }
495 return 0;
496}
497
498int fdt_node_check_compatible(const void *fdt, int nodeoffset,
499 const char *compatible)
500{
501 const void *prop;
502 int len;
503
504 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
505 if (!prop)
506 return len;
507 if (_stringlist_contains(prop, len, compatible))
508 return 0;
509 else
510 return 1;
511}
512
513int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
514 const char *compatible)
515{
516 uint32_t tag;
517 int offset, nextoffset;
518 int err;
519
520 CHECK_HEADER(fdt);
521
522 if (startoffset >= 0) {
523 tag = _fdt_next_tag(fdt, startoffset, &nextoffset);
524 if (tag != FDT_BEGIN_NODE)
525 return -FDT_ERR_BADOFFSET;
526 } else {
527 nextoffset = 0;
528 }
529
530 /* FIXME: The algorithm here is pretty horrible: we scan each
531 * property of a node in fdt_node_check_compatible(), then if
532 * that didn't find what we want, we scan over them again
533 * making our way to the next node. Still it's the easiest to
534 * implement approach; performance can come later. */
535 do {
536 offset = nextoffset;
537 tag = _fdt_next_tag(fdt, offset, &nextoffset);
538
539 switch (tag) {
540 case FDT_BEGIN_NODE:
541 err = fdt_node_check_compatible(fdt, offset,
542 compatible);
543 if ((err < 0)
544 && (err != -FDT_ERR_NOTFOUND))
545 return err;
546 else if (err == 0)
547 return offset;
548 break;
549
550 case FDT_PROP:
551 case FDT_END:
552 case FDT_END_NODE:
553 case FDT_NOP:
554 break;
555
556 default:
557 return -FDT_ERR_BADSTRUCTURE;
558 }
559 } while (tag != FDT_END);
560
561 return -FDT_ERR_NOTFOUND;
562}