blob: 4e2c325b4d5590615b1aa329d89d2cf86da09079 [file] [log] [blame]
Gerald Van Baren9e61ed82007-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 */
19#include "libfdt_env.h"
20
21#include <fdt.h>
22#include <libfdt.h>
23
24#include "libfdt_internal.h"
25
Wolfgang Denk39adcbb2007-04-04 01:49:15 +020026#define CHECK_HEADER(fdt) { \
27 int err; \
Gerald Van Baren9f0f3582007-04-06 14:17:14 -040028 if ((err = fdt_check_header(fdt)) != 0) \
Wolfgang Denk39adcbb2007-04-04 01:49:15 +020029 return err; \
30}
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040031
32static int offset_streq(const void *fdt, int offset,
33 const char *s, int len)
34{
35 const char *p = fdt_offset_ptr(fdt, offset, len+1);
36
37 if (! p)
38 /* short match */
39 return 0;
40
41 if (memcmp(p, s, len) != 0)
42 return 0;
43
44 if (p[len] != '\0')
45 return 0;
46
47 return 1;
48}
49
Gerald Van Barenca8d8a62007-03-31 12:13:43 -040050/*
51 * Return a pointer to the string at the given string offset.
52 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040053char *fdt_string(const void *fdt, int stroffset)
54{
55 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
56}
57
Gerald Van Barenca8d8a62007-03-31 12:13:43 -040058/*
59 * Return the node offset of the node specified by:
60 * parentoffset - starting place (0 to start at the root)
61 * name - name being searched for
62 * namelen - length of the name: typically strlen(name)
63 *
64 * Notes:
65 * If the start node has subnodes, the subnodes are _not_ searched for the
66 * requested name.
67 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040068int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
69 const char *name, int namelen)
70{
71 int level = 0;
72 uint32_t tag;
73 int offset, nextoffset;
74
75 CHECK_HEADER(fdt);
76
Gerald Van Barenca8d8a62007-03-31 12:13:43 -040077 tag = fdt_next_tag(fdt, parentoffset, &nextoffset, NULL);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040078 if (tag != FDT_BEGIN_NODE)
79 return -FDT_ERR_BADOFFSET;
80
81 do {
82 offset = nextoffset;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -040083 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040084
85 switch (tag) {
86 case FDT_END:
87 return -FDT_ERR_TRUNCATED;
88
89 case FDT_BEGIN_NODE:
90 level++;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -040091 /*
92 * If we are nested down levels, ignore the strings
93 * until we get back to the proper level.
94 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040095 if (level != 1)
96 continue;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -040097
98 /* Return the offset if this is "our" string. */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -040099 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400100 return offset;
101 break;
102
103 case FDT_END_NODE:
104 level--;
105 break;
106
107 case FDT_PROP:
108 case FDT_NOP:
109 break;
110
111 default:
112 return -FDT_ERR_BADSTRUCTURE;
113 }
114 } while (level >= 0);
115
116 return -FDT_ERR_NOTFOUND;
117}
118
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400119/*
120 * See fdt_subnode_offset_namelen()
121 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400122int fdt_subnode_offset(const void *fdt, int parentoffset,
123 const char *name)
124{
125 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
126}
127
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400128/*
129 * Searches for the node corresponding to the given path and returns the
130 * offset of that node.
131 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400132int fdt_path_offset(const void *fdt, const char *path)
133{
134 const char *end = path + strlen(path);
135 const char *p = path;
136 int offset = 0;
137
138 CHECK_HEADER(fdt);
139
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400140 /* Paths must be absolute */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400141 if (*path != '/')
142 return -FDT_ERR_BADPATH;
143
144 while (*p) {
145 const char *q;
146
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400147 /* Skip path separator(s) */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400148 while (*p == '/')
149 p++;
150 if (! *p)
151 return -FDT_ERR_BADPATH;
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400152
153 /*
154 * Find the next path separator. The characters between
155 * p and q are the next segment of the the path to find.
156 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400157 q = strchr(p, '/');
158 if (! q)
159 q = end;
160
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400161 /*
162 * Find the offset corresponding to the this path segment.
163 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400164 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400165
166 /* Oops, error, abort abort abort */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400167 if (offset < 0)
168 return offset;
169
170 p = q;
171 }
172
Gerald Van Baren32480892007-03-31 14:30:53 -0400173 return offset;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400174}
175
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400176/*
177 * Given the offset of a node and a name of a property in that node, return
178 * a pointer to the property struct.
179 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400180struct fdt_property *fdt_get_property(const void *fdt,
181 int nodeoffset,
182 const char *name, int *lenp)
183{
184 int level = 0;
185 uint32_t tag;
186 struct fdt_property *prop;
187 int namestroff;
188 int offset, nextoffset;
189 int err;
190
Gerald Van Baren9f0f3582007-04-06 14:17:14 -0400191 if ((err = fdt_check_header(fdt)) != 0)
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400192 goto fail;
193
194 err = -FDT_ERR_BADOFFSET;
195 if (nodeoffset % FDT_TAGSIZE)
196 goto fail;
197
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400198 tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400199 if (tag != FDT_BEGIN_NODE)
200 goto fail;
201
202 do {
203 offset = nextoffset;
204
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400205 tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400206 switch (tag) {
207 case FDT_END:
208 err = -FDT_ERR_TRUNCATED;
209 goto fail;
210
211 case FDT_BEGIN_NODE:
212 level++;
213 break;
214
215 case FDT_END_NODE:
216 level--;
217 break;
218
219 case FDT_PROP:
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400220 /*
221 * If we are nested down levels, ignore the strings
222 * until we get back to the proper level.
223 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400224 if (level != 0)
225 continue;
226
227 err = -FDT_ERR_BADSTRUCTURE;
228 prop = fdt_offset_ptr_typed(fdt, offset, prop);
229 if (! prop)
230 goto fail;
231 namestroff = fdt32_to_cpu(prop->nameoff);
232 if (streq(fdt_string(fdt, namestroff), name)) {
233 /* Found it! */
234 int len = fdt32_to_cpu(prop->len);
235 prop = fdt_offset_ptr(fdt, offset,
236 sizeof(*prop)+len);
237 if (! prop)
238 goto fail;
239
240 if (lenp)
241 *lenp = len;
Gerald Van Baren32480892007-03-31 14:30:53 -0400242
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400243 return prop;
244 }
245 break;
246
247 case FDT_NOP:
248 break;
249
250 default:
251 err = -FDT_ERR_BADSTRUCTURE;
252 goto fail;
253 }
254 } while (level >= 0);
255
256 err = -FDT_ERR_NOTFOUND;
Wolfgang Denk39adcbb2007-04-04 01:49:15 +0200257fail:
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400258 if (lenp)
259 *lenp = err;
260 return NULL;
261}
262
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400263/*
264 * Given the offset of a node and a name of a property in that node, return
265 * a pointer to the property data (ONLY).
266 */
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400267void *fdt_getprop(const void *fdt, int nodeoffset,
268 const char *name, int *lenp)
269{
270 const struct fdt_property *prop;
271
272 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
273 if (! prop)
274 return NULL;
275
Gerald Van Barenca8d8a62007-03-31 12:13:43 -0400276 return (void *)prop->data;
277}
278
279
280uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset, char **namep)
281{
282 const uint32_t *tagp, *lenp;
283 uint32_t tag;
284 const char *p;
285
286 if (offset % FDT_TAGSIZE)
287 return -1;
288
289 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
290 if (! tagp)
291 return FDT_END; /* premature end */
292 tag = fdt32_to_cpu(*tagp);
293 offset += FDT_TAGSIZE;
294
295 switch (tag) {
296 case FDT_BEGIN_NODE:
297 if(namep)
298 *namep = fdt_offset_ptr(fdt, offset, 1);
299
300 /* skip name */
301 do {
302 p = fdt_offset_ptr(fdt, offset++, 1);
303 } while (p && (*p != '\0'));
304 if (! p)
305 return FDT_END;
306 break;
307 case FDT_PROP:
308 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
309 if (! lenp)
310 return FDT_END;
311 /*
312 * Get the property and set the namep to the name.
313 */
314 if(namep) {
315 struct fdt_property *prop;
316
317 prop = fdt_offset_ptr_typed(fdt, offset - FDT_TAGSIZE, prop);
318 if (! prop)
319 return -FDT_ERR_BADSTRUCTURE;
320 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
321 }
322 /* skip name offset, length and value */
323 offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
324 break;
325 }
326
327 if (nextoffset)
328 *nextoffset = ALIGN(offset, FDT_TAGSIZE);
329
330 return tag;
Gerald Van Baren9e61ed82007-03-31 12:00:56 -0400331}
Gerald Van Barenc077a132007-04-14 22:46:41 -0400332
333/*
334 * Return the number of used reserve map entries and total slots available.
335 */
336int fdt_num_reservemap(void *fdt, int *used, int *total)
337{
338 struct fdt_reserve_entry *re;
339 int start;
340 int end;
341 int err = fdt_check_header(fdt);
342
343 if (err != 0)
344 return err;
345
346 start = fdt_off_mem_rsvmap(fdt);
347
348 /*
349 * Convention is that the reserve map is before the dt_struct,
350 * but it does not have to be.
351 */
352 end = fdt_totalsize(fdt);
353 if (end > fdt_off_dt_struct(fdt))
354 end = fdt_off_dt_struct(fdt);
355 if (end > fdt_off_dt_strings(fdt))
356 end = fdt_off_dt_strings(fdt);
357
358 /*
359 * Since the reserved area list is zero terminated, you get one fewer.
360 */
361 if (total)
362 *total = ((end - start) / sizeof(struct fdt_reserve_entry)) - 1;
363
364 if (used) {
365 *used = 0;
366 while (start < end) {
367 re = (struct fdt_reserve_entry *)(fdt + start);
368 if (re->size == 0)
369 return 0; /* zero size terminates the list */
370
371 *used += 1;
372 start += sizeof(struct fdt_reserve_entry);
373 }
374 /*
375 * If we get here, there was no zero size termination.
376 */
377 return -FDT_ERR_BADLAYOUT;
378 }
379 return 0;
380}
381
382/*
383 * Return the nth reserve map entry.
384 */
385int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re)
386{
387 int used;
388 int total;
389 int err;
390
391 err = fdt_num_reservemap(fdt, &used, &total);
392 if (err != 0)
393 return err;
394
395 if (n >= total)
396 return -FDT_ERR_NOSPACE;
397 if (re) {
398 *re = *(struct fdt_reserve_entry *)
399 _fdt_offset_ptr(fdt, n * sizeof(struct fdt_reserve_entry));
400 }
401 return 0;
402}