1// See LICENSE for license details.
2
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6#include "config.h"
7#include "fdt.h"
8#include "mtrap.h"
9
10static inline uint32_t bswap(uint32_t x)
11{
12  uint32_t y = (x & 0x00FF00FF) <<  8 | (x & 0xFF00FF00) >>  8;
13  uint32_t z = (y & 0x0000FFFF) << 16 | (y & 0xFFFF0000) >> 16;
14  return z;
15}
16
17static inline int isstring(char c)
18{
19  if (c >= 'A' && c <= 'Z')
20    return 1;
21  if (c >= 'a' && c <= 'z')
22    return 1;
23  if (c >= '0' && c <= '9')
24    return 1;
25  if (c == '\0' || c == ' ' || c == ',' || c == '-')
26    return 1;
27  return 0;
28}
29
30static uint32_t *fdt_scan_helper(
31  uint32_t *lex,
32  const char *strings,
33  struct fdt_scan_node *node,
34  const struct fdt_cb *cb)
35{
36  struct fdt_scan_node child;
37  struct fdt_scan_prop prop;
38  int last = 0;
39
40  child.parent = node;
41  // these are the default cell counts, as per the FDT spec
42  child.address_cells = 2;
43  child.size_cells = 1;
44  prop.node = node;
45
46  while (1) {
47    switch (bswap(lex[0])) {
48      case FDT_NOP: {
49        lex += 1;
50        break;
51      }
52      case FDT_PROP: {
53        assert (!last);
54        prop.name  = strings + bswap(lex[2]);
55        prop.len   = bswap(lex[1]);
56        prop.value = lex + 3;
57        if (node && !strcmp(prop.name, "#address-cells")) { node->address_cells = bswap(lex[3]); }
58        if (node && !strcmp(prop.name, "#size-cells"))    { node->size_cells    = bswap(lex[3]); }
59        lex += 3 + (prop.len+3)/4;
60        cb->prop(&prop, cb->extra);
61        break;
62      }
63      case FDT_BEGIN_NODE: {
64        uint32_t *lex_next;
65        if (!last && node && cb->done) cb->done(node, cb->extra);
66        last = 1;
67        child.name = (const char *)(lex+1);
68        if (cb->open) cb->open(&child, cb->extra);
69        lex_next = fdt_scan_helper(
70          lex + 2 + strlen(child.name)/4,
71          strings, &child, cb);
72        if (cb->close && cb->close(&child, cb->extra) == -1)
73          while (lex != lex_next) *lex++ = bswap(FDT_NOP);
74        lex = lex_next;
75        break;
76      }
77      case FDT_END_NODE: {
78        if (!last && node && cb->done) cb->done(node, cb->extra);
79        return lex + 1;
80      }
81      default: { // FDT_END
82        if (!last && node && cb->done) cb->done(node, cb->extra);
83        return lex;
84      }
85    }
86  }
87}
88
89void fdt_scan(uintptr_t fdt, const struct fdt_cb *cb)
90{
91  struct fdt_header *header = (struct fdt_header *)fdt;
92
93  // Only process FDT that we understand
94  if (bswap(header->magic) != FDT_MAGIC ||
95      bswap(header->last_comp_version) > FDT_VERSION) return;
96
97  const char *strings = (const char *)(fdt + bswap(header->off_dt_strings));
98  uint32_t *lex = (uint32_t *)(fdt + bswap(header->off_dt_struct));
99
100  fdt_scan_helper(lex, strings, 0, cb);
101}
102
103uint32_t fdt_size(uintptr_t fdt)
104{
105  struct fdt_header *header = (struct fdt_header *)fdt;
106
107  // Only process FDT that we understand
108  if (bswap(header->magic) != FDT_MAGIC ||
109      bswap(header->last_comp_version) > FDT_VERSION) return 0;
110  return bswap(header->totalsize);
111}
112
113const uint32_t *fdt_get_address(const struct fdt_scan_node *node, const uint32_t *value, uint64_t *result)
114{
115  *result = 0;
116  for (int cells = node->address_cells; cells > 0; --cells)
117    *result = (*result << 32) + bswap(*value++);
118  return value;
119}
120
121const uint32_t *fdt_get_size(const struct fdt_scan_node *node, const uint32_t *value, uint64_t *result)
122{
123  *result = 0;
124  for (int cells = node->size_cells; cells > 0; --cells)
125    *result = (*result << 32) + bswap(*value++);
126  return value;
127}
128
129int fdt_string_list_index(const struct fdt_scan_prop *prop, const char *str)
130{
131  const char *list = (const char *)prop->value;
132  const char *end = list + prop->len;
133  int index = 0;
134  while (end - list > 0) {
135    if (!strcmp(list, str)) return index;
136    ++index;
137    list += strlen(list) + 1;
138  }
139  return -1;
140}
141
142const uint32_t *fdt_get_value(const uint32_t *value, uint32_t *result)
143{
144  *result = bswap(*value++);
145  return value;
146}
147//////////////////////////////////////////// MEMORY SCAN /////////////////////////////////////////
148
149struct mem_scan {
150  int memory;
151  const uint32_t *reg_value;
152  int reg_len;
153};
154
155static void mem_open(const struct fdt_scan_node *node, void *extra)
156{
157  struct mem_scan *scan = (struct mem_scan *)extra;
158  memset(scan, 0, sizeof(*scan));
159}
160
161static void mem_prop(const struct fdt_scan_prop *prop, void *extra)
162{
163  struct mem_scan *scan = (struct mem_scan *)extra;
164  if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "memory")) {
165    scan->memory = 1;
166  } else if (!strcmp(prop->name, "reg")) {
167    scan->reg_value = prop->value;
168    scan->reg_len = prop->len;
169  }
170}
171
172static void mem_done(const struct fdt_scan_node *node, void *extra)
173{
174  struct mem_scan *scan = (struct mem_scan *)extra;
175  const uint32_t *value = scan->reg_value;
176  const uint32_t *end = value + scan->reg_len/4;
177  uintptr_t self = (uintptr_t)mem_done;
178
179  if (!scan->memory) return;
180  assert (scan->reg_value && scan->reg_len % 4 == 0);
181
182  while (end - value > 0) {
183    uint64_t base, size;
184    value = fdt_get_address(node->parent, value, &base);
185    value = fdt_get_size   (node->parent, value, &size);
186    if (base <= self && self <= base + size) { mem_size = size; }
187  }
188  assert (end == value);
189}
190
191void query_mem(uintptr_t fdt)
192{
193  struct fdt_cb cb;
194  struct mem_scan scan;
195
196  memset(&cb, 0, sizeof(cb));
197  cb.open = mem_open;
198  cb.prop = mem_prop;
199  cb.done = mem_done;
200  cb.extra = &scan;
201
202  mem_size = 0;
203  fdt_scan(fdt, &cb);
204  assert (mem_size > 0);
205}
206
207///////////////////////////////////////////// HART SCAN //////////////////////////////////////////
208
209static uint32_t hart_phandles[MAX_HARTS];
210uint64_t hart_mask;
211
212struct hart_scan {
213  const struct fdt_scan_node *cpu;
214  int hart;
215  const struct fdt_scan_node *controller;
216  int cells;
217  uint32_t phandle;
218};
219
220static void hart_open(const struct fdt_scan_node *node, void *extra)
221{
222  struct hart_scan *scan = (struct hart_scan *)extra;
223  if (!scan->cpu) {
224    scan->hart = -1;
225  }
226  if (!scan->controller) {
227    scan->cells = 0;
228    scan->phandle = 0;
229  }
230}
231
232static void hart_prop(const struct fdt_scan_prop *prop, void *extra)
233{
234  struct hart_scan *scan = (struct hart_scan *)extra;
235  if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "cpu")) {
236    assert (!scan->cpu);
237    scan->cpu = prop->node;
238  } else if (!strcmp(prop->name, "interrupt-controller")) {
239    assert (!scan->controller);
240    scan->controller = prop->node;
241  } else if (!strcmp(prop->name, "#interrupt-cells")) {
242    scan->cells = bswap(prop->value[0]);
243  } else if (!strcmp(prop->name, "phandle")) {
244    scan->phandle = bswap(prop->value[0]);
245  } else if (!strcmp(prop->name, "reg")) {
246    uint64_t reg;
247    fdt_get_address(prop->node->parent, prop->value, &reg);
248    scan->hart = reg;
249  }
250}
251
252static void hart_done(const struct fdt_scan_node *node, void *extra)
253{
254  struct hart_scan *scan = (struct hart_scan *)extra;
255
256  if (scan->cpu == node) {
257    assert (scan->hart >= 0);
258  }
259
260  if (scan->controller == node && scan->cpu) {
261    assert (scan->phandle > 0);
262    assert (scan->cells == 1);
263
264    if (scan->hart < MAX_HARTS) {
265      hart_phandles[scan->hart] = scan->phandle;
266      hart_mask |= 1 << scan->hart;
267      hls_init(scan->hart);
268    }
269  }
270}
271
272static int hart_close(const struct fdt_scan_node *node, void *extra)
273{
274  struct hart_scan *scan = (struct hart_scan *)extra;
275  if (scan->cpu == node) scan->cpu = 0;
276  if (scan->controller == node) scan->controller = 0;
277  return 0;
278}
279
280void query_harts(uintptr_t fdt)
281{
282  struct fdt_cb cb;
283  struct hart_scan scan;
284
285  memset(&cb, 0, sizeof(cb));
286  memset(&scan, 0, sizeof(scan));
287  cb.open = hart_open;
288  cb.prop = hart_prop;
289  cb.done = hart_done;
290  cb.close= hart_close;
291  cb.extra = &scan;
292
293  fdt_scan(fdt, &cb);
294
295  // The current hart should have been detected
296  assert ((hart_mask >> read_csr(mhartid)) != 0);
297}
298
299///////////////////////////////////////////// CLINT SCAN /////////////////////////////////////////
300
301struct clint_scan
302{
303  int compat;
304  uint64_t reg;
305  const uint32_t *int_value;
306  int int_len;
307  int done;
308};
309
310static void clint_open(const struct fdt_scan_node *node, void *extra)
311{
312  struct clint_scan *scan = (struct clint_scan *)extra;
313  scan->compat = 0;
314  scan->reg = 0;
315  scan->int_value = 0;
316}
317
318static void clint_prop(const struct fdt_scan_prop *prop, void *extra)
319{
320  struct clint_scan *scan = (struct clint_scan *)extra;
321  if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, "riscv,clint0") >= 0) {
322    scan->compat = 1;
323  } else if (!strcmp(prop->name, "reg")) {
324    fdt_get_address(prop->node->parent, prop->value, &scan->reg);
325  } else if (!strcmp(prop->name, "interrupts-extended")) {
326    scan->int_value = prop->value;
327    scan->int_len = prop->len;
328  }
329}
330
331static void clint_done(const struct fdt_scan_node *node, void *extra)
332{
333  struct clint_scan *scan = (struct clint_scan *)extra;
334  const uint32_t *value = scan->int_value;
335  const uint32_t *end = value + scan->int_len/4;
336
337  if (!scan->compat) return;
338  assert (scan->reg != 0);
339  assert (scan->int_value && scan->int_len % 16 == 0);
340  assert (!scan->done); // only one clint
341
342  scan->done = 1;
343  mtime = (void*)((uintptr_t)scan->reg + 0xbff8);
344
345  for (int index = 0; end - value > 0; ++index) {
346    uint32_t phandle = bswap(value[0]);
347    int hart;
348    for (hart = 0; hart < MAX_HARTS; ++hart)
349      if (hart_phandles[hart] == phandle)
350        break;
351    if (hart < MAX_HARTS) {
352      hls_t *hls = OTHER_HLS(hart);
353      hls->ipi = (void*)((uintptr_t)scan->reg + index * 4);
354      hls->timecmp = (void*)((uintptr_t)scan->reg + 0x4000 + (index * 8));
355    }
356    value += 4;
357  }
358}
359
360void query_clint(uintptr_t fdt)
361{
362  struct fdt_cb cb;
363  struct clint_scan scan;
364
365  memset(&cb, 0, sizeof(cb));
366  cb.open = clint_open;
367  cb.prop = clint_prop;
368  cb.done = clint_done;
369  cb.extra = &scan;
370
371  scan.done = 0;
372  fdt_scan(fdt, &cb);
373  assert (scan.done);
374}
375
376///////////////////////////////////////////// PLIC SCAN /////////////////////////////////////////
377
378struct plic_scan
379{
380  int compat;
381  uint64_t reg;
382  uint32_t *int_value;
383  int int_len;
384  int done;
385  int ndev;
386};
387
388static void plic_open(const struct fdt_scan_node *node, void *extra)
389{
390  struct plic_scan *scan = (struct plic_scan *)extra;
391  scan->compat = 0;
392  scan->reg = 0;
393  scan->int_value = 0;
394}
395
396static void plic_prop(const struct fdt_scan_prop *prop, void *extra)
397{
398  struct plic_scan *scan = (struct plic_scan *)extra;
399  if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, "riscv,plic0") >= 0) {
400    scan->compat = 1;
401  } else if (!strcmp(prop->name, "reg")) {
402    fdt_get_address(prop->node->parent, prop->value, &scan->reg);
403  } else if (!strcmp(prop->name, "interrupts-extended")) {
404    scan->int_value = prop->value;
405    scan->int_len = prop->len;
406  } else if (!strcmp(prop->name, "riscv,ndev")) {
407    scan->ndev = bswap(prop->value[0]);
408  }
409}
410
411#define HART_BASE	0x200000
412#define HART_SIZE	0x1000
413#define ENABLE_BASE	0x2000
414#define ENABLE_SIZE	0x80
415
416static void plic_done(const struct fdt_scan_node *node, void *extra)
417{
418  struct plic_scan *scan = (struct plic_scan *)extra;
419  const uint32_t *value = scan->int_value;
420  const uint32_t *end = value + scan->int_len/4;
421
422  if (!scan->compat) return;
423  assert (scan->reg != 0);
424  assert (scan->int_value && scan->int_len % 8 == 0);
425  assert (scan->ndev >= 0 && scan->ndev < 1024);
426  assert (!scan->done); // only one plic
427
428  scan->done = 1;
429  plic_priorities = (uint32_t*)(uintptr_t)scan->reg;
430  plic_ndevs = scan->ndev;
431
432  for (int index = 0; end - value > 0; ++index) {
433    uint32_t phandle = bswap(value[0]);
434    uint32_t cpu_int = bswap(value[1]);
435    int hart;
436    for (hart = 0; hart < MAX_HARTS; ++hart)
437      if (hart_phandles[hart] == phandle)
438        break;
439    if (hart < MAX_HARTS) {
440      hls_t *hls = OTHER_HLS(hart);
441      if (cpu_int == IRQ_M_EXT) {
442        hls->plic_m_ie     = (uintptr_t*)((uintptr_t)scan->reg + ENABLE_BASE + ENABLE_SIZE * index);
443        hls->plic_m_thresh = (uint32_t*) ((uintptr_t)scan->reg + HART_BASE   + HART_SIZE   * index);
444      } else if (cpu_int == IRQ_S_EXT) {
445        hls->plic_s_ie     = (uintptr_t*)((uintptr_t)scan->reg + ENABLE_BASE + ENABLE_SIZE * index);
446        hls->plic_s_thresh = (uint32_t*) ((uintptr_t)scan->reg + HART_BASE   + HART_SIZE   * index);
447      } else {
448        printm("PLIC wired hart %d to wrong interrupt %d", hart, cpu_int);
449      }
450    }
451    value += 2;
452  }
453#if 0
454  printm("PLIC: prio %x devs %d\r\n", (uint32_t)(uintptr_t)plic_priorities, plic_ndevs);
455  for (int i = 0; i < MAX_HARTS; ++i) {
456    hls_t *hls = OTHER_HLS(i);
457    printm("CPU %d: %x %x %x %x\r\n", i, (uint32_t)(uintptr_t)hls->plic_m_ie, (uint32_t)(uintptr_t)hls->plic_m_thresh, (uint32_t)(uintptr_t)hls->plic_s_ie, (uint32_t)(uintptr_t)hls->plic_s_thresh);
458  }
459#endif
460}
461
462void query_plic(uintptr_t fdt)
463{
464  struct fdt_cb cb;
465  struct plic_scan scan;
466
467  memset(&cb, 0, sizeof(cb));
468  cb.open = plic_open;
469  cb.prop = plic_prop;
470  cb.done = plic_done;
471  cb.extra = &scan;
472
473  scan.done = 0;
474  fdt_scan(fdt, &cb);
475}
476
477static void plic_redact(const struct fdt_scan_node *node, void *extra)
478{
479  struct plic_scan *scan = (struct plic_scan *)extra;
480  uint32_t *value = scan->int_value;
481  uint32_t *end = value + scan->int_len/4;
482
483  if (!scan->compat) return;
484  scan->done = 1;
485
486  while (end - value > 0) {
487    if (bswap(value[1]) == IRQ_M_EXT) value[1] = bswap(-1);
488    value += 2;
489  }
490}
491
492void filter_plic(uintptr_t fdt)
493{
494  struct fdt_cb cb;
495  struct plic_scan scan;
496
497  memset(&cb, 0, sizeof(cb));
498  cb.open = plic_open;
499  cb.prop = plic_prop;
500  cb.done = plic_redact;
501  cb.extra = &scan;
502
503  scan.done = 0;
504  fdt_scan(fdt, &cb);
505}
506
507//////////////////////////////////////////// COMPAT SCAN ////////////////////////////////////////
508
509struct compat_scan
510{
511  const char *compat;
512  int depth;
513  int kill;
514};
515
516static void compat_open(const struct fdt_scan_node *node, void *extra)
517{
518  struct compat_scan *scan = (struct compat_scan *)extra;
519  ++scan->depth;
520}
521
522static void compat_prop(const struct fdt_scan_prop *prop, void *extra)
523{
524  struct compat_scan *scan = (struct compat_scan *)extra;
525  if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, scan->compat) >= 0)
526    if (scan->depth < scan->kill)
527      scan->kill = scan->depth;
528}
529
530static int compat_close(const struct fdt_scan_node *node, void *extra)
531{
532  struct compat_scan *scan = (struct compat_scan *)extra;
533  if (scan->kill == scan->depth--) {
534    scan->kill = 999;
535    return -1;
536  } else {
537    return 0;
538  }
539}
540
541void filter_compat(uintptr_t fdt, const char *compat)
542{
543  struct fdt_cb cb;
544  struct compat_scan scan;
545
546  memset(&cb, 0, sizeof(cb));
547  cb.open = compat_open;
548  cb.prop = compat_prop;
549  cb.close = compat_close;
550  cb.extra = &scan;
551
552  scan.compat = compat;
553  scan.depth = 0;
554  scan.kill = 999;
555  fdt_scan(fdt, &cb);
556}
557
558//////////////////////////////////////////// CHOSEN SCAN ////////////////////////////////////////
559
560struct chosen_scan {
561  const struct fdt_scan_node *chosen;
562  void* kernel_start;
563  void* kernel_end;
564};
565
566static void chosen_open(const struct fdt_scan_node *node, void *extra)
567{
568  struct chosen_scan *scan = (struct chosen_scan *)extra;
569  if (!strcmp(node->name, "chosen")) {
570    scan->chosen = node;
571  }
572}
573
574static int chosen_close(const struct fdt_scan_node *node, void *extra)
575{
576  struct chosen_scan *scan = (struct chosen_scan *)extra;
577  if (scan->chosen && scan->chosen == node) {
578    scan->chosen = NULL;
579  }
580  return 0;
581}
582
583static void chosen_prop(const struct fdt_scan_prop *prop, void *extra)
584{
585  struct chosen_scan *scan = (struct chosen_scan *)extra;
586  uint64_t val;
587  if (!scan->chosen) return;
588  if (!strcmp(prop->name, "riscv,kernel-start")) {
589    fdt_get_address(prop->node->parent, prop->value, &val);
590    scan->kernel_start = (void*)(uintptr_t)val;
591  } else if (!strcmp(prop->name, "riscv,kernel-end")) {
592    fdt_get_address(prop->node->parent, prop->value, &val);
593    scan->kernel_end = (void*)(uintptr_t)val;
594  }
595}
596
597void query_chosen(uintptr_t fdt)
598{
599  struct fdt_cb cb;
600  struct chosen_scan chosen;
601
602  memset(&cb, 0, sizeof(cb));
603  cb.open = chosen_open;
604  cb.close = chosen_close;
605  cb.prop = chosen_prop;
606
607  memset(&chosen, 0, sizeof(chosen));
608  cb.extra = &chosen;
609
610  fdt_scan(fdt, &cb);
611  kernel_start = chosen.kernel_start;
612  kernel_end = chosen.kernel_end;
613}
614
615//////////////////////////////////////////// HART FILTER ////////////////////////////////////////
616
617struct hart_filter {
618  int compat;
619  int hart;
620  char *status;
621  char *mmu_type;
622  long *disabled_hart_mask;
623};
624
625static void hart_filter_open(const struct fdt_scan_node *node, void *extra)
626{
627  struct hart_filter *filter = (struct hart_filter *)extra;
628  filter->status = NULL;
629  filter->mmu_type = NULL;
630  filter->compat = 0;
631  filter->hart = -1;
632}
633
634static void hart_filter_prop(const struct fdt_scan_prop *prop, void *extra)
635{
636  struct hart_filter *filter = (struct hart_filter *)extra;
637  if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "cpu")) {
638    filter->compat = 1;
639  } else if (!strcmp(prop->name, "reg")) {
640    uint64_t reg;
641    fdt_get_address(prop->node->parent, prop->value, &reg);
642    filter->hart = reg;
643  } else if (!strcmp(prop->name, "status")) {
644    filter->status = (char*)prop->value;
645  } else if (!strcmp(prop->name, "mmu-type")) {
646    filter->mmu_type = (char*)prop->value;
647  }
648}
649
650static bool hart_filter_mask(const struct hart_filter *filter)
651{
652  if (filter->mmu_type == NULL) return true;
653  if (strcmp(filter->status, "okay")) return true;
654  if (!strcmp(filter->mmu_type, "riscv,sv32")) return false;
655  if (!strcmp(filter->mmu_type, "riscv,sv39")) return false;
656  if (!strcmp(filter->mmu_type, "riscv,sv48")) return false;
657  printm("hart_filter_mask saw unknown hart type: status=\"%s\", mmu_type=\"%s\"\n",
658         filter->status, filter->mmu_type);
659  return true;
660}
661
662static void hart_filter_done(const struct fdt_scan_node *node, void *extra)
663{
664  struct hart_filter *filter = (struct hart_filter *)extra;
665
666  if (!filter->compat) return;
667  assert (filter->status);
668  assert (filter->hart >= 0);
669
670  if (hart_filter_mask(filter)) {
671    strcpy(filter->status, "masked");
672    uint32_t *len = (uint32_t*)filter->status;
673    len[-2] = bswap(strlen("masked")+1);
674    *filter->disabled_hart_mask |= (1 << filter->hart);
675  }
676}
677
678void filter_harts(uintptr_t fdt, long *disabled_hart_mask)
679{
680  struct fdt_cb cb;
681  struct hart_filter filter;
682
683  memset(&cb, 0, sizeof(cb));
684  cb.open = hart_filter_open;
685  cb.prop = hart_filter_prop;
686  cb.done = hart_filter_done;
687  cb.extra = &filter;
688
689  filter.disabled_hart_mask = disabled_hart_mask;
690  *disabled_hart_mask = 0;
691  fdt_scan(fdt, &cb);
692}
693
694//////////////////////////////////////////// PRINT //////////////////////////////////////////////
695
696#ifdef PK_PRINT_DEVICE_TREE
697#define FDT_PRINT_MAX_DEPTH 32
698
699struct fdt_print_info {
700  int depth;
701  const struct fdt_scan_node *stack[FDT_PRINT_MAX_DEPTH];
702};
703
704void fdt_print_printm(struct fdt_print_info *info, const char *format, ...)
705{
706  va_list vl;
707
708  for (int i = 0; i < info->depth; ++i)
709    printm("  ");
710
711  va_start(vl, format);
712  vprintm(format, vl);
713  va_end(vl);
714}
715
716static void fdt_print_open(const struct fdt_scan_node *node, void *extra)
717{
718  struct fdt_print_info *info = (struct fdt_print_info *)extra;
719
720  while (node->parent != NULL && info->stack[info->depth-1] != node->parent) {
721    info->depth--;
722    fdt_print_printm(info, "}\r\n");
723  }
724
725  fdt_print_printm(info, "%s {\r\n", node->name);
726  info->stack[info->depth] = node;
727  info->depth++;
728}
729
730static void fdt_print_prop(const struct fdt_scan_prop *prop, void *extra)
731{
732  struct fdt_print_info *info = (struct fdt_print_info *)extra;
733  int asstring = 1;
734  char *char_data = (char *)(prop->value);
735
736  fdt_print_printm(info, "%s", prop->name);
737
738  if (prop->len == 0) {
739    printm(";\r\n");
740    return;
741  } else {
742    printm(" = ");
743  }
744
745  /* It appears that dtc uses a hueristic to detect strings so I'm using a
746   * similar one here. */
747  for (int i = 0; i < prop->len; ++i) {
748    if (!isstring(char_data[i]))
749      asstring = 0;
750    if (i > 0 && char_data[i] == '\0' && char_data[i-1] == '\0')
751      asstring = 0;
752  }
753
754  if (asstring) {
755    for (size_t i = 0; i < prop->len; i += strlen(char_data + i) + 1) {
756      if (i != 0)
757        printm(", ");
758      printm("\"%s\"", char_data + i);
759    }
760  } else {
761    printm("<");
762    for (size_t i = 0; i < prop->len/4; ++i) {
763      if (i != 0)
764        printm(" ");
765      printm("0x%08x", bswap(prop->value[i]));
766    }
767    printm(">");
768  }
769
770  printm(";\r\n");
771}
772
773static void fdt_print_done(const struct fdt_scan_node *node, void *extra)
774{
775  struct fdt_print_info *info = (struct fdt_print_info *)extra;
776}
777
778static int fdt_print_close(const struct fdt_scan_node *node, void *extra)
779{
780  struct fdt_print_info *info = (struct fdt_print_info *)extra;
781  return 0;
782}
783
784void fdt_print(uintptr_t fdt)
785{
786  struct fdt_print_info info;
787  struct fdt_cb cb;
788
789  info.depth = 0;
790
791  memset(&cb, 0, sizeof(cb));
792  cb.open = fdt_print_open;
793  cb.prop = fdt_print_prop;
794  cb.done = fdt_print_done;
795  cb.close = fdt_print_close;
796  cb.extra = &info;
797
798  fdt_scan(fdt, &cb);
799
800  while (info.depth > 0) {
801    info.depth--;
802    fdt_print_printm(&info, "}\r\n");
803  }
804}
805#endif
806