blob: dc4a596c80404bbeff284064c469ebc51a4eb2ff [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 Gibson73d60922006-12-15 15:12:47 +110065static int offset_streq(const void *fdt, int offset,
David Gibson3da0f9a2006-11-27 16:21:28 +110066 const char *s, int len)
67{
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
77 if (p[len] != '\0')
78 return 0;
79
80 return 1;
81}
82
David Gibson73d60922006-12-15 15:12:47 +110083char *fdt_string(const void *fdt, int stroffset)
David Gibson3aa4cfd2006-11-29 16:34:30 +110084{
David Gibsonede25de2006-12-01 15:02:10 +110085 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
David Gibson3aa4cfd2006-11-29 16:34:30 +110086}
87
David Gibson73d60922006-12-15 15:12:47 +110088int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +110089 const char *name, int namelen)
90{
91 int level = 0;
92 uint32_t tag;
93 int offset, nextoffset;
94
David Gibson9a9fdf52006-12-15 15:12:51 +110095 CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +110096
97 tag = _fdt_next_tag(fdt, parentoffset, &nextoffset);
98 if (tag != FDT_BEGIN_NODE)
David Gibson9a9fdf52006-12-15 15:12:51 +110099 return -FDT_ERR_BADOFFSET;
David Gibson3da0f9a2006-11-27 16:21:28 +1100100
101 do {
102 offset = nextoffset;
103 tag = _fdt_next_tag(fdt, offset, &nextoffset);
104
105 switch (tag) {
106 case FDT_END:
David Gibson9a9fdf52006-12-15 15:12:51 +1100107 return -FDT_ERR_TRUNCATED;
David Gibson3da0f9a2006-11-27 16:21:28 +1100108
109 case FDT_BEGIN_NODE:
110 level++;
111 if (level != 1)
112 continue;
113 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
114 /* Found it! */
115 return offset;
116 break;
117
118 case FDT_END_NODE:
119 level--;
120 break;
121
122 case FDT_PROP:
123 case FDT_NOP:
124 break;
125
126 default:
David Gibson9a9fdf52006-12-15 15:12:51 +1100127 return -FDT_ERR_BADSTRUCTURE;
David Gibson3da0f9a2006-11-27 16:21:28 +1100128 }
129 } while (level >= 0);
130
David Gibson9a9fdf52006-12-15 15:12:51 +1100131 return -FDT_ERR_NOTFOUND;
David Gibson3da0f9a2006-11-27 16:21:28 +1100132}
133
David Gibson73d60922006-12-15 15:12:47 +1100134int fdt_subnode_offset(const void *fdt, int parentoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100135 const char *name)
136{
137 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
138}
139
David Gibson73d60922006-12-15 15:12:47 +1100140int fdt_path_offset(const void *fdt, const char *path)
David Gibson3da0f9a2006-11-27 16:21:28 +1100141{
142 const char *end = path + strlen(path);
143 const char *p = path;
144 int offset = 0;
145
David Gibson9a9fdf52006-12-15 15:12:51 +1100146 CHECK_HEADER(fdt);
David Gibson3da0f9a2006-11-27 16:21:28 +1100147
148 if (*path != '/')
David Gibson9a9fdf52006-12-15 15:12:51 +1100149 return -FDT_ERR_BADPATH;
David Gibson3da0f9a2006-11-27 16:21:28 +1100150
151 while (*p) {
152 const char *q;
153
154 while (*p == '/')
155 p++;
156 if (! *p)
David Gibsonbd2ae2f2007-08-29 12:22:50 +1000157 return offset;
David Gibson3da0f9a2006-11-27 16:21:28 +1100158 q = strchr(p, '/');
159 if (! q)
160 q = end;
161
162 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
David Gibson9a9fdf52006-12-15 15:12:51 +1100163 if (offset < 0)
David Gibson3da0f9a2006-11-27 16:21:28 +1100164 return offset;
165
166 p = q;
167 }
168
169 return offset;
170}
171
David Gibson9d26eab2007-08-30 14:54:04 +1000172const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
173{
174 const struct fdt_node_header *nh;
175 int err;
176
177 if ((err = _fdt_check_header(fdt)) != 0)
178 goto fail;
179
180 err = -FDT_ERR_BADOFFSET;
181 nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
182 if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
183 goto fail;
184
185 if (len)
186 *len = strlen(nh->name);
187
188 return nh->name;
189
190 fail:
191 if (len)
192 *len = err;
193 return NULL;
194}
195
David Gibsona6c76f92007-06-13 14:18:10 +1000196const struct fdt_property *fdt_get_property(const void *fdt,
197 int nodeoffset,
198 const char *name, int *lenp)
David Gibson3da0f9a2006-11-27 16:21:28 +1100199{
David Gibson94993f42006-12-11 16:15:34 +1100200 int level = 0;
201 uint32_t tag;
David Gibsona6c76f92007-06-13 14:18:10 +1000202 const struct fdt_property *prop;
David Gibson94993f42006-12-11 16:15:34 +1100203 int namestroff;
204 int offset, nextoffset;
David Gibsona7ee95d2006-12-15 15:12:49 +1100205 int err;
David Gibson3da0f9a2006-11-27 16:21:28 +1100206
David Gibsona7ee95d2006-12-15 15:12:49 +1100207 if ((err = _fdt_check_header(fdt)) != 0)
208 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100209
David Gibson9a9fdf52006-12-15 15:12:51 +1100210 err = -FDT_ERR_BADOFFSET;
David Gibson94993f42006-12-11 16:15:34 +1100211 if (nodeoffset % FDT_TAGSIZE)
David Gibsona7ee95d2006-12-15 15:12:49 +1100212 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100213
David Gibson94993f42006-12-11 16:15:34 +1100214 tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset);
215 if (tag != FDT_BEGIN_NODE)
David Gibsona7ee95d2006-12-15 15:12:49 +1100216 goto fail;
David Gibson3da0f9a2006-11-27 16:21:28 +1100217
David Gibson94993f42006-12-11 16:15:34 +1100218 do {
219 offset = nextoffset;
David Gibson94993f42006-12-11 16:15:34 +1100220
221 tag = _fdt_next_tag(fdt, offset, &nextoffset);
222 switch (tag) {
223 case FDT_END:
David Gibson9a9fdf52006-12-15 15:12:51 +1100224 err = -FDT_ERR_TRUNCATED;
David Gibsona7ee95d2006-12-15 15:12:49 +1100225 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100226
227 case FDT_BEGIN_NODE:
228 level++;
229 break;
230
231 case FDT_END_NODE:
232 level--;
233 break;
234
235 case FDT_PROP:
236 if (level != 0)
237 continue;
238
David Gibson9a9fdf52006-12-15 15:12:51 +1100239 err = -FDT_ERR_BADSTRUCTURE;
David Gibson94993f42006-12-11 16:15:34 +1100240 prop = fdt_offset_ptr_typed(fdt, offset, prop);
241 if (! prop)
David Gibsona7ee95d2006-12-15 15:12:49 +1100242 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100243 namestroff = fdt32_to_cpu(prop->nameoff);
244 if (streq(fdt_string(fdt, namestroff), name)) {
245 /* Found it! */
246 int len = fdt32_to_cpu(prop->len);
247 prop = fdt_offset_ptr(fdt, offset,
David Gibson9825f822006-12-14 15:29:25 +1100248 sizeof(*prop)+len);
David Gibson94993f42006-12-11 16:15:34 +1100249 if (! prop)
David Gibsona7ee95d2006-12-15 15:12:49 +1100250 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100251
252 if (lenp)
253 *lenp = len;
254
255 return prop;
256 }
257 break;
258
259 case FDT_NOP:
260 break;
261
262 default:
David Gibson9a9fdf52006-12-15 15:12:51 +1100263 err = -FDT_ERR_BADSTRUCTURE;
David Gibsona7ee95d2006-12-15 15:12:49 +1100264 goto fail;
David Gibson94993f42006-12-11 16:15:34 +1100265 }
266 } while (level >= 0);
267
David Gibson9a9fdf52006-12-15 15:12:51 +1100268 err = -FDT_ERR_NOTFOUND;
David Gibsona7ee95d2006-12-15 15:12:49 +1100269 fail:
270 if (lenp)
David Gibson9a9fdf52006-12-15 15:12:51 +1100271 *lenp = err;
David Gibsona7ee95d2006-12-15 15:12:49 +1100272 return NULL;
David Gibson3da0f9a2006-11-27 16:21:28 +1100273}
274
David Gibsona6c76f92007-06-13 14:18:10 +1000275const void *fdt_getprop(const void *fdt, int nodeoffset,
David Gibson3da0f9a2006-11-27 16:21:28 +1100276 const char *name, int *lenp)
277{
278 const struct fdt_property *prop;
David Gibson3da0f9a2006-11-27 16:21:28 +1100279
David Gibson94993f42006-12-11 16:15:34 +1100280 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
David Gibsona7ee95d2006-12-15 15:12:49 +1100281 if (! prop)
282 return NULL;
David Gibson3da0f9a2006-11-27 16:21:28 +1100283
284 return prop->data;
285}
David Gibson037db262007-08-30 14:54:04 +1000286
287int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
288{
289 uint32_t tag;
290 int p = 0, overflow = 0;
291 int offset, nextoffset, namelen;
292 const char *name;
293
294 CHECK_HEADER(fdt);
295
296 tag = _fdt_next_tag(fdt, 0, &nextoffset);
297 if (tag != FDT_BEGIN_NODE)
298 return -FDT_ERR_BADSTRUCTURE;
299
300 if (buflen < 2)
301 return -FDT_ERR_NOSPACE;
302 buf[0] = '/';
303 p = 1;
304
305 while (offset < nodeoffset) {
306 offset = nextoffset;
307 tag = _fdt_next_tag(fdt, offset, &nextoffset);
308 switch (tag) {
309 case FDT_END:
310 return -FDT_ERR_BADOFFSET;
311
312 case FDT_BEGIN_NODE:
313 name = fdt_get_name(fdt, offset, &namelen);
314 if (!name)
315 return namelen;
316 if (overflow || ((p + namelen + 1) > buflen)) {
317 overflow++;
318 break;
319 }
320 memcpy(buf + p, name, namelen);
321 p += namelen;
322 buf[p++] = '/';
323 break;
324
325 case FDT_END_NODE:
326 if (overflow) {
327 overflow--;
328 break;
329 }
330 do {
331 p--;
332 } while (buf[p-1] != '/');
333 break;
334
335 case FDT_PROP:
336 case FDT_NOP:
337 break;
338
339 default:
340 return -FDT_ERR_BADSTRUCTURE;
341 }
342 }
343
344 if (overflow)
345 return -FDT_ERR_NOSPACE;
346
347 if (p > 1) /* special case so that root path is "/", not "" */
348 p--;
349 buf[p] = '\0';
350 return p;
351}
David Gibson12482372007-08-30 14:54:04 +1000352
353int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
354 int supernodedepth, int *nodedepth)
355{
356 int level = -1;
357 uint32_t tag;
358 int offset, nextoffset = 0;
359 int supernodeoffset = -FDT_ERR_INTERNAL;
360
361 CHECK_HEADER(fdt);
362
363 if (supernodedepth < 0)
364 return -FDT_ERR_NOTFOUND;
365
366 do {
367 offset = nextoffset;
368 tag = _fdt_next_tag(fdt, offset, &nextoffset);
369 switch (tag) {
370 case FDT_END:
371 return -FDT_ERR_BADOFFSET;
372
373 case FDT_BEGIN_NODE:
374 level++;
375 if (level == supernodedepth)
376 supernodeoffset = offset;
377 break;
378
379 case FDT_END_NODE:
380 level--;
381 break;
382
383 case FDT_PROP:
384 case FDT_NOP:
385 break;
386
387 default:
388 return -FDT_ERR_BADSTRUCTURE;
389 }
390 } while (offset < nodeoffset);
391
392 if (nodedepth)
393 *nodedepth = level;
394
395 if (supernodedepth > level)
396 return -FDT_ERR_NOTFOUND;
397 return supernodeoffset;
398}
399
400int fdt_node_depth(const void *fdt, int nodeoffset)
401{
402 int nodedepth;
403 int err;
404
405 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
406 if (err)
407 return (err < 0) ? err : -FDT_ERR_INTERNAL;
408 return nodedepth;
409}
410
411int fdt_parent_offset(const void *fdt, int nodeoffset)
412{
413 int nodedepth = fdt_node_depth(fdt, nodeoffset);
414
415 if (nodedepth < 0)
416 return nodedepth;
417 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
418 nodedepth - 1, NULL);
419}