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