blob: 12a37d59f96eba267235cf06962f7ccc7a6c2259 [file] [log] [blame]
Gerald Van Baren35748172007-03-31 12:00:56 -04001/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
Kumar Gala8d04f022007-10-24 11:04:22 -05005 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
Gerald Van Baren35748172007-03-31 12:00:56 -04007 *
Kumar Gala8d04f022007-10-24 11:04:22 -05008 * 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.
Gerald Van Baren35748172007-03-31 12:00:56 -040012 *
Kumar Gala8d04f022007-10-24 11:04:22 -050013 * 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.
Gerald Van Baren35748172007-03-31 12:00:56 -040050 */
51#include "libfdt_env.h"
52
53#include <fdt.h>
54#include <libfdt.h>
55
56#include "libfdt_internal.h"
57
Kumar Gala8d04f022007-10-24 11:04:22 -050058#define CHECK_HEADER(fdt) \
59 { \
60 int err; \
61 if ((err = fdt_check_header(fdt)) != 0) \
62 return err; \
63 }
Gerald Van Baren35748172007-03-31 12:00:56 -040064
Kumar Gala8d04f022007-10-24 11:04:22 -050065static int nodename_eq(const void *fdt, int offset,
66 const char *s, int len)
Gerald Van Baren35748172007-03-31 12:00:56 -040067{
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
Kumar Gala8d04f022007-10-24 11:04:22 -050077 if (p[len] == '\0')
78 return 1;
79 else if (!memchr(s, '@', len) && (p[len] == '@'))
80 return 1;
81 else
Gerald Van Baren35748172007-03-31 12:00:56 -040082 return 0;
Gerald Van Baren35748172007-03-31 12:00:56 -040083}
84
Kumar Gala8d04f022007-10-24 11:04:22 -050085const char *fdt_string(const void *fdt, int stroffset)
Gerald Van Baren35748172007-03-31 12:00:56 -040086{
87 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
88}
89
Kumar Gala8d04f022007-10-24 11:04:22 -050090int 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
Gerald Van Baren35748172007-03-31 12:00:56 -0400107int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
108 const char *name, int namelen)
109{
110 int level = 0;
111 uint32_t tag;
112 int offset, nextoffset;
113
114 CHECK_HEADER(fdt);
115
Kumar Gala8d04f022007-10-24 11:04:22 -0500116 tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
Gerald Van Baren35748172007-03-31 12:00:56 -0400117 if (tag != FDT_BEGIN_NODE)
118 return -FDT_ERR_BADOFFSET;
119
120 do {
121 offset = nextoffset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500122 tag = fdt_next_tag(fdt, offset, &nextoffset);
Gerald Van Baren35748172007-03-31 12:00:56 -0400123
124 switch (tag) {
125 case FDT_END:
126 return -FDT_ERR_TRUNCATED;
127
128 case FDT_BEGIN_NODE:
129 level++;
130 if (level != 1)
131 continue;
Kumar Gala8d04f022007-10-24 11:04:22 -0500132 if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
133 /* Found it! */
Gerald Van Baren35748172007-03-31 12:00:56 -0400134 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:
146 return -FDT_ERR_BADSTRUCTURE;
147 }
148 } while (level >= 0);
149
150 return -FDT_ERR_NOTFOUND;
151}
152
153int fdt_subnode_offset(const void *fdt, int parentoffset,
154 const char *name)
155{
156 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
157}
158
Kumar Gala8d04f022007-10-24 11:04:22 -0500159int fdt_path_offset(const void *fdt, const char *path)
Gerald Van Baren35748172007-03-31 12:00:56 -0400160{
161 const char *end = path + strlen(path);
162 const char *p = path;
163 int offset = 0;
164
165 CHECK_HEADER(fdt);
166
167 if (*path != '/')
168 return -FDT_ERR_BADPATH;
169
170 while (*p) {
171 const char *q;
172
173 while (*p == '/')
174 p++;
175 if (! *p)
Kumar Gala8d04f022007-10-24 11:04:22 -0500176 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400177 q = strchr(p, '/');
178 if (! q)
179 q = end;
180
181 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
182 if (offset < 0)
183 return offset;
184
185 p = q;
186 }
187
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400188 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400189}
190
Kumar Gala8d04f022007-10-24 11:04:22 -0500191const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
Gerald Van Baren35748172007-03-31 12:00:56 -0400192{
Kumar Gala8d04f022007-10-24 11:04:22 -0500193 const struct fdt_node_header *nh;
194 int err;
195
196 if ((err = fdt_check_header(fdt)) != 0)
197 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
215const struct fdt_property *fdt_get_property(const void *fdt,
216 int nodeoffset,
217 const char *name, int *lenp)
218{
Gerald Van Baren35748172007-03-31 12:00:56 -0400219 uint32_t tag;
Kumar Gala8d04f022007-10-24 11:04:22 -0500220 const struct fdt_property *prop;
221 int namestroff;
Gerald Van Baren35748172007-03-31 12:00:56 -0400222 int offset, nextoffset;
223 int err;
224
Gerald Van Baren6679f922007-04-06 14:17:14 -0400225 if ((err = fdt_check_header(fdt)) != 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400226 goto fail;
227
228 err = -FDT_ERR_BADOFFSET;
229 if (nodeoffset % FDT_TAGSIZE)
230 goto fail;
231
Kumar Gala8d04f022007-10-24 11:04:22 -0500232 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
Gerald Van Baren35748172007-03-31 12:00:56 -0400233 if (tag != FDT_BEGIN_NODE)
234 goto fail;
235
236 do {
237 offset = nextoffset;
238
Kumar Gala8d04f022007-10-24 11:04:22 -0500239 tag = fdt_next_tag(fdt, offset, &nextoffset);
Gerald Van Baren35748172007-03-31 12:00:56 -0400240 switch (tag) {
241 case FDT_END:
242 err = -FDT_ERR_TRUNCATED;
243 goto fail;
244
245 case FDT_BEGIN_NODE:
Gerald Van Baren35748172007-03-31 12:00:56 -0400246 case FDT_END_NODE:
Kumar Gala8d04f022007-10-24 11:04:22 -0500247 case FDT_NOP:
Gerald Van Baren35748172007-03-31 12:00:56 -0400248 break;
249
250 case FDT_PROP:
Kumar Gala8d04f022007-10-24 11:04:22 -0500251 err = -FDT_ERR_BADSTRUCTURE;
252 prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
253 if (! prop)
Gerald Van Baren9675ee72007-05-17 23:54:36 -0400254 goto fail;
Kumar Gala8d04f022007-10-24 11:04:22 -0500255 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,
260 sizeof(*prop)+len);
261 if (! prop)
262 goto fail;
Gerald Van Baren35748172007-03-31 12:00:56 -0400263
Kumar Gala8d04f022007-10-24 11:04:22 -0500264 if (lenp)
265 *lenp = len;
266
267 return prop;
268 }
Gerald Van Baren35748172007-03-31 12:00:56 -0400269 break;
270
271 default:
272 err = -FDT_ERR_BADSTRUCTURE;
273 goto fail;
274 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500275 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
Gerald Van Baren35748172007-03-31 12:00:56 -0400276
277 err = -FDT_ERR_NOTFOUND;
Kumar Gala8d04f022007-10-24 11:04:22 -0500278 fail:
Gerald Van Baren35748172007-03-31 12:00:56 -0400279 if (lenp)
280 *lenp = err;
281 return NULL;
282}
283
Kumar Gala8d04f022007-10-24 11:04:22 -0500284const void *fdt_getprop(const void *fdt, int nodeoffset,
Gerald Van Baren35748172007-03-31 12:00:56 -0400285 const char *name, int *lenp)
286{
287 const struct fdt_property *prop;
288
289 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
290 if (! prop)
291 return NULL;
292
Kumar Gala8d04f022007-10-24 11:04:22 -0500293 return prop->data;
Gerald Van Baren35748172007-03-31 12:00:56 -0400294}
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400295
Kumar Gala8d04f022007-10-24 11:04:22 -0500296uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400297{
Kumar Gala8d04f022007-10-24 11:04:22 -0500298 const uint32_t *php;
299 int len;
300
301 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
302 if (!php || (len != sizeof(*php)))
303 return 0;
304
305 return fdt32_to_cpu(*php);
306}
307
308int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
309{
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400310 uint32_t tag;
Kumar Gala8d04f022007-10-24 11:04:22 -0500311 int p = 0, overflow = 0;
312 int offset, nextoffset, namelen;
313 const char *name;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400314
Kumar Gala8d04f022007-10-24 11:04:22 -0500315 CHECK_HEADER(fdt);
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400316
Kumar Gala8d04f022007-10-24 11:04:22 -0500317 tag = fdt_next_tag(fdt, 0, &nextoffset);
318 if (tag != FDT_BEGIN_NODE)
319 return -FDT_ERR_BADSTRUCTURE;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400320
Kumar Gala8d04f022007-10-24 11:04:22 -0500321 if (buflen < 2)
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400322 return -FDT_ERR_NOSPACE;
Kumar Gala8d04f022007-10-24 11:04:22 -0500323 buf[0] = '/';
324 p = 1;
325
326 while (nextoffset <= nodeoffset) {
327 offset = nextoffset;
328 tag = fdt_next_tag(fdt, offset, &nextoffset);
329 switch (tag) {
330 case FDT_END:
331 return -FDT_ERR_BADOFFSET;
332
333 case FDT_BEGIN_NODE:
334 name = fdt_get_name(fdt, offset, &namelen);
335 if (!name)
336 return namelen;
337 if (overflow || ((p + namelen + 1) > buflen)) {
338 overflow++;
339 break;
340 }
341 memcpy(buf + p, name, namelen);
342 p += namelen;
343 buf[p++] = '/';
344 break;
345
346 case FDT_END_NODE:
347 if (overflow) {
348 overflow--;
349 break;
350 }
351 do {
352 p--;
353 } while (buf[p-1] != '/');
354 break;
355
356 case FDT_PROP:
357 case FDT_NOP:
358 break;
359
360 default:
361 return -FDT_ERR_BADSTRUCTURE;
362 }
363 }
364
365 if (overflow)
366 return -FDT_ERR_NOSPACE;
367
368 if (p > 1) /* special case so that root path is "/", not "" */
369 p--;
370 buf[p] = '\0';
371 return p;
372}
373
374int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
375 int supernodedepth, int *nodedepth)
376{
377 int level = -1;
378 uint32_t tag;
379 int offset, nextoffset = 0;
380 int supernodeoffset = -FDT_ERR_INTERNAL;
381
382 CHECK_HEADER(fdt);
383
384 if (supernodedepth < 0)
385 return -FDT_ERR_NOTFOUND;
386
387 do {
388 offset = nextoffset;
389 tag = fdt_next_tag(fdt, offset, &nextoffset);
390 switch (tag) {
391 case FDT_END:
392 return -FDT_ERR_BADOFFSET;
393
394 case FDT_BEGIN_NODE:
395 level++;
396 if (level == supernodedepth)
397 supernodeoffset = offset;
398 break;
399
400 case FDT_END_NODE:
401 level--;
402 break;
403
404 case FDT_PROP:
405 case FDT_NOP:
406 break;
407
408 default:
409 return -FDT_ERR_BADSTRUCTURE;
410 }
411 } while (offset < nodeoffset);
412
413 if (nodedepth)
414 *nodedepth = level;
415
416 if (supernodedepth > level)
417 return -FDT_ERR_NOTFOUND;
418 return supernodeoffset;
419}
420
421int fdt_node_depth(const void *fdt, int nodeoffset)
422{
423 int nodedepth;
424 int err;
425
426 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
427 if (err)
428 return (err < 0) ? err : -FDT_ERR_INTERNAL;
429 return nodedepth;
430}
431
432int fdt_parent_offset(const void *fdt, int nodeoffset)
433{
434 int nodedepth = fdt_node_depth(fdt, nodeoffset);
435
436 if (nodedepth < 0)
437 return nodedepth;
438 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
439 nodedepth - 1, NULL);
440}
441
442int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
443 const char *propname,
444 const void *propval, int proplen)
445{
446 uint32_t tag;
447 int offset, nextoffset;
448 const void *val;
449 int len;
450
451 CHECK_HEADER(fdt);
452
453 if (startoffset >= 0) {
454 tag = fdt_next_tag(fdt, startoffset, &nextoffset);
455 if (tag != FDT_BEGIN_NODE)
456 return -FDT_ERR_BADOFFSET;
457 } else {
458 nextoffset = 0;
459 }
460
461 /* FIXME: The algorithm here is pretty horrible: we scan each
462 * property of a node in fdt_getprop(), then if that didn't
463 * find what we want, we scan over them again making our way
464 * to the next node. Still it's the easiest to implement
465 * approach; performance can come later. */
466 do {
467 offset = nextoffset;
468 tag = fdt_next_tag(fdt, offset, &nextoffset);
469
470 switch (tag) {
471 case FDT_BEGIN_NODE:
472 val = fdt_getprop(fdt, offset, propname, &len);
473 if (val
474 && (len == proplen)
475 && (memcmp(val, propval, len) == 0))
476 return offset;
477 break;
478
479 case FDT_PROP:
480 case FDT_END:
481 case FDT_END_NODE:
482 case FDT_NOP:
483 break;
484
485 default:
486 return -FDT_ERR_BADSTRUCTURE;
487 }
488 } while (tag != FDT_END);
489
490 return -FDT_ERR_NOTFOUND;
491}
492
493int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
494{
495 if ((phandle == 0) || (phandle == -1))
496 return -FDT_ERR_BADPHANDLE;
497 phandle = cpu_to_fdt32(phandle);
498 return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
499 &phandle, sizeof(phandle));
500}
501
502int _stringlist_contains(const void *strlist, int listlen, const char *str)
503{
504 int len = strlen(str);
505 const void *p;
506
507 while (listlen >= len) {
508 if (memcmp(str, strlist, len+1) == 0)
509 return 1;
510 p = memchr(strlist, '\0', listlen);
511 if (!p)
512 return 0; /* malformed strlist.. */
513 listlen -= (p-strlist) + 1;
514 strlist = p + 1;
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400515 }
516 return 0;
517}
Kumar Gala8d04f022007-10-24 11:04:22 -0500518
519int fdt_node_check_compatible(const void *fdt, int nodeoffset,
520 const char *compatible)
521{
522 const void *prop;
523 int len;
524
525 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
526 if (!prop)
527 return len;
528 if (_stringlist_contains(prop, len, compatible))
529 return 0;
530 else
531 return 1;
532}
533
534int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
535 const char *compatible)
536{
537 uint32_t tag;
538 int offset, nextoffset;
539 int err;
540
541 CHECK_HEADER(fdt);
542
543 if (startoffset >= 0) {
544 tag = fdt_next_tag(fdt, startoffset, &nextoffset);
545 if (tag != FDT_BEGIN_NODE)
546 return -FDT_ERR_BADOFFSET;
547 } else {
548 nextoffset = 0;
549 }
550
551 /* FIXME: The algorithm here is pretty horrible: we scan each
552 * property of a node in fdt_node_check_compatible(), then if
553 * that didn't find what we want, we scan over them again
554 * making our way to the next node. Still it's the easiest to
555 * implement approach; performance can come later. */
556 do {
557 offset = nextoffset;
558 tag = fdt_next_tag(fdt, offset, &nextoffset);
559
560 switch (tag) {
561 case FDT_BEGIN_NODE:
562 err = fdt_node_check_compatible(fdt, offset,
563 compatible);
564 if ((err < 0)
565 && (err != -FDT_ERR_NOTFOUND))
566 return err;
567 else if (err == 0)
568 return offset;
569 break;
570
571 case FDT_PROP:
572 case FDT_END:
573 case FDT_END_NODE:
574 case FDT_NOP:
575 break;
576
577 default:
578 return -FDT_ERR_BADSTRUCTURE;
579 }
580 } while (tag != FDT_END);
581
582 return -FDT_ERR_NOTFOUND;
583}