Branch data Line data Source code
1 : : // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 : : /*
3 : : * libfdt - Flat Device Tree manipulation
4 : : * Copyright (C) 2006 David Gibson, IBM Corporation.
5 : : */
6 : : #include "libfdt_env.h"
7 : :
8 : : #include <fdt.h>
9 : : #include <libfdt.h>
10 : :
11 : : #include "libfdt_internal.h"
12 : :
13 : 0 : static int fdt_nodename_eq_(const void *fdt, int offset,
14 : : const char *s, int len)
15 : : {
16 : : int olen;
17 : 0 : const char *p = fdt_get_name(fdt, offset, &olen);
18 : :
19 : 0 : if (!p || olen < len)
20 : : /* short match */
21 : 0 : return 0;
22 : :
23 : 0 : if (memcmp(p, s, len) != 0)
24 : 0 : return 0;
25 : :
26 : 0 : if (p[len] == '\0')
27 : 0 : return 1;
28 : 0 : else if (!memchr(s, '@', len) && (p[len] == '@'))
29 : 0 : return 1;
30 : : else
31 : 0 : return 0;
32 : : }
33 : :
34 : 32 : const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
35 : : {
36 : : int32_t totalsize;
37 : : uint32_t absoffset;
38 : : size_t len;
39 : : int err;
40 : : const char *s, *n;
41 : :
42 : 32 : if (can_assume(VALID_INPUT)) {
43 : 0 : s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
44 : :
45 : 0 : if (lenp)
46 : 0 : *lenp = strlen(s);
47 : 0 : return s;
48 : : }
49 : 32 : totalsize = fdt_ro_probe_(fdt);
50 : 32 : err = totalsize;
51 : 32 : if (totalsize < 0)
52 : 0 : goto fail;
53 : :
54 : 32 : err = -FDT_ERR_BADOFFSET;
55 : 32 : absoffset = stroffset + fdt_off_dt_strings(fdt);
56 : 32 : if (absoffset >= (unsigned)totalsize)
57 : 0 : goto fail;
58 : 32 : len = totalsize - absoffset;
59 : :
60 : 32 : if (fdt_magic(fdt) == FDT_MAGIC) {
61 : 32 : if (stroffset < 0)
62 : 0 : goto fail;
63 : 32 : if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
64 : 32 : if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
65 : 0 : goto fail;
66 : 32 : if ((fdt_size_dt_strings(fdt) - stroffset) < len)
67 : 0 : len = fdt_size_dt_strings(fdt) - stroffset;
68 : : }
69 : 0 : } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
70 : 0 : unsigned int sw_stroffset = -stroffset;
71 : :
72 : 0 : if ((stroffset >= 0) ||
73 : 0 : (sw_stroffset > fdt_size_dt_strings(fdt)))
74 : 0 : goto fail;
75 : 0 : if (sw_stroffset < len)
76 : 0 : len = sw_stroffset;
77 : : } else {
78 : 0 : err = -FDT_ERR_INTERNAL;
79 : 0 : goto fail;
80 : : }
81 : :
82 : 32 : s = (const char *)fdt + absoffset;
83 : 32 : n = memchr(s, '\0', len);
84 : 32 : if (!n) {
85 : : /* missing terminating NULL */
86 : 0 : err = -FDT_ERR_TRUNCATED;
87 : 0 : goto fail;
88 : : }
89 : :
90 : 32 : if (lenp)
91 : 0 : *lenp = n - s;
92 : 32 : return s;
93 : :
94 : 0 : fail:
95 : 0 : if (lenp)
96 : 0 : *lenp = err;
97 : 0 : return NULL;
98 : : }
99 : :
100 : 32 : const char *fdt_string(const void *fdt, int stroffset)
101 : : {
102 : 32 : return fdt_get_string(fdt, stroffset, NULL);
103 : : }
104 : :
105 : 0 : static int fdt_string_eq_(const void *fdt, int stroffset,
106 : : const char *s, int len)
107 : : {
108 : : int slen;
109 : 0 : const char *p = fdt_get_string(fdt, stroffset, &slen);
110 : :
111 : 0 : return p && (slen == len) && (memcmp(p, s, len) == 0);
112 : : }
113 : :
114 : 0 : int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
115 : : {
116 : 0 : uint32_t max = 0;
117 : 0 : int offset = -1;
118 : :
119 : 0 : while (true) {
120 : : uint32_t value;
121 : :
122 : 0 : offset = fdt_next_node(fdt, offset, NULL);
123 : 0 : if (offset < 0) {
124 : 0 : if (offset == -FDT_ERR_NOTFOUND)
125 : 0 : break;
126 : :
127 : 0 : return offset;
128 : : }
129 : :
130 : 0 : value = fdt_get_phandle(fdt, offset);
131 : :
132 : 0 : if (value > max)
133 : 0 : max = value;
134 : : }
135 : :
136 : 0 : if (phandle)
137 : 0 : *phandle = max;
138 : :
139 : 0 : return 0;
140 : : }
141 : :
142 : 0 : int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
143 : : {
144 : : uint32_t max;
145 : : int err;
146 : :
147 : 0 : err = fdt_find_max_phandle(fdt, &max);
148 : 0 : if (err < 0)
149 : 0 : return err;
150 : :
151 : 0 : if (max == FDT_MAX_PHANDLE)
152 : 0 : return -FDT_ERR_NOPHANDLES;
153 : :
154 : 0 : if (phandle)
155 : 0 : *phandle = max + 1;
156 : :
157 : 0 : return 0;
158 : : }
159 : :
160 : 0 : static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
161 : : {
162 : 0 : unsigned int offset = n * sizeof(struct fdt_reserve_entry);
163 : 0 : unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
164 : :
165 : 0 : if (!can_assume(VALID_INPUT)) {
166 : 0 : if (absoffset < fdt_off_mem_rsvmap(fdt))
167 : 0 : return NULL;
168 : 0 : if (absoffset > fdt_totalsize(fdt) -
169 : : sizeof(struct fdt_reserve_entry))
170 : 0 : return NULL;
171 : : }
172 : 0 : return fdt_mem_rsv_(fdt, n);
173 : : }
174 : :
175 : 0 : int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
176 : : {
177 : : const struct fdt_reserve_entry *re;
178 : :
179 : 0 : FDT_RO_PROBE(fdt);
180 : 0 : re = fdt_mem_rsv(fdt, n);
181 : 0 : if (!can_assume(VALID_INPUT) && !re)
182 : 0 : return -FDT_ERR_BADOFFSET;
183 : :
184 : 0 : *address = fdt64_ld_(&re->address);
185 : 0 : *size = fdt64_ld_(&re->size);
186 : 0 : return 0;
187 : : }
188 : :
189 : 0 : int fdt_num_mem_rsv(const void *fdt)
190 : : {
191 : : int i;
192 : : const struct fdt_reserve_entry *re;
193 : :
194 : 0 : for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
195 : 0 : if (fdt64_ld_(&re->size) == 0)
196 : 0 : return i;
197 : : }
198 : 0 : return -FDT_ERR_TRUNCATED;
199 : : }
200 : :
201 : 0 : static int nextprop_(const void *fdt, int offset)
202 : : {
203 : : uint32_t tag;
204 : : int nextoffset;
205 : :
206 : : do {
207 : 0 : tag = fdt_next_tag(fdt, offset, &nextoffset);
208 : :
209 : 0 : switch (tag) {
210 : 0 : case FDT_END:
211 : 0 : if (nextoffset >= 0)
212 : 0 : return -FDT_ERR_BADSTRUCTURE;
213 : : else
214 : 0 : return nextoffset;
215 : :
216 : 0 : case FDT_PROP:
217 : 0 : return offset;
218 : : }
219 : 0 : offset = nextoffset;
220 : 0 : } while (tag == FDT_NOP);
221 : :
222 : 0 : return -FDT_ERR_NOTFOUND;
223 : : }
224 : :
225 : 0 : int fdt_subnode_offset_namelen(const void *fdt, int offset,
226 : : const char *name, int namelen)
227 : : {
228 : : int depth;
229 : :
230 : 0 : FDT_RO_PROBE(fdt);
231 : :
232 : 0 : for (depth = 0;
233 : 0 : (offset >= 0) && (depth >= 0);
234 : 0 : offset = fdt_next_node(fdt, offset, &depth))
235 : 0 : if ((depth == 1)
236 : 0 : && fdt_nodename_eq_(fdt, offset, name, namelen))
237 : 0 : return offset;
238 : :
239 : 0 : if (depth < 0)
240 : 0 : return -FDT_ERR_NOTFOUND;
241 : 0 : return offset; /* error */
242 : : }
243 : :
244 : 0 : int fdt_subnode_offset(const void *fdt, int parentoffset,
245 : : const char *name)
246 : : {
247 : 0 : return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
248 : : }
249 : :
250 : 0 : int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
251 : : {
252 : 0 : const char *end = path + namelen;
253 : 0 : const char *p = path;
254 : 0 : int offset = 0;
255 : :
256 : 0 : FDT_RO_PROBE(fdt);
257 : :
258 : : /* see if we have an alias */
259 : 0 : if (*path != '/') {
260 : 0 : const char *q = memchr(path, '/', end - p);
261 : :
262 : 0 : if (!q)
263 : 0 : q = end;
264 : :
265 : 0 : p = fdt_get_alias_namelen(fdt, p, q - p);
266 : 0 : if (!p)
267 : 0 : return -FDT_ERR_BADPATH;
268 : 0 : offset = fdt_path_offset(fdt, p);
269 : :
270 : 0 : p = q;
271 : : }
272 : :
273 : 0 : while (p < end) {
274 : : const char *q;
275 : :
276 : 0 : while (*p == '/') {
277 : 0 : p++;
278 : 0 : if (p == end)
279 : 0 : return offset;
280 : : }
281 : 0 : q = memchr(p, '/', end - p);
282 : 0 : if (! q)
283 : 0 : q = end;
284 : :
285 : 0 : offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
286 : 0 : if (offset < 0)
287 : 0 : return offset;
288 : :
289 : 0 : p = q;
290 : : }
291 : :
292 : 0 : return offset;
293 : : }
294 : :
295 : 0 : int fdt_path_offset(const void *fdt, const char *path)
296 : : {
297 : 0 : return fdt_path_offset_namelen(fdt, path, strlen(path));
298 : : }
299 : :
300 : 7 : const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
301 : : {
302 : 7 : const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
303 : : const char *nameptr;
304 : : int err;
305 : :
306 : 7 : if (((err = fdt_ro_probe_(fdt)) < 0)
307 : 7 : || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
308 : 0 : goto fail;
309 : :
310 : 7 : nameptr = nh->name;
311 : :
312 : 7 : if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
313 : : /*
314 : : * For old FDT versions, match the naming conventions of V16:
315 : : * give only the leaf name (after all /). The actual tree
316 : : * contents are loosely checked.
317 : : */
318 : : const char *leaf;
319 : 0 : leaf = strrchr(nameptr, '/');
320 : 0 : if (leaf == NULL) {
321 : 0 : err = -FDT_ERR_BADSTRUCTURE;
322 : 0 : goto fail;
323 : : }
324 : 0 : nameptr = leaf+1;
325 : : }
326 : :
327 : 7 : if (len)
328 : 0 : *len = strlen(nameptr);
329 : :
330 : 7 : return nameptr;
331 : :
332 : 0 : fail:
333 : 0 : if (len)
334 : 0 : *len = err;
335 : 0 : return NULL;
336 : : }
337 : :
338 : 0 : int fdt_first_property_offset(const void *fdt, int nodeoffset)
339 : : {
340 : : int offset;
341 : :
342 : 0 : if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
343 : 0 : return offset;
344 : :
345 : 0 : return nextprop_(fdt, offset);
346 : : }
347 : :
348 : 0 : int fdt_next_property_offset(const void *fdt, int offset)
349 : : {
350 : 0 : if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
351 : 0 : return offset;
352 : :
353 : 0 : return nextprop_(fdt, offset);
354 : : }
355 : :
356 : 0 : static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
357 : : int offset,
358 : : int *lenp)
359 : : {
360 : : int err;
361 : : const struct fdt_property *prop;
362 : :
363 : 0 : if (!can_assume(VALID_INPUT) &&
364 : 0 : (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
365 : 0 : if (lenp)
366 : 0 : *lenp = err;
367 : 0 : return NULL;
368 : : }
369 : :
370 : 0 : prop = fdt_offset_ptr_(fdt, offset);
371 : :
372 : 0 : if (lenp)
373 : 0 : *lenp = fdt32_ld_(&prop->len);
374 : :
375 : 0 : return prop;
376 : : }
377 : :
378 : 0 : const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
379 : : int offset,
380 : : int *lenp)
381 : : {
382 : : /* Prior to version 16, properties may need realignment
383 : : * and this API does not work. fdt_getprop_*() will, however. */
384 : :
385 : 0 : if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
386 : 0 : if (lenp)
387 : 0 : *lenp = -FDT_ERR_BADVERSION;
388 : 0 : return NULL;
389 : : }
390 : :
391 : 0 : return fdt_get_property_by_offset_(fdt, offset, lenp);
392 : : }
393 : :
394 : 0 : static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
395 : : int offset,
396 : : const char *name,
397 : : int namelen,
398 : : int *lenp,
399 : : int *poffset)
400 : : {
401 : 0 : for (offset = fdt_first_property_offset(fdt, offset);
402 : 0 : (offset >= 0);
403 : 0 : (offset = fdt_next_property_offset(fdt, offset))) {
404 : : const struct fdt_property *prop;
405 : :
406 : 0 : prop = fdt_get_property_by_offset_(fdt, offset, lenp);
407 : 0 : if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
408 : 0 : offset = -FDT_ERR_INTERNAL;
409 : 0 : break;
410 : : }
411 : 0 : if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
412 : : name, namelen)) {
413 : 0 : if (poffset)
414 : 0 : *poffset = offset;
415 : 0 : return prop;
416 : : }
417 : : }
418 : :
419 : 0 : if (lenp)
420 : 0 : *lenp = offset;
421 : 0 : return NULL;
422 : : }
423 : :
424 : :
425 : 0 : const struct fdt_property *fdt_get_property_namelen(const void *fdt,
426 : : int offset,
427 : : const char *name,
428 : : int namelen, int *lenp)
429 : : {
430 : : /* Prior to version 16, properties may need realignment
431 : : * and this API does not work. fdt_getprop_*() will, however. */
432 : 0 : if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
433 : 0 : if (lenp)
434 : 0 : *lenp = -FDT_ERR_BADVERSION;
435 : 0 : return NULL;
436 : : }
437 : :
438 : 0 : return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
439 : : NULL);
440 : : }
441 : :
442 : :
443 : 0 : const struct fdt_property *fdt_get_property(const void *fdt,
444 : : int nodeoffset,
445 : : const char *name, int *lenp)
446 : : {
447 : 0 : return fdt_get_property_namelen(fdt, nodeoffset, name,
448 : 0 : strlen(name), lenp);
449 : : }
450 : :
451 : 0 : const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
452 : : const char *name, int namelen, int *lenp)
453 : : {
454 : : int poffset;
455 : : const struct fdt_property *prop;
456 : :
457 : 0 : prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
458 : : &poffset);
459 : 0 : if (!prop)
460 : 0 : return NULL;
461 : :
462 : : /* Handle realignment */
463 : 0 : if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
464 : 0 : (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
465 : 0 : return prop->data + 4;
466 : 0 : return prop->data;
467 : : }
468 : :
469 : 0 : const void *fdt_getprop_by_offset(const void *fdt, int offset,
470 : : const char **namep, int *lenp)
471 : : {
472 : : const struct fdt_property *prop;
473 : :
474 : 0 : prop = fdt_get_property_by_offset_(fdt, offset, lenp);
475 : 0 : if (!prop)
476 : 0 : return NULL;
477 : 0 : if (namep) {
478 : : const char *name;
479 : : int namelen;
480 : :
481 : 0 : if (!can_assume(VALID_INPUT)) {
482 : 0 : name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
483 : : &namelen);
484 : 0 : if (!name) {
485 : 0 : if (lenp)
486 : 0 : *lenp = namelen;
487 : 0 : return NULL;
488 : : }
489 : 0 : *namep = name;
490 : : } else {
491 : 0 : *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
492 : : }
493 : : }
494 : :
495 : : /* Handle realignment */
496 : 0 : if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
497 : 0 : (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
498 : 0 : return prop->data + 4;
499 : 0 : return prop->data;
500 : : }
501 : :
502 : 0 : const void *fdt_getprop(const void *fdt, int nodeoffset,
503 : : const char *name, int *lenp)
504 : : {
505 : 0 : return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
506 : : }
507 : :
508 : 0 : uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
509 : : {
510 : : const fdt32_t *php;
511 : : int len;
512 : :
513 : : /* FIXME: This is a bit sub-optimal, since we potentially scan
514 : : * over all the properties twice. */
515 : 0 : php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
516 : 0 : if (!php || (len != sizeof(*php))) {
517 : 0 : php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
518 : 0 : if (!php || (len != sizeof(*php)))
519 : 0 : return 0;
520 : : }
521 : :
522 : 0 : return fdt32_ld_(php);
523 : : }
524 : :
525 : 0 : const char *fdt_get_alias_namelen(const void *fdt,
526 : : const char *name, int namelen)
527 : : {
528 : : int aliasoffset;
529 : :
530 : 0 : aliasoffset = fdt_path_offset(fdt, "/aliases");
531 : 0 : if (aliasoffset < 0)
532 : 0 : return NULL;
533 : :
534 : 0 : return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
535 : : }
536 : :
537 : 0 : const char *fdt_get_alias(const void *fdt, const char *name)
538 : : {
539 : 0 : return fdt_get_alias_namelen(fdt, name, strlen(name));
540 : : }
541 : :
542 : 0 : int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
543 : : {
544 : 0 : int pdepth = 0, p = 0;
545 : : int offset, depth, namelen;
546 : : const char *name;
547 : :
548 : 0 : FDT_RO_PROBE(fdt);
549 : :
550 : 0 : if (buflen < 2)
551 : 0 : return -FDT_ERR_NOSPACE;
552 : :
553 : 0 : for (offset = 0, depth = 0;
554 : 0 : (offset >= 0) && (offset <= nodeoffset);
555 : 0 : offset = fdt_next_node(fdt, offset, &depth)) {
556 : 0 : while (pdepth > depth) {
557 : : do {
558 : 0 : p--;
559 : 0 : } while (buf[p-1] != '/');
560 : 0 : pdepth--;
561 : : }
562 : :
563 : 0 : if (pdepth >= depth) {
564 : 0 : name = fdt_get_name(fdt, offset, &namelen);
565 : 0 : if (!name)
566 : 0 : return namelen;
567 : 0 : if ((p + namelen + 1) <= buflen) {
568 : 0 : memcpy(buf + p, name, namelen);
569 : 0 : p += namelen;
570 : 0 : buf[p++] = '/';
571 : 0 : pdepth++;
572 : : }
573 : : }
574 : :
575 : 0 : if (offset == nodeoffset) {
576 : 0 : if (pdepth < (depth + 1))
577 : 0 : return -FDT_ERR_NOSPACE;
578 : :
579 : 0 : if (p > 1) /* special case so that root path is "/", not "" */
580 : 0 : p--;
581 : 0 : buf[p] = '\0';
582 : 0 : return 0;
583 : : }
584 : : }
585 : :
586 : 0 : if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
587 : 0 : return -FDT_ERR_BADOFFSET;
588 : 0 : else if (offset == -FDT_ERR_BADOFFSET)
589 : 0 : return -FDT_ERR_BADSTRUCTURE;
590 : :
591 : 0 : return offset; /* error from fdt_next_node() */
592 : : }
593 : :
594 : 0 : int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
595 : : int supernodedepth, int *nodedepth)
596 : : {
597 : : int offset, depth;
598 : 0 : int supernodeoffset = -FDT_ERR_INTERNAL;
599 : :
600 : 0 : FDT_RO_PROBE(fdt);
601 : :
602 : 0 : if (supernodedepth < 0)
603 : 0 : return -FDT_ERR_NOTFOUND;
604 : :
605 : 0 : for (offset = 0, depth = 0;
606 : 0 : (offset >= 0) && (offset <= nodeoffset);
607 : 0 : offset = fdt_next_node(fdt, offset, &depth)) {
608 : 0 : if (depth == supernodedepth)
609 : 0 : supernodeoffset = offset;
610 : :
611 : 0 : if (offset == nodeoffset) {
612 : 0 : if (nodedepth)
613 : 0 : *nodedepth = depth;
614 : :
615 : 0 : if (supernodedepth > depth)
616 : 0 : return -FDT_ERR_NOTFOUND;
617 : : else
618 : 0 : return supernodeoffset;
619 : : }
620 : : }
621 : :
622 : 0 : if (!can_assume(VALID_INPUT)) {
623 : 0 : if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
624 : 0 : return -FDT_ERR_BADOFFSET;
625 : 0 : else if (offset == -FDT_ERR_BADOFFSET)
626 : 0 : return -FDT_ERR_BADSTRUCTURE;
627 : : }
628 : :
629 : 0 : return offset; /* error from fdt_next_node() */
630 : : }
631 : :
632 : 0 : int fdt_node_depth(const void *fdt, int nodeoffset)
633 : : {
634 : : int nodedepth;
635 : : int err;
636 : :
637 : 0 : err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
638 : 0 : if (err)
639 : 0 : return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
640 : : -FDT_ERR_INTERNAL;
641 : 0 : return nodedepth;
642 : : }
643 : :
644 : 0 : int fdt_parent_offset(const void *fdt, int nodeoffset)
645 : : {
646 : 0 : int nodedepth = fdt_node_depth(fdt, nodeoffset);
647 : :
648 : 0 : if (nodedepth < 0)
649 : 0 : return nodedepth;
650 : 0 : return fdt_supernode_atdepth_offset(fdt, nodeoffset,
651 : : nodedepth - 1, NULL);
652 : : }
653 : :
654 : 0 : int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
655 : : const char *propname,
656 : : const void *propval, int proplen)
657 : : {
658 : : int offset;
659 : : const void *val;
660 : : int len;
661 : :
662 : 0 : FDT_RO_PROBE(fdt);
663 : :
664 : : /* FIXME: The algorithm here is pretty horrible: we scan each
665 : : * property of a node in fdt_getprop(), then if that didn't
666 : : * find what we want, we scan over them again making our way
667 : : * to the next node. Still it's the easiest to implement
668 : : * approach; performance can come later. */
669 : 0 : for (offset = fdt_next_node(fdt, startoffset, NULL);
670 : 0 : offset >= 0;
671 : 0 : offset = fdt_next_node(fdt, offset, NULL)) {
672 : 0 : val = fdt_getprop(fdt, offset, propname, &len);
673 : 0 : if (val && (len == proplen)
674 : 0 : && (memcmp(val, propval, len) == 0))
675 : 0 : return offset;
676 : : }
677 : :
678 : 0 : return offset; /* error from fdt_next_node() */
679 : : }
680 : :
681 : 0 : int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
682 : : {
683 : : int offset;
684 : :
685 : 0 : if ((phandle == 0) || (phandle == ~0U))
686 : 0 : return -FDT_ERR_BADPHANDLE;
687 : :
688 : 0 : FDT_RO_PROBE(fdt);
689 : :
690 : : /* FIXME: The algorithm here is pretty horrible: we
691 : : * potentially scan each property of a node in
692 : : * fdt_get_phandle(), then if that didn't find what
693 : : * we want, we scan over them again making our way to the next
694 : : * node. Still it's the easiest to implement approach;
695 : : * performance can come later. */
696 : 0 : for (offset = fdt_next_node(fdt, -1, NULL);
697 : 0 : offset >= 0;
698 : 0 : offset = fdt_next_node(fdt, offset, NULL)) {
699 : 0 : if (fdt_get_phandle(fdt, offset) == phandle)
700 : 0 : return offset;
701 : : }
702 : :
703 : 0 : return offset; /* error from fdt_next_node() */
704 : : }
705 : :
706 : 0 : int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
707 : : {
708 : 0 : int len = strlen(str);
709 : : const char *p;
710 : :
711 : 0 : while (listlen >= len) {
712 : 0 : if (memcmp(str, strlist, len+1) == 0)
713 : 0 : return 1;
714 : 0 : p = memchr(strlist, '\0', listlen);
715 : 0 : if (!p)
716 : 0 : return 0; /* malformed strlist.. */
717 : 0 : listlen -= (p-strlist) + 1;
718 : 0 : strlist = p + 1;
719 : : }
720 : 0 : return 0;
721 : : }
722 : :
723 : 0 : int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
724 : : {
725 : : const char *list, *end;
726 : 0 : int length, count = 0;
727 : :
728 : 0 : list = fdt_getprop(fdt, nodeoffset, property, &length);
729 : 0 : if (!list)
730 : 0 : return length;
731 : :
732 : 0 : end = list + length;
733 : :
734 : 0 : while (list < end) {
735 : 0 : length = strnlen(list, end - list) + 1;
736 : :
737 : : /* Abort if the last string isn't properly NUL-terminated. */
738 : 0 : if (list + length > end)
739 : 0 : return -FDT_ERR_BADVALUE;
740 : :
741 : 0 : list += length;
742 : 0 : count++;
743 : : }
744 : :
745 : 0 : return count;
746 : : }
747 : :
748 : 0 : int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
749 : : const char *string)
750 : : {
751 : 0 : int length, len, idx = 0;
752 : : const char *list, *end;
753 : :
754 : 0 : list = fdt_getprop(fdt, nodeoffset, property, &length);
755 : 0 : if (!list)
756 : 0 : return length;
757 : :
758 : 0 : len = strlen(string) + 1;
759 : 0 : end = list + length;
760 : :
761 : 0 : while (list < end) {
762 : 0 : length = strnlen(list, end - list) + 1;
763 : :
764 : : /* Abort if the last string isn't properly NUL-terminated. */
765 : 0 : if (list + length > end)
766 : 0 : return -FDT_ERR_BADVALUE;
767 : :
768 : 0 : if (length == len && memcmp(list, string, length) == 0)
769 : 0 : return idx;
770 : :
771 : 0 : list += length;
772 : 0 : idx++;
773 : : }
774 : :
775 : 0 : return -FDT_ERR_NOTFOUND;
776 : : }
777 : :
778 : 0 : const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
779 : : const char *property, int idx,
780 : : int *lenp)
781 : : {
782 : : const char *list, *end;
783 : : int length;
784 : :
785 : 0 : list = fdt_getprop(fdt, nodeoffset, property, &length);
786 : 0 : if (!list) {
787 : 0 : if (lenp)
788 : 0 : *lenp = length;
789 : :
790 : 0 : return NULL;
791 : : }
792 : :
793 : 0 : end = list + length;
794 : :
795 : 0 : while (list < end) {
796 : 0 : length = strnlen(list, end - list) + 1;
797 : :
798 : : /* Abort if the last string isn't properly NUL-terminated. */
799 : 0 : if (list + length > end) {
800 : 0 : if (lenp)
801 : 0 : *lenp = -FDT_ERR_BADVALUE;
802 : :
803 : 0 : return NULL;
804 : : }
805 : :
806 : 0 : if (idx == 0) {
807 : 0 : if (lenp)
808 : 0 : *lenp = length - 1;
809 : :
810 : 0 : return list;
811 : : }
812 : :
813 : 0 : list += length;
814 : 0 : idx--;
815 : : }
816 : :
817 : 0 : if (lenp)
818 : 0 : *lenp = -FDT_ERR_NOTFOUND;
819 : :
820 : 0 : return NULL;
821 : : }
822 : :
823 : 0 : int fdt_node_check_compatible(const void *fdt, int nodeoffset,
824 : : const char *compatible)
825 : : {
826 : : const void *prop;
827 : : int len;
828 : :
829 : 0 : prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
830 : 0 : if (!prop)
831 : 0 : return len;
832 : :
833 : 0 : return !fdt_stringlist_contains(prop, len, compatible);
834 : : }
835 : :
836 : 0 : int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
837 : : const char *compatible)
838 : : {
839 : : int offset, err;
840 : :
841 : 0 : FDT_RO_PROBE(fdt);
842 : :
843 : : /* FIXME: The algorithm here is pretty horrible: we scan each
844 : : * property of a node in fdt_node_check_compatible(), then if
845 : : * that didn't find what we want, we scan over them again
846 : : * making our way to the next node. Still it's the easiest to
847 : : * implement approach; performance can come later. */
848 : 0 : for (offset = fdt_next_node(fdt, startoffset, NULL);
849 : 0 : offset >= 0;
850 : 0 : offset = fdt_next_node(fdt, offset, NULL)) {
851 : 0 : err = fdt_node_check_compatible(fdt, offset, compatible);
852 : 0 : if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
853 : 0 : return err;
854 : 0 : else if (err == 0)
855 : 0 : return offset;
856 : : }
857 : :
858 : 0 : return offset; /* error from fdt_next_node() */
859 : : }
|