blob: 46d525db1453ad29caad64890039008a373754af [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 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
Gerald Van Baren8096b3b2007-04-20 22:46:53 -040019#include "config.h"
20#if CONFIG_OF_LIBFDT
21
Gerald Van Baren35748172007-03-31 12:00:56 -040022#include "libfdt_env.h"
23
24#include <fdt.h>
25#include <libfdt.h>
26
27#include "libfdt_internal.h"
28
Wolfgang Denk94abd7c2007-04-04 01:49:15 +020029#define CHECK_HEADER(fdt) { \
30 int err; \
Gerald Van Baren6679f922007-04-06 14:17:14 -040031 if ((err = fdt_check_header(fdt)) != 0) \
Wolfgang Denk94abd7c2007-04-04 01:49:15 +020032 return err; \
33}
Gerald Van Baren35748172007-03-31 12:00:56 -040034
35static int offset_streq(const void *fdt, int offset,
36 const char *s, int len)
37{
38 const char *p = fdt_offset_ptr(fdt, offset, len+1);
39
40 if (! p)
41 /* short match */
42 return 0;
43
44 if (memcmp(p, s, len) != 0)
45 return 0;
46
47 if (p[len] != '\0')
48 return 0;
49
50 return 1;
51}
52
Gerald Van Baren3af0d582007-03-31 12:13:43 -040053/*
Gerald Van Baren9675ee72007-05-17 23:54:36 -040054 * Checks if the property name matches.
55 */
56static int prop_name_eq(const void *fdt, int offset, const char *name,
57 struct fdt_property **prop, int *lenp)
58{
59 int namestroff, len;
60
61 *prop = fdt_offset_ptr_typed(fdt, offset, *prop);
62 if (! *prop)
63 return -FDT_ERR_BADSTRUCTURE;
64
65 namestroff = fdt32_to_cpu((*prop)->nameoff);
66 if (streq(fdt_string(fdt, namestroff), name)) {
67 len = fdt32_to_cpu((*prop)->len);
68 *prop = fdt_offset_ptr(fdt, offset,
69 sizeof(**prop) + len);
70 if (*prop) {
71 if (lenp)
72 *lenp = len;
73 return 1;
74 } else
75 return -FDT_ERR_BADSTRUCTURE;
76 }
77 return 0;
78}
79
80/*
Gerald Van Baren3af0d582007-03-31 12:13:43 -040081 * Return a pointer to the string at the given string offset.
82 */
Gerald Van Baren35748172007-03-31 12:00:56 -040083char *fdt_string(const void *fdt, int stroffset)
84{
85 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
86}
87
Gerald Van Baren3af0d582007-03-31 12:13:43 -040088/*
Gerald Van Baren9675ee72007-05-17 23:54:36 -040089 * Check if the specified node is compatible by comparing the tokens
90 * in its "compatible" property with the specified string:
91 *
92 * nodeoffset - starting place of the node
93 * compat - the string to match to one of the tokens in the
94 * "compatible" list.
95 */
96int fdt_node_is_compatible(const void *fdt, int nodeoffset,
97 const char *compat)
98{
99 const char* cp;
100 int cplen, len;
101
102 cp = fdt_getprop(fdt, nodeoffset, "compatible", &cplen);
103 if (cp == NULL)
104 return 0;
105 while (cplen > 0) {
106 if (strncmp(cp, compat, strlen(compat)) == 0)
107 return 1;
108 len = strlen(cp) + 1;
109 cp += len;
110 cplen -= len;
111 }
112
113 return 0;
114}
115
116/*
117 * Find a node by its device type property. On success, the offset of that
118 * node is returned or an error code otherwise:
119 *
120 * nodeoffset - the node to start searching from or 0, the node you pass
121 * will not be searched, only the next one will; typically,
122 * you pass 0 to start the search and then what the previous
123 * call returned.
124 * type - the device type string to match against.
125 */
126int fdt_find_node_by_type(const void *fdt, int nodeoffset, const char *type)
127{
128 int offset, nextoffset;
129 struct fdt_property *prop;
130 uint32_t tag;
131 int len, ret;
132
133 CHECK_HEADER(fdt);
134
135 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
136 if (tag != FDT_BEGIN_NODE)
137 return -FDT_ERR_BADOFFSET;
138 if (nodeoffset)
139 nodeoffset = 0; /* start searching with next node */
140
141 while (1) {
142 offset = nextoffset;
143 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
144
145 switch (tag) {
146 case FDT_BEGIN_NODE:
147 nodeoffset = offset;
148 break;
149
150 case FDT_PROP:
151 if (nodeoffset == 0)
152 break;
153 ret = prop_name_eq(fdt, offset, "device_type",
154 &prop, &len);
155 if (ret < 0)
156 return ret;
157 else if (ret > 0 &&
158 strncmp(prop->data, type, len - 1) == 0)
159 return nodeoffset;
160 break;
161
162 case FDT_END_NODE:
163 case FDT_NOP:
164 break;
165
166 case FDT_END:
167 return -FDT_ERR_NOTFOUND;
168
169 default:
170 return -FDT_ERR_BADSTRUCTURE;
171 }
172 }
173}
174
175/*
176 * Find a node based on its device type and one of the tokens in its its
177 * "compatible" property. On success, the offset of that node is returned
178 * or an error code otherwise:
179 *
180 * nodeoffset - the node to start searching from or 0, the node you pass
181 * will not be searched, only the next one will; typically,
182 * you pass 0 to start the search and then what the previous
183 * call returned.
184 * type - the device type string to match against.
185 * compat - the string to match to one of the tokens in the
186 * "compatible" list.
187 */
188int fdt_find_compatible_node(const void *fdt, int nodeoffset,
189 const char *type, const char *compat)
190{
191 int offset;
192
193 offset = fdt_find_node_by_type(fdt, nodeoffset, type);
194 if (offset < 0 || fdt_node_is_compatible(fdt, offset, compat))
195 return offset;
196
197 return -FDT_ERR_NOTFOUND;
198}
199
200/*
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400201 * Return the node offset of the node specified by:
202 * parentoffset - starting place (0 to start at the root)
203 * name - name being searched for
204 * namelen - length of the name: typically strlen(name)
205 *
206 * Notes:
207 * If the start node has subnodes, the subnodes are _not_ searched for the
208 * requested name.
209 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400210int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
211 const char *name, int namelen)
212{
213 int level = 0;
214 uint32_t tag;
215 int offset, nextoffset;
216
217 CHECK_HEADER(fdt);
218
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400219 tag = fdt_next_tag(fdt, parentoffset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400220 if (tag != FDT_BEGIN_NODE)
221 return -FDT_ERR_BADOFFSET;
222
223 do {
224 offset = nextoffset;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400225 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400226
227 switch (tag) {
228 case FDT_END:
229 return -FDT_ERR_TRUNCATED;
230
231 case FDT_BEGIN_NODE:
232 level++;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400233 /*
234 * If we are nested down levels, ignore the strings
235 * until we get back to the proper level.
236 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400237 if (level != 1)
238 continue;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400239
240 /* Return the offset if this is "our" string. */
Gerald Van Baren35748172007-03-31 12:00:56 -0400241 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
Gerald Van Baren35748172007-03-31 12:00:56 -0400242 return offset;
243 break;
244
245 case FDT_END_NODE:
246 level--;
247 break;
248
249 case FDT_PROP:
250 case FDT_NOP:
251 break;
252
253 default:
254 return -FDT_ERR_BADSTRUCTURE;
255 }
256 } while (level >= 0);
257
258 return -FDT_ERR_NOTFOUND;
259}
260
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400261/*
262 * See fdt_subnode_offset_namelen()
263 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400264int fdt_subnode_offset(const void *fdt, int parentoffset,
265 const char *name)
266{
267 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
268}
269
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400270/*
271 * Searches for the node corresponding to the given path and returns the
272 * offset of that node.
273 */
Gerald Van Baren1a861162007-06-06 22:47:58 -0400274int fdt_find_node_by_path(const void *fdt, const char *path)
Gerald Van Baren35748172007-03-31 12:00:56 -0400275{
276 const char *end = path + strlen(path);
277 const char *p = path;
278 int offset = 0;
279
280 CHECK_HEADER(fdt);
281
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400282 /* Paths must be absolute */
Gerald Van Baren35748172007-03-31 12:00:56 -0400283 if (*path != '/')
284 return -FDT_ERR_BADPATH;
285
Gerald Van Baren06e19a02007-05-21 23:27:16 -0400286 /* Handle the root path: root offset is 0 */
287 if (strcmp(path, "/") == 0)
288 return 0;
289
Gerald Van Baren35748172007-03-31 12:00:56 -0400290 while (*p) {
291 const char *q;
292
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400293 /* Skip path separator(s) */
Gerald Van Baren35748172007-03-31 12:00:56 -0400294 while (*p == '/')
295 p++;
296 if (! *p)
297 return -FDT_ERR_BADPATH;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400298
299 /*
300 * Find the next path separator. The characters between
301 * p and q are the next segment of the the path to find.
302 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400303 q = strchr(p, '/');
304 if (! q)
305 q = end;
306
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400307 /*
308 * Find the offset corresponding to the this path segment.
309 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400310 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400311
312 /* Oops, error, abort abort abort */
Gerald Van Baren35748172007-03-31 12:00:56 -0400313 if (offset < 0)
314 return offset;
315
316 p = q;
317 }
318
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400319 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400320}
321
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400322/*
323 * Given the offset of a node and a name of a property in that node, return
324 * a pointer to the property struct.
325 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400326struct fdt_property *fdt_get_property(const void *fdt,
327 int nodeoffset,
328 const char *name, int *lenp)
329{
330 int level = 0;
331 uint32_t tag;
332 struct fdt_property *prop;
Gerald Van Baren35748172007-03-31 12:00:56 -0400333 int offset, nextoffset;
334 int err;
335
Gerald Van Baren6679f922007-04-06 14:17:14 -0400336 if ((err = fdt_check_header(fdt)) != 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400337 goto fail;
338
339 err = -FDT_ERR_BADOFFSET;
340 if (nodeoffset % FDT_TAGSIZE)
341 goto fail;
342
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400343 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400344 if (tag != FDT_BEGIN_NODE)
345 goto fail;
346
347 do {
348 offset = nextoffset;
349
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400350 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400351 switch (tag) {
352 case FDT_END:
353 err = -FDT_ERR_TRUNCATED;
354 goto fail;
355
356 case FDT_BEGIN_NODE:
357 level++;
358 break;
359
360 case FDT_END_NODE:
361 level--;
362 break;
363
364 case FDT_PROP:
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400365 /*
366 * If we are nested down levels, ignore the strings
367 * until we get back to the proper level.
368 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400369 if (level != 0)
370 continue;
371
Gerald Van Baren9675ee72007-05-17 23:54:36 -0400372 err = prop_name_eq(fdt, offset, name, &prop, lenp);
373 if (err > 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400374 return prop;
Gerald Van Baren9675ee72007-05-17 23:54:36 -0400375 else if (err < 0)
376 goto fail;
Gerald Van Baren35748172007-03-31 12:00:56 -0400377 break;
378
379 case FDT_NOP:
380 break;
381
382 default:
383 err = -FDT_ERR_BADSTRUCTURE;
384 goto fail;
385 }
386 } while (level >= 0);
387
388 err = -FDT_ERR_NOTFOUND;
Wolfgang Denk94abd7c2007-04-04 01:49:15 +0200389fail:
Gerald Van Baren35748172007-03-31 12:00:56 -0400390 if (lenp)
391 *lenp = err;
392 return NULL;
393}
394
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400395/*
396 * Given the offset of a node and a name of a property in that node, return
397 * a pointer to the property data (ONLY).
398 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400399void *fdt_getprop(const void *fdt, int nodeoffset,
400 const char *name, int *lenp)
401{
402 const struct fdt_property *prop;
403
404 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
405 if (! prop)
406 return NULL;
407
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400408 return (void *)prop->data;
Gerald Van Baren35748172007-03-31 12:00:56 -0400409}
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400410
411
412uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset, char **namep)
413{
414 const uint32_t *tagp, *lenp;
415 uint32_t tag;
416 const char *p;
417
418 if (offset % FDT_TAGSIZE)
419 return -1;
420
421 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
422 if (! tagp)
423 return FDT_END; /* premature end */
424 tag = fdt32_to_cpu(*tagp);
425 offset += FDT_TAGSIZE;
426
427 switch (tag) {
428 case FDT_BEGIN_NODE:
429 if(namep)
430 *namep = fdt_offset_ptr(fdt, offset, 1);
431
432 /* skip name */
433 do {
434 p = fdt_offset_ptr(fdt, offset++, 1);
435 } while (p && (*p != '\0'));
436 if (! p)
437 return FDT_END;
438 break;
439 case FDT_PROP:
440 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
441 if (! lenp)
442 return FDT_END;
443 /*
444 * Get the property and set the namep to the name.
445 */
446 if(namep) {
447 struct fdt_property *prop;
448
449 prop = fdt_offset_ptr_typed(fdt, offset - FDT_TAGSIZE, prop);
450 if (! prop)
451 return -FDT_ERR_BADSTRUCTURE;
452 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
453 }
454 /* skip name offset, length and value */
455 offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
456 break;
457 }
458
459 if (nextoffset)
460 *nextoffset = ALIGN(offset, FDT_TAGSIZE);
461
462 return tag;
463}
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400464
465/*
466 * Return the number of used reserve map entries and total slots available.
467 */
468int fdt_num_reservemap(void *fdt, int *used, int *total)
469{
470 struct fdt_reserve_entry *re;
471 int start;
472 int end;
473 int err = fdt_check_header(fdt);
474
475 if (err != 0)
476 return err;
477
478 start = fdt_off_mem_rsvmap(fdt);
479
480 /*
481 * Convention is that the reserve map is before the dt_struct,
482 * but it does not have to be.
483 */
484 end = fdt_totalsize(fdt);
485 if (end > fdt_off_dt_struct(fdt))
486 end = fdt_off_dt_struct(fdt);
487 if (end > fdt_off_dt_strings(fdt))
488 end = fdt_off_dt_strings(fdt);
489
490 /*
491 * Since the reserved area list is zero terminated, you get one fewer.
492 */
493 if (total)
494 *total = ((end - start) / sizeof(struct fdt_reserve_entry)) - 1;
495
496 if (used) {
497 *used = 0;
498 while (start < end) {
499 re = (struct fdt_reserve_entry *)(fdt + start);
500 if (re->size == 0)
501 return 0; /* zero size terminates the list */
502
503 *used += 1;
504 start += sizeof(struct fdt_reserve_entry);
505 }
506 /*
507 * If we get here, there was no zero size termination.
508 */
509 return -FDT_ERR_BADLAYOUT;
510 }
511 return 0;
512}
513
514/*
515 * Return the nth reserve map entry.
516 */
517int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re)
518{
519 int used;
520 int total;
521 int err;
522
523 err = fdt_num_reservemap(fdt, &used, &total);
524 if (err != 0)
525 return err;
526
527 if (n >= total)
528 return -FDT_ERR_NOSPACE;
529 if (re) {
530 *re = *(struct fdt_reserve_entry *)
531 _fdt_offset_ptr(fdt, n * sizeof(struct fdt_reserve_entry));
532 }
533 return 0;
534}
Gerald Van Baren8096b3b2007-04-20 22:46:53 -0400535
536#endif /* CONFIG_OF_LIBFDT */