blob: f08941a7adecb6ac23beeb310b075c845f19d382 [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{
David Gibsonae0b5902008-02-12 11:58:31 +110068 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
Gerald Van Baren35748172007-03-31 12:00:56 -040069
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
David Gibsonae0b5902008-02-12 11:58:31 +1100107int fdt_subnode_offset_namelen(const void *fdt, int offset,
Gerald Van Baren35748172007-03-31 12:00:56 -0400108 const char *name, int namelen)
109{
David Gibsonae0b5902008-02-12 11:58:31 +1100110 int depth;
Gerald Van Baren35748172007-03-31 12:00:56 -0400111
112 CHECK_HEADER(fdt);
113
David Gibsonae0b5902008-02-12 11:58:31 +1100114 for (depth = 0;
115 offset >= 0;
116 offset = fdt_next_node(fdt, offset, &depth)) {
117 if (depth < 0)
118 return -FDT_ERR_NOTFOUND;
119 else if ((depth == 1)
120 && nodename_eq(fdt, offset, name, namelen))
121 return offset;
122 }
Gerald Van Baren35748172007-03-31 12:00:56 -0400123
David Gibsonae0b5902008-02-12 11:58:31 +1100124 return offset; /* error */
Gerald Van Baren35748172007-03-31 12:00:56 -0400125}
126
127int fdt_subnode_offset(const void *fdt, int parentoffset,
128 const char *name)
129{
130 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
131}
132
Kumar Gala8d04f022007-10-24 11:04:22 -0500133int fdt_path_offset(const void *fdt, const char *path)
Gerald Van Baren35748172007-03-31 12:00:56 -0400134{
135 const char *end = path + strlen(path);
136 const char *p = path;
137 int offset = 0;
138
139 CHECK_HEADER(fdt);
140
141 if (*path != '/')
142 return -FDT_ERR_BADPATH;
143
144 while (*p) {
145 const char *q;
146
147 while (*p == '/')
148 p++;
149 if (! *p)
Kumar Gala8d04f022007-10-24 11:04:22 -0500150 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400151 q = strchr(p, '/');
152 if (! q)
153 q = end;
154
155 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
156 if (offset < 0)
157 return offset;
158
159 p = q;
160 }
161
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400162 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400163}
164
Kumar Gala8d04f022007-10-24 11:04:22 -0500165const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
Gerald Van Baren35748172007-03-31 12:00:56 -0400166{
Kumar Gala8d04f022007-10-24 11:04:22 -0500167 const struct fdt_node_header *nh;
168 int err;
169
170 if ((err = fdt_check_header(fdt)) != 0)
171 goto fail;
172
173 err = -FDT_ERR_BADOFFSET;
174 nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
175 if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
176 goto fail;
177
178 if (len)
179 *len = strlen(nh->name);
180
181 return nh->name;
182
183 fail:
184 if (len)
185 *len = err;
186 return NULL;
187}
188
189const struct fdt_property *fdt_get_property(const void *fdt,
190 int nodeoffset,
191 const char *name, int *lenp)
192{
Gerald Van Baren35748172007-03-31 12:00:56 -0400193 uint32_t tag;
Kumar Gala8d04f022007-10-24 11:04:22 -0500194 const struct fdt_property *prop;
195 int namestroff;
Gerald Van Baren35748172007-03-31 12:00:56 -0400196 int offset, nextoffset;
197 int err;
198
Gerald Van Baren6679f922007-04-06 14:17:14 -0400199 if ((err = fdt_check_header(fdt)) != 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400200 goto fail;
201
202 err = -FDT_ERR_BADOFFSET;
203 if (nodeoffset % FDT_TAGSIZE)
204 goto fail;
205
Kumar Gala8d04f022007-10-24 11:04:22 -0500206 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
Gerald Van Baren35748172007-03-31 12:00:56 -0400207 if (tag != FDT_BEGIN_NODE)
208 goto fail;
209
210 do {
211 offset = nextoffset;
212
Kumar Gala8d04f022007-10-24 11:04:22 -0500213 tag = fdt_next_tag(fdt, offset, &nextoffset);
Gerald Van Baren35748172007-03-31 12:00:56 -0400214 switch (tag) {
215 case FDT_END:
216 err = -FDT_ERR_TRUNCATED;
217 goto fail;
218
219 case FDT_BEGIN_NODE:
Gerald Van Baren35748172007-03-31 12:00:56 -0400220 case FDT_END_NODE:
Kumar Gala8d04f022007-10-24 11:04:22 -0500221 case FDT_NOP:
Gerald Van Baren35748172007-03-31 12:00:56 -0400222 break;
223
224 case FDT_PROP:
Kumar Gala8d04f022007-10-24 11:04:22 -0500225 err = -FDT_ERR_BADSTRUCTURE;
226 prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
227 if (! prop)
Gerald Van Baren9675ee72007-05-17 23:54:36 -0400228 goto fail;
Kumar Gala8d04f022007-10-24 11:04:22 -0500229 namestroff = fdt32_to_cpu(prop->nameoff);
230 if (streq(fdt_string(fdt, namestroff), name)) {
231 /* Found it! */
232 int len = fdt32_to_cpu(prop->len);
233 prop = fdt_offset_ptr(fdt, offset,
234 sizeof(*prop)+len);
235 if (! prop)
236 goto fail;
Gerald Van Baren35748172007-03-31 12:00:56 -0400237
Kumar Gala8d04f022007-10-24 11:04:22 -0500238 if (lenp)
239 *lenp = len;
240
241 return prop;
242 }
Gerald Van Baren35748172007-03-31 12:00:56 -0400243 break;
244
245 default:
246 err = -FDT_ERR_BADSTRUCTURE;
247 goto fail;
248 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500249 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
Gerald Van Baren35748172007-03-31 12:00:56 -0400250
251 err = -FDT_ERR_NOTFOUND;
Kumar Gala8d04f022007-10-24 11:04:22 -0500252 fail:
Gerald Van Baren35748172007-03-31 12:00:56 -0400253 if (lenp)
254 *lenp = err;
255 return NULL;
256}
257
Kumar Gala8d04f022007-10-24 11:04:22 -0500258const void *fdt_getprop(const void *fdt, int nodeoffset,
Gerald Van Baren35748172007-03-31 12:00:56 -0400259 const char *name, int *lenp)
260{
261 const struct fdt_property *prop;
262
263 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
264 if (! prop)
265 return NULL;
266
Kumar Gala8d04f022007-10-24 11:04:22 -0500267 return prop->data;
Gerald Van Baren35748172007-03-31 12:00:56 -0400268}
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400269
Kumar Gala8d04f022007-10-24 11:04:22 -0500270uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400271{
Kumar Gala8d04f022007-10-24 11:04:22 -0500272 const uint32_t *php;
273 int len;
274
275 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
276 if (!php || (len != sizeof(*php)))
277 return 0;
278
279 return fdt32_to_cpu(*php);
280}
281
282int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
283{
David Gibsonae0b5902008-02-12 11:58:31 +1100284 int pdepth = 0, p = 0;
285 int offset, depth, namelen;
Kumar Gala8d04f022007-10-24 11:04:22 -0500286 const char *name;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400287
Kumar Gala8d04f022007-10-24 11:04:22 -0500288 CHECK_HEADER(fdt);
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400289
Kumar Gala8d04f022007-10-24 11:04:22 -0500290 if (buflen < 2)
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400291 return -FDT_ERR_NOSPACE;
Kumar Gala8d04f022007-10-24 11:04:22 -0500292
David Gibsonae0b5902008-02-12 11:58:31 +1100293 for (offset = 0, depth = 0;
294 (offset >= 0) && (offset <= nodeoffset);
295 offset = fdt_next_node(fdt, offset, &depth)) {
296 if (pdepth < depth)
297 continue; /* overflowed buffer */
Kumar Gala8d04f022007-10-24 11:04:22 -0500298
David Gibsonae0b5902008-02-12 11:58:31 +1100299 while (pdepth > depth) {
300 do {
301 p--;
302 } while (buf[p-1] != '/');
303 pdepth--;
304 }
305
306 name = fdt_get_name(fdt, offset, &namelen);
307 if (!name)
308 return namelen;
309 if ((p + namelen + 1) <= buflen) {
Kumar Gala8d04f022007-10-24 11:04:22 -0500310 memcpy(buf + p, name, namelen);
311 p += namelen;
312 buf[p++] = '/';
David Gibsonae0b5902008-02-12 11:58:31 +1100313 pdepth++;
314 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500315
David Gibsonae0b5902008-02-12 11:58:31 +1100316 if (offset == nodeoffset) {
317 if (pdepth < (depth + 1))
318 return -FDT_ERR_NOSPACE;
319
320 if (p > 1) /* special case so that root path is "/", not "" */
Kumar Gala8d04f022007-10-24 11:04:22 -0500321 p--;
David Gibsonae0b5902008-02-12 11:58:31 +1100322 buf[p] = '\0';
323 return p;
Kumar Gala8d04f022007-10-24 11:04:22 -0500324 }
325 }
326
David Gibsonae0b5902008-02-12 11:58:31 +1100327 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
328 return -FDT_ERR_BADOFFSET;
329 else if (offset == -FDT_ERR_BADOFFSET)
330 return -FDT_ERR_BADSTRUCTURE;
Kumar Gala8d04f022007-10-24 11:04:22 -0500331
David Gibsonae0b5902008-02-12 11:58:31 +1100332 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500333}
334
335int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
336 int supernodedepth, int *nodedepth)
337{
David Gibsonae0b5902008-02-12 11:58:31 +1100338 int offset, depth;
Kumar Gala8d04f022007-10-24 11:04:22 -0500339 int supernodeoffset = -FDT_ERR_INTERNAL;
340
341 CHECK_HEADER(fdt);
342
343 if (supernodedepth < 0)
344 return -FDT_ERR_NOTFOUND;
345
David Gibsonae0b5902008-02-12 11:58:31 +1100346 for (offset = 0, depth = 0;
347 (offset >= 0) && (offset <= nodeoffset);
348 offset = fdt_next_node(fdt, offset, &depth)) {
349 if (depth == supernodedepth)
350 supernodeoffset = offset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500351
David Gibsonae0b5902008-02-12 11:58:31 +1100352 if (offset == nodeoffset) {
353 if (nodedepth)
354 *nodedepth = depth;
Kumar Gala8d04f022007-10-24 11:04:22 -0500355
David Gibsonae0b5902008-02-12 11:58:31 +1100356 if (supernodedepth > depth)
357 return -FDT_ERR_NOTFOUND;
358 else
359 return supernodeoffset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500360 }
David Gibsonae0b5902008-02-12 11:58:31 +1100361 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500362
David Gibsonae0b5902008-02-12 11:58:31 +1100363 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
364 return -FDT_ERR_BADOFFSET;
365 else if (offset == -FDT_ERR_BADOFFSET)
366 return -FDT_ERR_BADSTRUCTURE;
Kumar Gala8d04f022007-10-24 11:04:22 -0500367
David Gibsonae0b5902008-02-12 11:58:31 +1100368 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500369}
370
371int fdt_node_depth(const void *fdt, int nodeoffset)
372{
373 int nodedepth;
374 int err;
375
376 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
377 if (err)
378 return (err < 0) ? err : -FDT_ERR_INTERNAL;
379 return nodedepth;
380}
381
382int fdt_parent_offset(const void *fdt, int nodeoffset)
383{
384 int nodedepth = fdt_node_depth(fdt, nodeoffset);
385
386 if (nodedepth < 0)
387 return nodedepth;
388 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
389 nodedepth - 1, NULL);
390}
391
392int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
393 const char *propname,
394 const void *propval, int proplen)
395{
David Gibsonae0b5902008-02-12 11:58:31 +1100396 int offset;
Kumar Gala8d04f022007-10-24 11:04:22 -0500397 const void *val;
398 int len;
399
400 CHECK_HEADER(fdt);
401
Kumar Gala8d04f022007-10-24 11:04:22 -0500402 /* FIXME: The algorithm here is pretty horrible: we scan each
403 * property of a node in fdt_getprop(), then if that didn't
404 * find what we want, we scan over them again making our way
405 * to the next node. Still it's the easiest to implement
406 * approach; performance can come later. */
David Gibsonae0b5902008-02-12 11:58:31 +1100407 for (offset = fdt_next_node(fdt, startoffset, NULL);
408 offset >= 0;
409 offset = fdt_next_node(fdt, offset, NULL)) {
410 val = fdt_getprop(fdt, offset, propname, &len);
411 if (val && (len == proplen)
412 && (memcmp(val, propval, len) == 0))
413 return offset;
414 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500415
David Gibsonae0b5902008-02-12 11:58:31 +1100416 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500417}
418
419int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
420{
421 if ((phandle == 0) || (phandle == -1))
422 return -FDT_ERR_BADPHANDLE;
423 phandle = cpu_to_fdt32(phandle);
424 return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
425 &phandle, sizeof(phandle));
426}
427
428int _stringlist_contains(const void *strlist, int listlen, const char *str)
429{
430 int len = strlen(str);
431 const void *p;
432
433 while (listlen >= len) {
434 if (memcmp(str, strlist, len+1) == 0)
435 return 1;
436 p = memchr(strlist, '\0', listlen);
437 if (!p)
438 return 0; /* malformed strlist.. */
439 listlen -= (p-strlist) + 1;
440 strlist = p + 1;
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400441 }
442 return 0;
443}
Kumar Gala8d04f022007-10-24 11:04:22 -0500444
445int fdt_node_check_compatible(const void *fdt, int nodeoffset,
446 const char *compatible)
447{
448 const void *prop;
449 int len;
450
451 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
452 if (!prop)
453 return len;
454 if (_stringlist_contains(prop, len, compatible))
455 return 0;
456 else
457 return 1;
458}
459
460int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
461 const char *compatible)
462{
463 uint32_t tag;
464 int offset, nextoffset;
465 int err;
466
467 CHECK_HEADER(fdt);
468
469 if (startoffset >= 0) {
470 tag = fdt_next_tag(fdt, startoffset, &nextoffset);
471 if (tag != FDT_BEGIN_NODE)
472 return -FDT_ERR_BADOFFSET;
473 } else {
474 nextoffset = 0;
475 }
476
477 /* FIXME: The algorithm here is pretty horrible: we scan each
478 * property of a node in fdt_node_check_compatible(), then if
479 * that didn't find what we want, we scan over them again
480 * making our way to the next node. Still it's the easiest to
481 * implement approach; performance can come later. */
David Gibsonae0b5902008-02-12 11:58:31 +1100482 for (offset = fdt_next_node(fdt, startoffset, NULL);
483 offset >= 0;
484 offset = fdt_next_node(fdt, offset, NULL)) {
485 err = fdt_node_check_compatible(fdt, offset, compatible);
486 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
487 return err;
488 else if (err == 0)
489 return offset;
490 }
Kumar Gala8d04f022007-10-24 11:04:22 -0500491
David Gibsonae0b5902008-02-12 11:58:31 +1100492 return offset; /* error from fdt_next_node() */
Kumar Gala8d04f022007-10-24 11:04:22 -0500493}