1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51#include "libfdt_env.h"
52
53#ifndef USE_HOSTCC
54#include <fdt.h>
55#include <libfdt.h>
56#else
57#include "fdt_host.h"
58#endif
59
60#include "libfdt_internal.h"
61
62static int nodename_eq(const void *fdt, int offset,
63 const char *s, int len)
64{
65 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
66
67 if (! p)
68
69 return 0;
70
71 if (memcmp(p, s, len) != 0)
72 return 0;
73
74 if (p[len] == '\0')
75 return 1;
76 else if (!memchr(s, '@', len) && (p[len] == '@'))
77 return 1;
78 else
79 return 0;
80}
81
82const char *fdt_string(const void *fdt, int stroffset)
83{
84 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
85}
86
87int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
88{
89 CHECK_HEADER(fdt);
90 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
91 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
92 return 0;
93}
94
95int fdt_num_mem_rsv(const void *fdt)
96{
97 int i = 0;
98
99 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
100 i++;
101 return i;
102}
103
104int fdt_subnode_offset_namelen(const void *fdt, int offset,
105 const char *name, int namelen)
106{
107 int depth;
108
109 CHECK_HEADER(fdt);
110
111 for (depth = 0;
112 offset >= 0;
113 offset = fdt_next_node(fdt, offset, &depth)) {
114 if (depth < 0)
115 return -FDT_ERR_NOTFOUND;
116 else if ((depth == 1)
117 && nodename_eq(fdt, offset, name, namelen))
118 return offset;
119 }
120
121 return offset;
122}
123
124int fdt_subnode_offset(const void *fdt, int parentoffset,
125 const char *name)
126{
127 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
128}
129
130int fdt_path_offset(const void *fdt, const char *path)
131{
132 const char *end = path + strlen(path);
133 const char *p = path;
134 int offset = 0;
135
136 CHECK_HEADER(fdt);
137
138 if (*path != '/')
139 return -FDT_ERR_BADPATH;
140
141 while (*p) {
142 const char *q;
143
144 while (*p == '/')
145 p++;
146 if (! *p)
147 return offset;
148 q = strchr(p, '/');
149 if (! q)
150 q = end;
151
152 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
153 if (offset < 0)
154 return offset;
155
156 p = q;
157 }
158
159 return offset;
160}
161
162const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
163{
164 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
165 int err;
166
167 if (((err = fdt_check_header(fdt)) != 0)
168 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
169 goto fail;
170
171 if (len)
172 *len = strlen(nh->name);
173
174 return nh->name;
175
176 fail:
177 if (len)
178 *len = err;
179 return NULL;
180}
181
182const struct fdt_property *fdt_get_property(const void *fdt,
183 int nodeoffset,
184 const char *name, int *lenp)
185{
186 uint32_t tag;
187 const struct fdt_property *prop;
188 int namestroff;
189 int offset, nextoffset;
190 int err;
191
192 if (((err = fdt_check_header(fdt)) != 0)
193 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
194 goto fail;
195
196 nextoffset = err;
197 do {
198 offset = nextoffset;
199
200 tag = fdt_next_tag(fdt, offset, &nextoffset);
201 switch (tag) {
202 case FDT_END:
203 err = -FDT_ERR_TRUNCATED;
204 goto fail;
205
206 case FDT_BEGIN_NODE:
207 case FDT_END_NODE:
208 case FDT_NOP:
209 break;
210
211 case FDT_PROP:
212 err = -FDT_ERR_BADSTRUCTURE;
213 prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
214 if (! prop)
215 goto fail;
216 namestroff = fdt32_to_cpu(prop->nameoff);
217 if (streq(fdt_string(fdt, namestroff), name)) {
218
219 int len = fdt32_to_cpu(prop->len);
220 prop = fdt_offset_ptr(fdt, offset,
221 sizeof(*prop)+len);
222 if (! prop)
223 goto fail;
224
225 if (lenp)
226 *lenp = len;
227
228 return prop;
229 }
230 break;
231
232 default:
233 err = -FDT_ERR_BADSTRUCTURE;
234 goto fail;
235 }
236 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
237
238 err = -FDT_ERR_NOTFOUND;
239 fail:
240 if (lenp)
241 *lenp = err;
242 return NULL;
243}
244
245const void *fdt_getprop(const void *fdt, int nodeoffset,
246 const char *name, int *lenp)
247{
248 const struct fdt_property *prop;
249
250 prop = fdt_get_property(fdt, nodeoffset, name, lenp);
251 if (! prop)
252 return NULL;
253
254 return prop->data;
255}
256
257uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
258{
259 const uint32_t *php;
260 int len;
261
262 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
263 if (!php || (len != sizeof(*php)))
264 return 0;
265
266 return fdt32_to_cpu(*php);
267}
268
269int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
270{
271 int pdepth = 0, p = 0;
272 int offset, depth, namelen;
273 const char *name;
274
275 CHECK_HEADER(fdt);
276
277 if (buflen < 2)
278 return -FDT_ERR_NOSPACE;
279
280 for (offset = 0, depth = 0;
281 (offset >= 0) && (offset <= nodeoffset);
282 offset = fdt_next_node(fdt, offset, &depth)) {
283 if (pdepth < depth)
284 continue;
285
286 while (pdepth > depth) {
287 do {
288 p--;
289 } while (buf[p-1] != '/');
290 pdepth--;
291 }
292
293 name = fdt_get_name(fdt, offset, &namelen);
294 if (!name)
295 return namelen;
296 if ((p + namelen + 1) <= buflen) {
297 memcpy(buf + p, name, namelen);
298 p += namelen;
299 buf[p++] = '/';
300 pdepth++;
301 }
302
303 if (offset == nodeoffset) {
304 if (pdepth < (depth + 1))
305 return -FDT_ERR_NOSPACE;
306
307 if (p > 1)
308 p--;
309 buf[p] = '\0';
310 return p;
311 }
312 }
313
314 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
315 return -FDT_ERR_BADOFFSET;
316 else if (offset == -FDT_ERR_BADOFFSET)
317 return -FDT_ERR_BADSTRUCTURE;
318
319 return offset;
320}
321
322int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
323 int supernodedepth, int *nodedepth)
324{
325 int offset, depth;
326 int supernodeoffset = -FDT_ERR_INTERNAL;
327
328 CHECK_HEADER(fdt);
329
330 if (supernodedepth < 0)
331 return -FDT_ERR_NOTFOUND;
332
333 for (offset = 0, depth = 0;
334 (offset >= 0) && (offset <= nodeoffset);
335 offset = fdt_next_node(fdt, offset, &depth)) {
336 if (depth == supernodedepth)
337 supernodeoffset = offset;
338
339 if (offset == nodeoffset) {
340 if (nodedepth)
341 *nodedepth = depth;
342
343 if (supernodedepth > depth)
344 return -FDT_ERR_NOTFOUND;
345 else
346 return supernodeoffset;
347 }
348 }
349
350 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
351 return -FDT_ERR_BADOFFSET;
352 else if (offset == -FDT_ERR_BADOFFSET)
353 return -FDT_ERR_BADSTRUCTURE;
354
355 return offset;
356}
357
358int fdt_node_depth(const void *fdt, int nodeoffset)
359{
360 int nodedepth;
361 int err;
362
363 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
364 if (err)
365 return (err < 0) ? err : -FDT_ERR_INTERNAL;
366 return nodedepth;
367}
368
369int fdt_parent_offset(const void *fdt, int nodeoffset)
370{
371 int nodedepth = fdt_node_depth(fdt, nodeoffset);
372
373 if (nodedepth < 0)
374 return nodedepth;
375 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
376 nodedepth - 1, NULL);
377}
378
379int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
380 const char *propname,
381 const void *propval, int proplen)
382{
383 int offset;
384 const void *val;
385 int len;
386
387 CHECK_HEADER(fdt);
388
389
390
391
392
393
394 for (offset = fdt_next_node(fdt, startoffset, NULL);
395 offset >= 0;
396 offset = fdt_next_node(fdt, offset, NULL)) {
397 val = fdt_getprop(fdt, offset, propname, &len);
398 if (val && (len == proplen)
399 && (memcmp(val, propval, len) == 0))
400 return offset;
401 }
402
403 return offset;
404}
405
406int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
407{
408 if ((phandle == 0) || (phandle == -1))
409 return -FDT_ERR_BADPHANDLE;
410 phandle = cpu_to_fdt32(phandle);
411 return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
412 &phandle, sizeof(phandle));
413}
414
415int _stringlist_contains(const void *strlist, int listlen, const char *str)
416{
417 int len = strlen(str);
418 const void *p;
419
420 while (listlen >= len) {
421 if (memcmp(str, strlist, len+1) == 0)
422 return 1;
423 p = memchr(strlist, '\0', listlen);
424 if (!p)
425 return 0;
426 listlen -= (p-strlist) + 1;
427 strlist = p + 1;
428 }
429 return 0;
430}
431
432int fdt_node_check_compatible(const void *fdt, int nodeoffset,
433 const char *compatible)
434{
435 const void *prop;
436 int len;
437
438 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
439 if (!prop)
440 return len;
441 if (_stringlist_contains(prop, len, compatible))
442 return 0;
443 else
444 return 1;
445}
446
447int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
448 const char *compatible)
449{
450 int offset, err;
451
452 CHECK_HEADER(fdt);
453
454
455
456
457
458
459 for (offset = fdt_next_node(fdt, startoffset, NULL);
460 offset >= 0;
461 offset = fdt_next_node(fdt, offset, NULL)) {
462 err = fdt_node_check_compatible(fdt, offset, compatible);
463 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
464 return err;
465 else if (err == 0)
466 return offset;
467 }
468
469 return offset;
470}
471