blob: 1a03109e812a8b9c8cdf3305870dbd708ebc42dd [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/*
54 * Return a pointer to the string at the given string offset.
55 */
Gerald Van Baren35748172007-03-31 12:00:56 -040056char *fdt_string(const void *fdt, int stroffset)
57{
58 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
59}
60
Gerald Van Baren3af0d582007-03-31 12:13:43 -040061/*
62 * Return the node offset of the node specified by:
63 * parentoffset - starting place (0 to start at the root)
64 * name - name being searched for
65 * namelen - length of the name: typically strlen(name)
66 *
67 * Notes:
68 * If the start node has subnodes, the subnodes are _not_ searched for the
69 * requested name.
70 */
Gerald Van Baren35748172007-03-31 12:00:56 -040071int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
72 const char *name, int namelen)
73{
74 int level = 0;
75 uint32_t tag;
76 int offset, nextoffset;
77
78 CHECK_HEADER(fdt);
79
Gerald Van Baren3af0d582007-03-31 12:13:43 -040080 tag = fdt_next_tag(fdt, parentoffset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -040081 if (tag != FDT_BEGIN_NODE)
82 return -FDT_ERR_BADOFFSET;
83
84 do {
85 offset = nextoffset;
Gerald Van Baren3af0d582007-03-31 12:13:43 -040086 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -040087
88 switch (tag) {
89 case FDT_END:
90 return -FDT_ERR_TRUNCATED;
91
92 case FDT_BEGIN_NODE:
93 level++;
Gerald Van Baren3af0d582007-03-31 12:13:43 -040094 /*
95 * If we are nested down levels, ignore the strings
96 * until we get back to the proper level.
97 */
Gerald Van Baren35748172007-03-31 12:00:56 -040098 if (level != 1)
99 continue;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400100
101 /* Return the offset if this is "our" string. */
Gerald Van Baren35748172007-03-31 12:00:56 -0400102 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
Gerald Van Baren35748172007-03-31 12:00:56 -0400103 return offset;
104 break;
105
106 case FDT_END_NODE:
107 level--;
108 break;
109
110 case FDT_PROP:
111 case FDT_NOP:
112 break;
113
114 default:
115 return -FDT_ERR_BADSTRUCTURE;
116 }
117 } while (level >= 0);
118
119 return -FDT_ERR_NOTFOUND;
120}
121
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400122/*
123 * See fdt_subnode_offset_namelen()
124 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400125int fdt_subnode_offset(const void *fdt, int parentoffset,
126 const char *name)
127{
128 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
129}
130
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400131/*
132 * Searches for the node corresponding to the given path and returns the
133 * offset of that node.
134 */
Gerald Van Baren1a861162007-06-06 22:47:58 -0400135int fdt_find_node_by_path(const void *fdt, const char *path)
Gerald Van Baren35748172007-03-31 12:00:56 -0400136{
137 const char *end = path + strlen(path);
138 const char *p = path;
139 int offset = 0;
140
141 CHECK_HEADER(fdt);
142
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400143 /* Paths must be absolute */
Gerald Van Baren35748172007-03-31 12:00:56 -0400144 if (*path != '/')
145 return -FDT_ERR_BADPATH;
146
147 while (*p) {
148 const char *q;
149
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400150 /* Skip path separator(s) */
Gerald Van Baren35748172007-03-31 12:00:56 -0400151 while (*p == '/')
152 p++;
153 if (! *p)
154 return -FDT_ERR_BADPATH;
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400155
156 /*
157 * Find the next path separator. The characters between
158 * p and q are the next segment of the the path to find.
159 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400160 q = strchr(p, '/');
161 if (! q)
162 q = end;
163
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400164 /*
165 * Find the offset corresponding to the this path segment.
166 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400167 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400168
169 /* Oops, error, abort abort abort */
Gerald Van Baren35748172007-03-31 12:00:56 -0400170 if (offset < 0)
171 return offset;
172
173 p = q;
174 }
175
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400176 return offset;
Gerald Van Baren35748172007-03-31 12:00:56 -0400177}
178
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400179/*
180 * Given the offset of a node and a name of a property in that node, return
181 * a pointer to the property struct.
182 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400183struct fdt_property *fdt_get_property(const void *fdt,
184 int nodeoffset,
185 const char *name, int *lenp)
186{
187 int level = 0;
188 uint32_t tag;
189 struct fdt_property *prop;
190 int namestroff;
191 int offset, nextoffset;
192 int err;
193
Gerald Van Baren6679f922007-04-06 14:17:14 -0400194 if ((err = fdt_check_header(fdt)) != 0)
Gerald Van Baren35748172007-03-31 12:00:56 -0400195 goto fail;
196
197 err = -FDT_ERR_BADOFFSET;
198 if (nodeoffset % FDT_TAGSIZE)
199 goto fail;
200
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400201 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400202 if (tag != FDT_BEGIN_NODE)
203 goto fail;
204
205 do {
206 offset = nextoffset;
207
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400208 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren35748172007-03-31 12:00:56 -0400209 switch (tag) {
210 case FDT_END:
211 err = -FDT_ERR_TRUNCATED;
212 goto fail;
213
214 case FDT_BEGIN_NODE:
215 level++;
216 break;
217
218 case FDT_END_NODE:
219 level--;
220 break;
221
222 case FDT_PROP:
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400223 /*
224 * If we are nested down levels, ignore the strings
225 * until we get back to the proper level.
226 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400227 if (level != 0)
228 continue;
229
230 err = -FDT_ERR_BADSTRUCTURE;
231 prop = fdt_offset_ptr_typed(fdt, offset, prop);
232 if (! prop)
233 goto fail;
234 namestroff = fdt32_to_cpu(prop->nameoff);
235 if (streq(fdt_string(fdt, namestroff), name)) {
236 /* Found it! */
237 int len = fdt32_to_cpu(prop->len);
238 prop = fdt_offset_ptr(fdt, offset,
239 sizeof(*prop)+len);
240 if (! prop)
241 goto fail;
242
243 if (lenp)
244 *lenp = len;
Gerald Van Barenaea03c42007-03-31 14:30:53 -0400245
Gerald Van Baren35748172007-03-31 12:00:56 -0400246 return prop;
247 }
248 break;
249
250 case FDT_NOP:
251 break;
252
253 default:
254 err = -FDT_ERR_BADSTRUCTURE;
255 goto fail;
256 }
257 } while (level >= 0);
258
259 err = -FDT_ERR_NOTFOUND;
Wolfgang Denk94abd7c2007-04-04 01:49:15 +0200260fail:
Gerald Van Baren35748172007-03-31 12:00:56 -0400261 if (lenp)
262 *lenp = err;
263 return NULL;
264}
265
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400266/*
267 * Given the offset of a node and a name of a property in that node, return
268 * a pointer to the property data (ONLY).
269 */
Gerald Van Baren35748172007-03-31 12:00:56 -0400270void *fdt_getprop(const void *fdt, int nodeoffset,
271 const char *name, int *lenp)
272{
273 const struct fdt_property *prop;
274
275 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
276 if (! prop)
277 return NULL;
278
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400279 return (void *)prop->data;
Gerald Van Baren35748172007-03-31 12:00:56 -0400280}
Gerald Van Baren3af0d582007-03-31 12:13:43 -0400281
282
283uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset, char **namep)
284{
285 const uint32_t *tagp, *lenp;
286 uint32_t tag;
287 const char *p;
288
289 if (offset % FDT_TAGSIZE)
290 return -1;
291
292 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
293 if (! tagp)
294 return FDT_END; /* premature end */
295 tag = fdt32_to_cpu(*tagp);
296 offset += FDT_TAGSIZE;
297
298 switch (tag) {
299 case FDT_BEGIN_NODE:
300 if(namep)
301 *namep = fdt_offset_ptr(fdt, offset, 1);
302
303 /* skip name */
304 do {
305 p = fdt_offset_ptr(fdt, offset++, 1);
306 } while (p && (*p != '\0'));
307 if (! p)
308 return FDT_END;
309 break;
310 case FDT_PROP:
311 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
312 if (! lenp)
313 return FDT_END;
314 /*
315 * Get the property and set the namep to the name.
316 */
317 if(namep) {
318 struct fdt_property *prop;
319
320 prop = fdt_offset_ptr_typed(fdt, offset - FDT_TAGSIZE, prop);
321 if (! prop)
322 return -FDT_ERR_BADSTRUCTURE;
323 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
324 }
325 /* skip name offset, length and value */
326 offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
327 break;
328 }
329
330 if (nextoffset)
331 *nextoffset = ALIGN(offset, FDT_TAGSIZE);
332
333 return tag;
334}
Gerald Van Baren3f9f08c2007-04-14 22:46:41 -0400335
336/*
337 * Return the number of used reserve map entries and total slots available.
338 */
339int fdt_num_reservemap(void *fdt, int *used, int *total)
340{
341 struct fdt_reserve_entry *re;
342 int start;
343 int end;
344 int err = fdt_check_header(fdt);
345
346 if (err != 0)
347 return err;
348
349 start = fdt_off_mem_rsvmap(fdt);
350
351 /*
352 * Convention is that the reserve map is before the dt_struct,
353 * but it does not have to be.
354 */
355 end = fdt_totalsize(fdt);
356 if (end > fdt_off_dt_struct(fdt))
357 end = fdt_off_dt_struct(fdt);
358 if (end > fdt_off_dt_strings(fdt))
359 end = fdt_off_dt_strings(fdt);
360
361 /*
362 * Since the reserved area list is zero terminated, you get one fewer.
363 */
364 if (total)
365 *total = ((end - start) / sizeof(struct fdt_reserve_entry)) - 1;
366
367 if (used) {
368 *used = 0;
369 while (start < end) {
370 re = (struct fdt_reserve_entry *)(fdt + start);
371 if (re->size == 0)
372 return 0; /* zero size terminates the list */
373
374 *used += 1;
375 start += sizeof(struct fdt_reserve_entry);
376 }
377 /*
378 * If we get here, there was no zero size termination.
379 */
380 return -FDT_ERR_BADLAYOUT;
381 }
382 return 0;
383}
384
385/*
386 * Return the nth reserve map entry.
387 */
388int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re)
389{
390 int used;
391 int total;
392 int err;
393
394 err = fdt_num_reservemap(fdt, &used, &total);
395 if (err != 0)
396 return err;
397
398 if (n >= total)
399 return -FDT_ERR_NOSPACE;
400 if (re) {
401 *re = *(struct fdt_reserve_entry *)
402 _fdt_offset_ptr(fdt, n * sizeof(struct fdt_reserve_entry));
403 }
404 return 0;
405}
Gerald Van Baren8096b3b2007-04-20 22:46:53 -0400406
407#endif /* CONFIG_OF_LIBFDT */
408