blob: 8dab7a8433f45d179faa874aa93cdb66258709bd [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 Gibson42369762006-12-01 15:07:19 +110061 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 Gibson73d60922006-12-15 15:12:47 +110085char *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 Gibson73d60922006-12-15 15:12:47 +110090int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +110091 const char *name, int namelen)
92{
93 int level = 0;
94 uint32_t tag;
95 int offset, nextoffset;
96
David Gibson9a9fdf52006-12-15 15:12:51 +110097 CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +110098
99 tag = _fdt_next_tag(fdt, parentoffset, &nextoffset);
100 if (tag != FDT_BEGIN_NODE)
David Gibson9a9fdf52006-12-15 15:12:51 +1100101 return -FDT_ERR_BADOFFSET;
David Gibson3da0f9a2006-11-27 16:21:28 +1100102
103 do {
104 offset = nextoffset;
105 tag = _fdt_next_tag(fdt, offset, &nextoffset);
106
107 switch (tag) {
108 case FDT_END:
David Gibson9a9fdf52006-12-15 15:12:51 +1100109 return -FDT_ERR_TRUNCATED;
David Gibson3da0f9a2006-11-27 16:21:28 +1100110
111 case FDT_BEGIN_NODE:
112 level++;
113 if (level != 1)
114 continue;
David Gibsond2a9da02007-09-28 15:51:04 +1000115 if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
David Gibson3da0f9a2006-11-27 16:21:28 +1100116 /* Found it! */
117 return offset;
118 break;
119
120 case FDT_END_NODE:
121 level--;
122 break;
123
124 case FDT_PROP:
125 case FDT_NOP:
126 break;
127
128 default:
David Gibson9a9fdf52006-12-15 15:12:51 +1100129 return -FDT_ERR_BADSTRUCTURE;
David Gibson3da0f9a2006-11-27 16:21:28 +1100130 }
131 } while (level >= 0);
132
David Gibson9a9fdf52006-12-15 15:12:51 +1100133 return -FDT_ERR_NOTFOUND;
David Gibson3da0f9a2006-11-27 16:21:28 +1100134}
135
David Gibson73d60922006-12-15 15:12:47 +1100136int fdt_subnode_offset(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100137 const char *name)
138{
139 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
140}
141
David Gibson73d60922006-12-15 15:12:47 +1100142int fdt_path_offset(const void *fdt, const char *path)
David Gibson3da0f9a2006-11-27 16:21:28 +1100143{
144 const char *end = path + strlen(path);
145 const char *p = path;
146 int offset = 0;
147
David Gibson9a9fdf52006-12-15 15:12:51 +1100148 CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +1100149
150 if (*path != '/')
David Gibson9a9fdf52006-12-15 15:12:51 +1100151 return -FDT_ERR_BADPATH;
David Gibson3da0f9a2006-11-27 16:21:28 +1100152
153 while (*p) {
154 const char *q;
155
156 while (*p == '/')
157 p++;
158 if (! *p)
David Gibsonbd2ae2f2007-08-29 12:22:50 +1000159 return offset;
David Gibson3da0f9a2006-11-27 16:21:28 +1100160 q = strchr(p, '/');
161 if (! q)
162 q = end;
163
164 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
David Gibson9a9fdf52006-12-15 15:12:51 +1100165 if (offset < 0)
David Gibson3da0f9a2006-11-27 16:21:28 +1100166 return offset;
167
168 p = q;
169 }
170
David Gibson63dc9c72007-09-18 11:44:04 +1000171 return offset;
David Gibson3da0f9a2006-11-27 16:21:28 +1100172}
173
David Gibson9d26eab2007-08-30 14:54:04 +1000174const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
175{
176 const struct fdt_node_header *nh;
177 int err;
178
179 if ((err = _fdt_check_header(fdt)) != 0)
180 goto fail;
181
182 err = -FDT_ERR_BADOFFSET;
183 nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
184 if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
185 goto fail;
186
187 if (len)
188 *len = strlen(nh->name);
189
190 return nh->name;
191
192 fail:
193 if (len)
194 *len = err;
195 return NULL;
196}
197
David Gibsona6c76f92007-06-13 14:18:10 +1000198const struct fdt_property *fdt_get_property(const void *fdt,
199 int nodeoffset,
200 const char *name, int *lenp)
David Gibson3da0f9a2006-11-27 16:21:28 +1100201{
David Gibson94993f42006-12-11 16:15:34 +1100202 uint32_t tag;
David Gibsona6c76f92007-06-13 14:18:10 +1000203 const struct fdt_property *prop;
David Gibson94993f42006-12-11 16:15:34 +1100204 int namestroff;
205 int offset, nextoffset;
David Gibsona7ee95d2006-12-15 15:12:49 +1100206 int err;
David Gibson3da0f9a2006-11-27 16:21:28 +1100207
David Gibsona7ee95d2006-12-15 15:12:49 +1100208 if ((err = _fdt_check_header(fdt)) != 0)
209 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100210
David Gibson9a9fdf52006-12-15 15:12:51 +1100211 err = -FDT_ERR_BADOFFSET;
David Gibson94993f42006-12-11 16:15:34 +1100212 if (nodeoffset % FDT_TAGSIZE)
David Gibsona7ee95d2006-12-15 15:12:49 +1100213 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100214
David Gibson94993f42006-12-11 16:15:34 +1100215 tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset);
216 if (tag != FDT_BEGIN_NODE)
David Gibsona7ee95d2006-12-15 15:12:49 +1100217 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100218
David Gibson94993f42006-12-11 16:15:34 +1100219 do {
220 offset = nextoffset;
David Gibson94993f42006-12-11 16:15:34 +1100221
222 tag = _fdt_next_tag(fdt, offset, &nextoffset);
223 switch (tag) {
224 case FDT_END:
David Gibson9a9fdf52006-12-15 15:12:51 +1100225 err = -FDT_ERR_TRUNCATED;
David Gibsona7ee95d2006-12-15 15:12:49 +1100226 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100227
228 case FDT_BEGIN_NODE:
David Gibson94993f42006-12-11 16:15:34 +1100229 case FDT_END_NODE:
David Gibson592ea582007-09-04 10:43:03 +1000230 case FDT_NOP:
David Gibson94993f42006-12-11 16:15:34 +1100231 break;
232
233 case FDT_PROP:
David Gibson9a9fdf52006-12-15 15:12:51 +1100234 err = -FDT_ERR_BADSTRUCTURE;
David Gibson94993f42006-12-11 16:15:34 +1100235 prop = fdt_offset_ptr_typed(fdt, offset, prop);
236 if (! prop)
David Gibsona7ee95d2006-12-15 15:12:49 +1100237 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100238 namestroff = fdt32_to_cpu(prop->nameoff);
239 if (streq(fdt_string(fdt, namestroff), name)) {
240 /* Found it! */
241 int len = fdt32_to_cpu(prop->len);
242 prop = fdt_offset_ptr(fdt, offset,
David Gibson9825f822006-12-14 15:29:25 +1100243 sizeof(*prop)+len);
David Gibson94993f42006-12-11 16:15:34 +1100244 if (! prop)
David Gibsona7ee95d2006-12-15 15:12:49 +1100245 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100246
247 if (lenp)
248 *lenp = len;
David Gibson63dc9c72007-09-18 11:44:04 +1000249
David Gibson94993f42006-12-11 16:15:34 +1100250 return prop;
251 }
252 break;
253
David Gibson94993f42006-12-11 16:15:34 +1100254 default:
David Gibson9a9fdf52006-12-15 15:12:51 +1100255 err = -FDT_ERR_BADSTRUCTURE;
David Gibsona7ee95d2006-12-15 15:12:49 +1100256 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100257 }
David Gibson592ea582007-09-04 10:43:03 +1000258 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
David Gibson94993f42006-12-11 16:15:34 +1100259
David Gibson9a9fdf52006-12-15 15:12:51 +1100260 err = -FDT_ERR_NOTFOUND;
David Gibsona7ee95d2006-12-15 15:12:49 +1100261 fail:
262 if (lenp)
David Gibson9a9fdf52006-12-15 15:12:51 +1100263 *lenp = err;
David Gibsona7ee95d2006-12-15 15:12:49 +1100264 return NULL;
David Gibson3da0f9a2006-11-27 16:21:28 +1100265}
266
David Gibsona6c76f92007-06-13 14:18:10 +1000267const void *fdt_getprop(const void *fdt, int nodeoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100268 const char *name, int *lenp)
269{
270 const struct fdt_property *prop;
David Gibson3da0f9a2006-11-27 16:21:28 +1100271
David Gibson94993f42006-12-11 16:15:34 +1100272 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
David Gibsona7ee95d2006-12-15 15:12:49 +1100273 if (! prop)
274 return NULL;
David Gibson3da0f9a2006-11-27 16:21:28 +1100275
276 return prop->data;
277}
David Gibson037db262007-08-30 14:54:04 +1000278
279int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
280{
281 uint32_t tag;
282 int p = 0, overflow = 0;
283 int offset, nextoffset, namelen;
284 const char *name;
285
286 CHECK_HEADER(fdt);
287
288 tag = _fdt_next_tag(fdt, 0, &nextoffset);
289 if (tag != FDT_BEGIN_NODE)
290 return -FDT_ERR_BADSTRUCTURE;
291
292 if (buflen < 2)
293 return -FDT_ERR_NOSPACE;
294 buf[0] = '/';
295 p = 1;
296
David Gibsone2b3bb32007-08-31 14:30:16 +1000297 while (nextoffset <= nodeoffset) {
David Gibson037db262007-08-30 14:54:04 +1000298 offset = nextoffset;
299 tag = _fdt_next_tag(fdt, offset, &nextoffset);
300 switch (tag) {
301 case FDT_END:
302 return -FDT_ERR_BADOFFSET;
303
304 case FDT_BEGIN_NODE:
305 name = fdt_get_name(fdt, offset, &namelen);
306 if (!name)
307 return namelen;
308 if (overflow || ((p + namelen + 1) > buflen)) {
309 overflow++;
310 break;
311 }
312 memcpy(buf + p, name, namelen);
313 p += namelen;
314 buf[p++] = '/';
315 break;
316
317 case FDT_END_NODE:
318 if (overflow) {
319 overflow--;
320 break;
321 }
322 do {
323 p--;
324 } while (buf[p-1] != '/');
325 break;
326
327 case FDT_PROP:
328 case FDT_NOP:
329 break;
330
331 default:
332 return -FDT_ERR_BADSTRUCTURE;
333 }
334 }
335
336 if (overflow)
337 return -FDT_ERR_NOSPACE;
338
339 if (p > 1) /* special case so that root path is "/", not "" */
340 p--;
341 buf[p] = '\0';
342 return p;
343}
David Gibson12482372007-08-30 14:54:04 +1000344
345int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
346 int supernodedepth, int *nodedepth)
347{
348 int level = -1;
349 uint32_t tag;
350 int offset, nextoffset = 0;
351 int supernodeoffset = -FDT_ERR_INTERNAL;
352
353 CHECK_HEADER(fdt);
354
355 if (supernodedepth < 0)
356 return -FDT_ERR_NOTFOUND;
357
358 do {
359 offset = nextoffset;
360 tag = _fdt_next_tag(fdt, offset, &nextoffset);
361 switch (tag) {
362 case FDT_END:
363 return -FDT_ERR_BADOFFSET;
364
365 case FDT_BEGIN_NODE:
366 level++;
367 if (level == supernodedepth)
368 supernodeoffset = offset;
369 break;
370
371 case FDT_END_NODE:
372 level--;
373 break;
374
375 case FDT_PROP:
376 case FDT_NOP:
377 break;
378
379 default:
380 return -FDT_ERR_BADSTRUCTURE;
381 }
382 } while (offset < nodeoffset);
383
384 if (nodedepth)
385 *nodedepth = level;
386
387 if (supernodedepth > level)
388 return -FDT_ERR_NOTFOUND;
389 return supernodeoffset;
390}
391
392int fdt_node_depth(const void *fdt, int nodeoffset)
393{
394 int nodedepth;
395 int err;
396
397 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
398 if (err)
399 return (err < 0) ? err : -FDT_ERR_INTERNAL;
400 return nodedepth;
401}
402
403int fdt_parent_offset(const void *fdt, int nodeoffset)
404{
405 int nodedepth = fdt_node_depth(fdt, nodeoffset);
406
407 if (nodedepth < 0)
408 return nodedepth;
409 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
410 nodedepth - 1, NULL);
411}
David Gibsonae1454b2007-09-17 14:28:34 +1000412
413int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
414 const char *propname,
415 const void *propval, int proplen)
416{
417 uint32_t tag;
418 int offset, nextoffset;
419 const void *val;
420 int len;
421
422 CHECK_HEADER(fdt);
423
424 if (startoffset >= 0) {
425 tag = _fdt_next_tag(fdt, startoffset, &nextoffset);
426 if (tag != FDT_BEGIN_NODE)
427 return -FDT_ERR_BADOFFSET;
428 } else {
429 nextoffset = 0;
430 }
431
432 /* FIXME: The algorithm here is pretty horrible: we scan each
433 * property of a node in fdt_getprop(), then if that didn't
434 * find what we want, we scan over them again making our way
435 * to the next node. Still it's the easiest to implement
436 * approach; performance can come later. */
437 do {
438 offset = nextoffset;
439 tag = _fdt_next_tag(fdt, offset, &nextoffset);
440
441 switch (tag) {
442 case FDT_BEGIN_NODE:
443 val = fdt_getprop(fdt, offset, propname, &len);
444 if (val
445 && (len == proplen)
446 && (memcmp(val, propval, len) == 0))
447 return offset;
448 break;
449
450 case FDT_PROP:
451 case FDT_END:
452 case FDT_END_NODE:
453 case FDT_NOP:
454 break;
455
456 default:
457 return -FDT_ERR_BADSTRUCTURE;
458 }
459 } while (tag != FDT_END);
460
461 return -FDT_ERR_NOTFOUND;
462}