Committer: Michael Beasley <mike@snafu.setup>
[mikesnafu-overlay.git] / arch / powerpc / boot / libfdt / fdt_ro.c
blob12a37d59f96eba267235cf06962f7ccc7a6c2259
1 /*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
5 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
8 * 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.
13 * 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.
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
23 * Alternatively,
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:
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.
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.
51 #include "libfdt_env.h"
53 #include <fdt.h>
54 #include <libfdt.h>
56 #include "libfdt_internal.h"
58 #define CHECK_HEADER(fdt) \
59 { \
60 int err; \
61 if ((err = fdt_check_header(fdt)) != 0) \
62 return err; \
65 static int nodename_eq(const void *fdt, int offset,
66 const char *s, int len)
68 const char *p = fdt_offset_ptr(fdt, offset, len+1);
70 if (! p)
71 /* short match */
72 return 0;
74 if (memcmp(p, s, len) != 0)
75 return 0;
77 if (p[len] == '\0')
78 return 1;
79 else if (!memchr(s, '@', len) && (p[len] == '@'))
80 return 1;
81 else
82 return 0;
85 const char *fdt_string(const void *fdt, int stroffset)
87 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
90 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
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;
98 int fdt_num_mem_rsv(const void *fdt)
100 int i = 0;
102 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
103 i++;
104 return i;
107 int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
108 const char *name, int namelen)
110 int level = 0;
111 uint32_t tag;
112 int offset, nextoffset;
114 CHECK_HEADER(fdt);
116 tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
117 if (tag != FDT_BEGIN_NODE)
118 return -FDT_ERR_BADOFFSET;
120 do {
121 offset = nextoffset;
122 tag = fdt_next_tag(fdt, offset, &nextoffset);
124 switch (tag) {
125 case FDT_END:
126 return -FDT_ERR_TRUNCATED;
128 case FDT_BEGIN_NODE:
129 level++;
130 if (level != 1)
131 continue;
132 if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
133 /* Found it! */
134 return offset;
135 break;
137 case FDT_END_NODE:
138 level--;
139 break;
141 case FDT_PROP:
142 case FDT_NOP:
143 break;
145 default:
146 return -FDT_ERR_BADSTRUCTURE;
148 } while (level >= 0);
150 return -FDT_ERR_NOTFOUND;
153 int fdt_subnode_offset(const void *fdt, int parentoffset,
154 const char *name)
156 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
159 int fdt_path_offset(const void *fdt, const char *path)
161 const char *end = path + strlen(path);
162 const char *p = path;
163 int offset = 0;
165 CHECK_HEADER(fdt);
167 if (*path != '/')
168 return -FDT_ERR_BADPATH;
170 while (*p) {
171 const char *q;
173 while (*p == '/')
174 p++;
175 if (! *p)
176 return offset;
177 q = strchr(p, '/');
178 if (! q)
179 q = end;
181 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
182 if (offset < 0)
183 return offset;
185 p = q;
188 return offset;
191 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
193 const struct fdt_node_header *nh;
194 int err;
196 if ((err = fdt_check_header(fdt)) != 0)
197 goto fail;
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;
204 if (len)
205 *len = strlen(nh->name);
207 return nh->name;
209 fail:
210 if (len)
211 *len = err;
212 return NULL;
215 const struct fdt_property *fdt_get_property(const void *fdt,
216 int nodeoffset,
217 const char *name, int *lenp)
219 uint32_t tag;
220 const struct fdt_property *prop;
221 int namestroff;
222 int offset, nextoffset;
223 int err;
225 if ((err = fdt_check_header(fdt)) != 0)
226 goto fail;
228 err = -FDT_ERR_BADOFFSET;
229 if (nodeoffset % FDT_TAGSIZE)
230 goto fail;
232 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
233 if (tag != FDT_BEGIN_NODE)
234 goto fail;
236 do {
237 offset = nextoffset;
239 tag = fdt_next_tag(fdt, offset, &nextoffset);
240 switch (tag) {
241 case FDT_END:
242 err = -FDT_ERR_TRUNCATED;
243 goto fail;
245 case FDT_BEGIN_NODE:
246 case FDT_END_NODE:
247 case FDT_NOP:
248 break;
250 case FDT_PROP:
251 err = -FDT_ERR_BADSTRUCTURE;
252 prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
253 if (! prop)
254 goto fail;
255 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;
264 if (lenp)
265 *lenp = len;
267 return prop;
269 break;
271 default:
272 err = -FDT_ERR_BADSTRUCTURE;
273 goto fail;
275 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
277 err = -FDT_ERR_NOTFOUND;
278 fail:
279 if (lenp)
280 *lenp = err;
281 return NULL;
284 const void *fdt_getprop(const void *fdt, int nodeoffset,
285 const char *name, int *lenp)
287 const struct fdt_property *prop;
289 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
290 if (! prop)
291 return NULL;
293 return prop->data;
296 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
298 const uint32_t *php;
299 int len;
301 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
302 if (!php || (len != sizeof(*php)))
303 return 0;
305 return fdt32_to_cpu(*php);
308 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
310 uint32_t tag;
311 int p = 0, overflow = 0;
312 int offset, nextoffset, namelen;
313 const char *name;
315 CHECK_HEADER(fdt);
317 tag = fdt_next_tag(fdt, 0, &nextoffset);
318 if (tag != FDT_BEGIN_NODE)
319 return -FDT_ERR_BADSTRUCTURE;
321 if (buflen < 2)
322 return -FDT_ERR_NOSPACE;
323 buf[0] = '/';
324 p = 1;
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;
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;
341 memcpy(buf + p, name, namelen);
342 p += namelen;
343 buf[p++] = '/';
344 break;
346 case FDT_END_NODE:
347 if (overflow) {
348 overflow--;
349 break;
351 do {
352 p--;
353 } while (buf[p-1] != '/');
354 break;
356 case FDT_PROP:
357 case FDT_NOP:
358 break;
360 default:
361 return -FDT_ERR_BADSTRUCTURE;
365 if (overflow)
366 return -FDT_ERR_NOSPACE;
368 if (p > 1) /* special case so that root path is "/", not "" */
369 p--;
370 buf[p] = '\0';
371 return p;
374 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
375 int supernodedepth, int *nodedepth)
377 int level = -1;
378 uint32_t tag;
379 int offset, nextoffset = 0;
380 int supernodeoffset = -FDT_ERR_INTERNAL;
382 CHECK_HEADER(fdt);
384 if (supernodedepth < 0)
385 return -FDT_ERR_NOTFOUND;
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;
394 case FDT_BEGIN_NODE:
395 level++;
396 if (level == supernodedepth)
397 supernodeoffset = offset;
398 break;
400 case FDT_END_NODE:
401 level--;
402 break;
404 case FDT_PROP:
405 case FDT_NOP:
406 break;
408 default:
409 return -FDT_ERR_BADSTRUCTURE;
411 } while (offset < nodeoffset);
413 if (nodedepth)
414 *nodedepth = level;
416 if (supernodedepth > level)
417 return -FDT_ERR_NOTFOUND;
418 return supernodeoffset;
421 int fdt_node_depth(const void *fdt, int nodeoffset)
423 int nodedepth;
424 int err;
426 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
427 if (err)
428 return (err < 0) ? err : -FDT_ERR_INTERNAL;
429 return nodedepth;
432 int fdt_parent_offset(const void *fdt, int nodeoffset)
434 int nodedepth = fdt_node_depth(fdt, nodeoffset);
436 if (nodedepth < 0)
437 return nodedepth;
438 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
439 nodedepth - 1, NULL);
442 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
443 const char *propname,
444 const void *propval, int proplen)
446 uint32_t tag;
447 int offset, nextoffset;
448 const void *val;
449 int len;
451 CHECK_HEADER(fdt);
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;
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);
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;
479 case FDT_PROP:
480 case FDT_END:
481 case FDT_END_NODE:
482 case FDT_NOP:
483 break;
485 default:
486 return -FDT_ERR_BADSTRUCTURE;
488 } while (tag != FDT_END);
490 return -FDT_ERR_NOTFOUND;
493 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
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));
502 int _stringlist_contains(const void *strlist, int listlen, const char *str)
504 int len = strlen(str);
505 const void *p;
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;
516 return 0;
519 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
520 const char *compatible)
522 const void *prop;
523 int len;
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;
534 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
535 const char *compatible)
537 uint32_t tag;
538 int offset, nextoffset;
539 int err;
541 CHECK_HEADER(fdt);
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;
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);
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;
571 case FDT_PROP:
572 case FDT_END:
573 case FDT_END_NODE:
574 case FDT_NOP:
575 break;
577 default:
578 return -FDT_ERR_BADSTRUCTURE;
580 } while (tag != FDT_END);
582 return -FDT_ERR_NOTFOUND;