1
2
3
4
5
6
7
8#include <fdt_support.h>
9#include <linux/libfdt_env.h>
10#include <fdt_region.h>
11
12#ifndef USE_HOSTCC
13#include <fdt.h>
14#include <linux/libfdt.h>
15#else
16#include "fdt_host.h"
17#endif
18
19#define FDT_MAX_DEPTH 32
20
21static int str_in_list(const char *str, char * const list[], int count)
22{
23 int i;
24
25 for (i = 0; i < count; i++)
26 if (!strcmp(list[i], str))
27 return 1;
28
29 return 0;
30}
31
32int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
33 char * const exc_prop[], int exc_prop_count,
34 struct fdt_region region[], int max_regions,
35 char *path, int path_len, int add_string_tab)
36{
37 int stack[FDT_MAX_DEPTH] = { 0 };
38 char *end;
39 int nextoffset = 0;
40 uint32_t tag;
41 int count = 0;
42 int start = -1;
43 int depth = -1;
44 int want = 0;
45 int base = fdt_off_dt_struct(fdt);
46 bool expect_end = false;
47
48 end = path;
49 *end = '\0';
50 do {
51 const struct fdt_property *prop;
52 const char *name;
53 const char *str;
54 int include = 0;
55 int stop_at = 0;
56 int offset;
57 int len;
58
59 offset = nextoffset;
60 tag = fdt_next_tag(fdt, offset, &nextoffset);
61 stop_at = nextoffset;
62
63
64 if (expect_end && tag != FDT_END)
65 return -FDT_ERR_BADLAYOUT;
66
67 switch (tag) {
68 case FDT_PROP:
69 include = want >= 2;
70 stop_at = offset;
71 prop = fdt_get_property_by_offset(fdt, offset, NULL);
72 str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
73 if (!str)
74 return -FDT_ERR_BADSTRUCTURE;
75 if (str_in_list(str, exc_prop, exc_prop_count))
76 include = 0;
77 break;
78
79 case FDT_NOP:
80 include = want >= 2;
81 stop_at = offset;
82 break;
83
84 case FDT_BEGIN_NODE:
85 depth++;
86 if (depth == FDT_MAX_DEPTH)
87 return -FDT_ERR_BADSTRUCTURE;
88 name = fdt_get_name(fdt, offset, &len);
89
90
91 if (!depth && *name)
92 return -FDT_ERR_BADLAYOUT;
93 if (end - path + 2 + len >= path_len)
94 return -FDT_ERR_NOSPACE;
95 if (end != path + 1)
96 *end++ = '/';
97 strcpy(end, name);
98 end += len;
99 stack[depth] = want;
100 if (want == 1)
101 stop_at = offset;
102 if (str_in_list(path, inc, inc_count))
103 want = 2;
104 else if (want)
105 want--;
106 else
107 stop_at = offset;
108 include = want;
109 break;
110
111 case FDT_END_NODE:
112
113 if (depth < 0)
114 return -FDT_ERR_BADSTRUCTURE;
115 include = want;
116 want = stack[depth--];
117 while (end > path && *--end != '/')
118 ;
119 *end = '\0';
120 if (depth == -1)
121 expect_end = true;
122 break;
123
124 case FDT_END:
125 include = 1;
126 break;
127 }
128
129 if (include && start == -1) {
130
131 if (count && count <= max_regions &&
132 offset == region[count - 1].offset +
133 region[count - 1].size - base)
134 start = region[--count].offset - base;
135 else
136 start = offset;
137 }
138
139 if (!include && start != -1) {
140 if (count < max_regions) {
141 region[count].offset = base + start;
142 region[count].size = stop_at - start;
143 }
144 count++;
145 start = -1;
146 }
147 } while (tag != FDT_END);
148
149 if (nextoffset != fdt_size_dt_struct(fdt))
150 return -FDT_ERR_BADLAYOUT;
151
152
153 if (count < max_regions) {
154 region[count].offset = base + start;
155 region[count].size = nextoffset - start;
156 if (add_string_tab)
157 region[count].size += fdt_size_dt_strings(fdt);
158 }
159 count++;
160
161 return count;
162}
163
164
165
166
167
168
169
170
171
172
173
174static int fdt_add_region(struct fdt_region_state *info, int offset, int size)
175{
176 struct fdt_region *reg;
177
178 reg = info->region ? &info->region[info->count - 1] : NULL;
179 if (info->can_merge && info->count &&
180 info->count <= info->max_regions &&
181 reg && offset <= reg->offset + reg->size) {
182 reg->size = offset + size - reg->offset;
183 } else if (info->count++ < info->max_regions) {
184 if (reg) {
185 reg++;
186 reg->offset = offset;
187 reg->size = size;
188 }
189 } else {
190 return -1;
191 }
192
193 return 0;
194}
195
196static int region_list_contains_offset(struct fdt_region_state *info,
197 const void *fdt, int target)
198{
199 struct fdt_region *reg;
200 int num;
201
202 target += fdt_off_dt_struct(fdt);
203 for (reg = info->region, num = 0; num < info->count; reg++, num++) {
204 if (target >= reg->offset && target < reg->offset + reg->size)
205 return 1;
206 }
207
208 return 0;
209}
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count,
236 int max_regions, struct fdt_region_state *info)
237{
238 int base = fdt_off_dt_struct(fdt);
239 int node, node_end, offset;
240 int did_alias_header;
241
242 node = fdt_subnode_offset(fdt, 0, "aliases");
243 if (node < 0)
244 return -FDT_ERR_NOTFOUND;
245
246
247
248
249
250 node_end = fdt_next_subnode(fdt, node);
251 if (node_end == -FDT_ERR_NOTFOUND)
252
253 node_end = fdt_size_dt_struct(fdt) - sizeof(fdt32_t) * 2;
254 else if (node_end < 0)
255 return node_end;
256 node_end -= sizeof(fdt32_t);
257
258 did_alias_header = 0;
259 info->region = region;
260 info->count = count;
261 info->can_merge = 0;
262 info->max_regions = max_regions;
263
264 for (offset = fdt_first_property_offset(fdt, node);
265 offset >= 0;
266 offset = fdt_next_property_offset(fdt, offset)) {
267 const struct fdt_property *prop;
268 const char *name;
269 int target, next;
270
271 prop = fdt_get_property_by_offset(fdt, offset, NULL);
272 name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
273 target = fdt_path_offset(fdt, name);
274 if (!region_list_contains_offset(info, fdt, target))
275 continue;
276 next = fdt_next_property_offset(fdt, offset);
277 if (next < 0)
278 next = node_end;
279
280 if (!did_alias_header) {
281 fdt_add_region(info, base + node, 12);
282 did_alias_header = 1;
283 }
284 fdt_add_region(info, base + offset, next - offset);
285 }
286
287
288 if (did_alias_header)
289 fdt_add_region(info, base + node_end, sizeof(fdt32_t));
290
291 return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE;
292}
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311static int fdt_include_supernodes(struct fdt_region_state *info, int depth)
312{
313 int base = fdt_off_dt_struct(info->fdt);
314 int start, stop_at;
315 int i;
316
317
318
319
320
321
322
323 for (i = 0; i <= depth; i++) {
324 if (!info->stack[i].included) {
325 start = info->stack[i].offset;
326
327
328 fdt_next_tag(info->fdt, start, &stop_at);
329 if (fdt_add_region(info, base + start, stop_at - start))
330 return -1;
331
332
333 info->stack[i].included = 1;
334 info->can_merge = 1;
335 }
336
337
338 if (!info->stack[i].want)
339 info->stack[i].want = WANT_NODES_ONLY;
340 }
341
342 return 0;
343}
344
345enum {
346 FDT_DONE_NOTHING,
347 FDT_DONE_MEM_RSVMAP,
348 FDT_DONE_STRUCT,
349 FDT_DONE_END,
350 FDT_DONE_STRINGS,
351 FDT_DONE_ALL,
352};
353
354int fdt_first_region(const void *fdt,
355 int (*h_include)(void *priv, const void *fdt, int offset,
356 int type, const char *data, int size),
357 void *priv, struct fdt_region *region,
358 char *path, int path_len, int flags,
359 struct fdt_region_state *info)
360{
361 struct fdt_region_ptrs *p = &info->ptrs;
362
363
364 info->fdt = fdt;
365 info->can_merge = 1;
366 info->max_regions = 1;
367 info->start = -1;
368 p->want = WANT_NOTHING;
369 p->end = path;
370 *p->end = '\0';
371 p->nextoffset = 0;
372 p->depth = -1;
373 p->done = FDT_DONE_NOTHING;
374
375 return fdt_next_region(fdt, h_include, priv, region,
376 path, path_len, flags, info);
377}
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451int fdt_next_region(const void *fdt,
452 int (*h_include)(void *priv, const void *fdt, int offset,
453 int type, const char *data, int size),
454 void *priv, struct fdt_region *region,
455 char *path, int path_len, int flags,
456 struct fdt_region_state *info)
457{
458 int base = fdt_off_dt_struct(fdt);
459 int last_node = 0;
460 const char *str;
461
462 info->region = region;
463 info->count = 0;
464 if (info->ptrs.done < FDT_DONE_MEM_RSVMAP &&
465 (flags & FDT_REG_ADD_MEM_RSVMAP)) {
466
467 if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt),
468 fdt_off_dt_struct(fdt) -
469 fdt_off_mem_rsvmap(fdt)))
470 return 0;
471 info->can_merge = 0;
472 info->ptrs.done = FDT_DONE_MEM_RSVMAP;
473 }
474
475
476
477
478
479
480
481
482 while (info->ptrs.done < FDT_DONE_STRUCT) {
483 const struct fdt_property *prop;
484 struct fdt_region_ptrs p;
485 const char *name;
486 int include = 0;
487 int stop_at = 0;
488 uint32_t tag;
489 int offset;
490 int val;
491 int len;
492
493
494
495
496
497
498
499 p = info->ptrs;
500
501
502
503
504
505
506 offset = p.nextoffset;
507 tag = fdt_next_tag(fdt, offset, &p.nextoffset);
508 stop_at = p.nextoffset;
509
510 switch (tag) {
511 case FDT_PROP:
512 stop_at = offset;
513 prop = fdt_get_property_by_offset(fdt, offset, NULL);
514 str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
515 val = h_include(priv, fdt, last_node, FDT_IS_PROP, str,
516 strlen(str) + 1);
517 if (val == -1) {
518 include = p.want >= WANT_NODES_AND_PROPS;
519 } else {
520 include = val;
521
522
523
524
525
526
527
528 if ((flags & FDT_REG_SUPERNODES) && val &&
529 !p.want)
530 p.want = WANT_NODES_ONLY;
531 }
532
533
534 break;
535
536 case FDT_NOP:
537 include = p.want >= WANT_NODES_AND_PROPS;
538 stop_at = offset;
539 break;
540
541 case FDT_BEGIN_NODE:
542 last_node = offset;
543 p.depth++;
544 if (p.depth == FDT_MAX_DEPTH)
545 return -FDT_ERR_BADSTRUCTURE;
546 name = fdt_get_name(fdt, offset, &len);
547 if (p.end - path + 2 + len >= path_len)
548 return -FDT_ERR_NOSPACE;
549
550
551 if (p.end != path + 1)
552 *p.end++ = '/';
553 strcpy(p.end, name);
554 p.end += len;
555 info->stack[p.depth].want = p.want;
556 info->stack[p.depth].offset = offset;
557
558
559
560
561
562 if (p.want == WANT_NODES_ONLY ||
563 !(flags & (FDT_REG_DIRECT_SUBNODES |
564 FDT_REG_ALL_SUBNODES))) {
565 stop_at = offset;
566 p.want = WANT_NOTHING;
567 }
568 val = h_include(priv, fdt, offset, FDT_IS_NODE, path,
569 p.end - path + 1);
570
571
572 if (val) {
573 p.want = (flags & FDT_REG_ALL_SUBNODES) ?
574 WANT_ALL_NODES_AND_PROPS :
575 WANT_NODES_AND_PROPS;
576 }
577
578
579 else if (p.want) {
580 if (p.want != WANT_ALL_NODES_AND_PROPS)
581 p.want--;
582
583
584 } else {
585 stop_at = offset;
586 }
587
588
589
590
591
592 include = p.want;
593 info->stack[p.depth].included = include;
594 break;
595
596 case FDT_END_NODE:
597 include = p.want;
598 if (p.depth < 0)
599 return -FDT_ERR_BADSTRUCTURE;
600
601
602
603
604
605 if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES))
606 stop_at = offset;
607 p.want = info->stack[p.depth].want;
608 p.depth--;
609 while (p.end > path && *--p.end != '/')
610 ;
611 *p.end = '\0';
612 break;
613
614 case FDT_END:
615
616 include = 1;
617 p.done = FDT_DONE_STRUCT;
618 break;
619 }
620
621
622 if (include && info->start == -1) {
623
624 if (flags & FDT_REG_SUPERNODES) {
625 if (fdt_include_supernodes(info, p.depth))
626 return 0;
627 }
628 info->start = offset;
629 }
630
631
632
633
634
635 if (!include && info->start != -1) {
636 if (fdt_add_region(info, base + info->start,
637 stop_at - info->start))
638 return 0;
639 info->start = -1;
640 info->can_merge = 1;
641 }
642
643
644 info->ptrs = p;
645 }
646
647
648 if (info->ptrs.done < FDT_DONE_END) {
649 if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt))
650 return -FDT_ERR_BADSTRUCTURE;
651
652 if (fdt_add_region(info, base + info->start,
653 info->ptrs.nextoffset - info->start))
654 return 0;
655 info->ptrs.done++;
656 }
657 if (info->ptrs.done < FDT_DONE_STRINGS) {
658 if (flags & FDT_REG_ADD_STRING_TAB) {
659 info->can_merge = 0;
660 if (fdt_off_dt_strings(fdt) <
661 base + info->ptrs.nextoffset)
662 return -FDT_ERR_BADLAYOUT;
663 if (fdt_add_region(info, fdt_off_dt_strings(fdt),
664 fdt_size_dt_strings(fdt)))
665 return 0;
666 }
667 info->ptrs.done++;
668 }
669
670 return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND;
671}
672