1
2
3
4
5
6
7
8
9
10
11
12
13
14#include "qemu/osdep.h"
15
16#ifdef CONFIG_LINUX
17#include <dirent.h>
18#endif
19
20#include "qapi/error.h"
21#include "qemu-common.h"
22#include "qemu/error-report.h"
23#include "qemu/bswap.h"
24#include "sysemu/device_tree.h"
25#include "sysemu/sysemu.h"
26#include "hw/loader.h"
27#include "hw/boards.h"
28#include "qemu/config-file.h"
29#include "qemu/log.h"
30
31#include <libfdt.h>
32
33#define FDT_MAX_SIZE 0x10000
34
35void *create_device_tree(int *sizep)
36{
37 void *fdt;
38 int ret;
39
40 *sizep = FDT_MAX_SIZE;
41 fdt = g_malloc0(FDT_MAX_SIZE);
42 ret = fdt_create(fdt, FDT_MAX_SIZE);
43 if (ret < 0) {
44 goto fail;
45 }
46 ret = fdt_finish_reservemap(fdt);
47 if (ret < 0) {
48 goto fail;
49 }
50 ret = fdt_begin_node(fdt, "");
51 if (ret < 0) {
52 goto fail;
53 }
54 ret = fdt_end_node(fdt);
55 if (ret < 0) {
56 goto fail;
57 }
58 ret = fdt_finish(fdt);
59 if (ret < 0) {
60 goto fail;
61 }
62 ret = fdt_open_into(fdt, fdt, *sizep);
63 if (ret) {
64 error_report("Unable to copy device tree in memory");
65 exit(1);
66 }
67
68 return fdt;
69fail:
70 error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
71 exit(1);
72}
73
74void *load_device_tree(const char *filename_path, int *sizep)
75{
76 int dt_size;
77 int dt_file_load_size;
78 int ret;
79 void *fdt = NULL;
80
81 if (sizep) {
82 *sizep = 0;
83 }
84 dt_size = get_image_size(filename_path);
85 if (dt_size < 0) {
86 error_report("Unable to get size of device tree file '%s'",
87 filename_path);
88 goto fail;
89 }
90
91
92 dt_size += 10000;
93 dt_size *= 2;
94
95 fdt = g_malloc0(dt_size);
96
97 dt_file_load_size = load_image(filename_path, fdt);
98 if (dt_file_load_size < 0) {
99 error_report("Unable to open device tree file '%s'",
100 filename_path);
101 goto fail;
102 }
103
104 ret = fdt_open_into(fdt, fdt, dt_size);
105 if (ret) {
106 error_report("Unable to copy device tree in memory");
107 goto fail;
108 }
109
110
111 if (fdt_check_header(fdt)) {
112 error_report("Device tree file loaded into memory is invalid: %s",
113 filename_path);
114 goto fail;
115 }
116 if (sizep) {
117 *sizep = dt_size;
118 }
119 return fdt;
120
121fail:
122 g_free(fdt);
123 return NULL;
124}
125
126#ifdef CONFIG_LINUX
127
128#define SYSFS_DT_BASEDIR "/proc/device-tree"
129
130
131
132
133
134
135
136
137
138
139static void read_fstree(void *fdt, const char *dirname)
140{
141 DIR *d;
142 struct dirent *de;
143 struct stat st;
144 const char *root_dir = SYSFS_DT_BASEDIR;
145 const char *parent_node;
146
147 if (strstr(dirname, root_dir) != dirname) {
148 error_setg(&error_fatal, "%s: %s must be searched within %s",
149 __func__, dirname, root_dir);
150 }
151 parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];
152
153 d = opendir(dirname);
154 if (!d) {
155 error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
156 return;
157 }
158
159 while ((de = readdir(d)) != NULL) {
160 char *tmpnam;
161
162 if (!g_strcmp0(de->d_name, ".")
163 || !g_strcmp0(de->d_name, "..")) {
164 continue;
165 }
166
167 tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
168
169 if (lstat(tmpnam, &st) < 0) {
170 error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam);
171 }
172
173 if (S_ISREG(st.st_mode)) {
174 gchar *val;
175 gsize len;
176
177 if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
178 error_setg(&error_fatal, "%s not able to extract info from %s",
179 __func__, tmpnam);
180 }
181
182 if (strlen(parent_node) > 0) {
183 qemu_fdt_setprop(fdt, parent_node,
184 de->d_name, val, len);
185 } else {
186 qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
187 }
188 g_free(val);
189 } else if (S_ISDIR(st.st_mode)) {
190 char *node_name;
191
192 node_name = g_strdup_printf("%s/%s",
193 parent_node, de->d_name);
194 qemu_fdt_add_subnode(fdt, node_name);
195 g_free(node_name);
196 read_fstree(fdt, tmpnam);
197 }
198
199 g_free(tmpnam);
200 }
201
202 closedir(d);
203}
204
205
206void *load_device_tree_from_sysfs(void)
207{
208 void *host_fdt;
209 int host_fdt_size;
210
211 host_fdt = create_device_tree(&host_fdt_size);
212 read_fstree(host_fdt, SYSFS_DT_BASEDIR);
213 if (fdt_check_header(host_fdt)) {
214 error_setg(&error_fatal,
215 "%s host device tree extracted into memory is invalid",
216 __func__);
217 }
218 return host_fdt;
219}
220
221#endif
222
223static int findnode_nofail(void *fdt, const char *node_path)
224{
225 int offset;
226
227 offset = fdt_path_offset(fdt, node_path);
228 if (offset < 0) {
229 error_report("%s Couldn't find node %s: %s", __func__, node_path,
230 fdt_strerror(offset));
231 exit(1);
232 }
233
234 return offset;
235}
236
237char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
238 Error **errp)
239{
240 int offset, len, ret;
241 const char *iter_name;
242 unsigned int path_len = 16, n = 0;
243 GSList *path_list = NULL, *iter;
244 char **path_array;
245
246 offset = fdt_node_offset_by_compatible(fdt, -1, compat);
247
248 while (offset >= 0) {
249 iter_name = fdt_get_name(fdt, offset, &len);
250 if (!iter_name) {
251 offset = len;
252 break;
253 }
254 if (!strcmp(iter_name, name)) {
255 char *path;
256
257 path = g_malloc(path_len);
258 while ((ret = fdt_get_path(fdt, offset, path, path_len))
259 == -FDT_ERR_NOSPACE) {
260 path_len += 16;
261 path = g_realloc(path, path_len);
262 }
263 path_list = g_slist_prepend(path_list, path);
264 n++;
265 }
266 offset = fdt_node_offset_by_compatible(fdt, offset, compat);
267 }
268
269 if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
270 error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
271 __func__, name, compat, fdt_strerror(offset));
272 for (iter = path_list; iter; iter = iter->next) {
273 g_free(iter->data);
274 }
275 g_slist_free(path_list);
276 return NULL;
277 }
278
279 path_array = g_new(char *, n + 1);
280 path_array[n--] = NULL;
281
282 for (iter = path_list; iter; iter = iter->next) {
283 path_array[n--] = iter->data;
284 }
285
286 g_slist_free(path_list);
287
288 return path_array;
289}
290
291int qemu_fdt_setprop(void *fdt, const char *node_path,
292 const char *property, const void *val, int size)
293{
294 int r;
295
296 r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
297 if (r < 0) {
298 error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
299 property, fdt_strerror(r));
300 exit(1);
301 }
302
303 return r;
304}
305
306int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
307 const char *property, uint32_t val)
308{
309 int r;
310
311 r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
312 if (r < 0) {
313 error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
314 node_path, property, val, fdt_strerror(r));
315 exit(1);
316 }
317
318 return r;
319}
320
321int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
322 const char *property, uint64_t val)
323{
324 val = cpu_to_be64(val);
325 return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
326}
327
328int qemu_fdt_setprop_string(void *fdt, const char *node_path,
329 const char *property, const char *string)
330{
331 int r;
332
333 r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
334 if (r < 0) {
335 error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
336 node_path, property, string, fdt_strerror(r));
337 exit(1);
338 }
339
340 return r;
341}
342
343void *qemu_fdt_getprop(void *fdt, const char *node_path,
344 const char *property, int *lenp,
345 bool inherit, Error **errp)
346{
347 int len;
348 const void *r;
349
350 if (!lenp) {
351 lenp = &len;
352 }
353 r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
354 if (!r) {
355 char parent[DT_PATH_LENGTH];
356 if (inherit && !qemu_devtree_getparent(fdt, parent, node_path)) {
357 return qemu_fdt_getprop(fdt, parent, property, lenp, true, errp);
358 }
359 error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
360 node_path, property, fdt_strerror(*lenp));
361 return NULL;
362 }
363 return g_memdup(r, *lenp);
364}
365
366char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
367 const char *property, int cell,
368 bool inherit, Error **errp)
369{
370 int len;
371 void *prop;
372 Error *err= NULL;
373
374 if (!errp) {
375 errp = &err;
376 }
377
378 prop = qemu_fdt_getprop(fdt, node_path, property, &len, inherit, errp);
379 if (*errp) {
380 return NULL;
381 }
382 while (cell) {
383 void *term = memchr(prop, '\0', len);
384 size_t diff;
385
386 if (!term) {
387 error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
388 node_path, property, fdt_strerror(len));
389 return NULL;
390 }
391 diff = term - prop + 1;
392 len -= diff;
393 assert(len >= 0);
394 prop += diff;
395 cell--;
396 }
397 if (!*(char *)prop) {
398 error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
399 node_path, property, fdt_strerror(len));
400 return NULL;
401 }
402 return len ? prop : NULL;
403}
404
405uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
406 const char *property, int offset,
407 bool inherit, Error **errp)
408{
409 int len;
410 uint32_t ret;
411 uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len,
412 inherit, errp);
413 if (errp && *errp) {
414 return 0;
415 }
416 if (len < (offset+1)*4) {
417 error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
418 __func__, node_path, property);
419 return 0;
420 }
421 ret = be32_to_cpu(p[offset]);
422 g_free(p);
423 return ret;
424}
425
426uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
427 const char *property, int offset,
428 int size, Error **errp)
429{
430 uint64_t ret = 0;
431 for (;size ;size--) {
432 ret <<= 32;
433 ret |= qemu_fdt_getprop_cell(fdt, node_path, property, offset++, false,
434 errp);
435 if (errp && *errp) {
436 return 0;
437 }
438 }
439 return ret;
440}
441
442uint32_t qemu_fdt_check_phandle(void *fdt, const char *path)
443{
444 uint32_t r;
445
446 r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
447 if (r == 0) {
448 qemu_log("%s: Couldn't find phandle for %s: %s", __func__,
449 path, fdt_strerror(r));
450 }
451
452 return r;
453}
454
455uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
456{
457 uint32_t r;
458
459 r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
460 if (r == 0) {
461 error_report("%s: Couldn't get phandle for %s: %s", __func__,
462 path, fdt_strerror(r));
463 exit(1);
464 }
465
466 return r;
467}
468
469int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
470 const char *property,
471 const char *target_node_path)
472{
473 uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
474 return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
475}
476
477uint32_t qemu_fdt_alloc_phandle(void *fdt)
478{
479 static int phandle = 0x0;
480
481
482
483
484
485 if (!phandle) {
486 phandle = machine_phandle_start(current_machine);
487 }
488
489 if (!phandle) {
490
491
492
493
494 phandle = 0x8000;
495 }
496
497 return phandle++;
498}
499
500int qemu_fdt_nop_node(void *fdt, const char *node_path)
501{
502 int r;
503
504 r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
505 if (r < 0) {
506 error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
507 fdt_strerror(r));
508 exit(1);
509 }
510
511 return r;
512}
513
514int qemu_fdt_add_subnode(void *fdt, const char *name)
515{
516 char *dupname = g_strdup(name);
517 char *basename = strrchr(dupname, '/');
518 int retval;
519 int parent = 0;
520
521 if (!basename) {
522 g_free(dupname);
523 return -1;
524 }
525
526 basename[0] = '\0';
527 basename++;
528
529 if (dupname[0]) {
530 parent = findnode_nofail(fdt, dupname);
531 }
532
533 retval = fdt_add_subnode(fdt, parent, basename);
534 if (retval < 0) {
535 error_report("FDT: Failed to create subnode %s: %s", name,
536 fdt_strerror(retval));
537 exit(1);
538 }
539
540 g_free(dupname);
541 return retval;
542}
543
544void qemu_fdt_dumpdtb(void *fdt, int size)
545{
546 const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
547
548 if (dumpdtb) {
549
550 exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
551 }
552}
553
554int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
555 const char *node_path,
556 const char *property,
557 int numvalues,
558 uint64_t *values)
559{
560 uint32_t *propcells;
561 uint64_t value;
562 int cellnum, vnum, ncells;
563 uint32_t hival;
564 int ret;
565
566 propcells = g_new0(uint32_t, numvalues * 2);
567
568 cellnum = 0;
569 for (vnum = 0; vnum < numvalues; vnum++) {
570 ncells = values[vnum * 2];
571 if (ncells != 1 && ncells != 2) {
572 ret = -1;
573 goto out;
574 }
575 value = values[vnum * 2 + 1];
576 hival = cpu_to_be32(value >> 32);
577 if (ncells > 1) {
578 propcells[cellnum++] = hival;
579 } else if (hival != 0) {
580 ret = -1;
581 goto out;
582 }
583 propcells[cellnum++] = cpu_to_be32(value);
584 }
585
586 ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
587 cellnum * sizeof(uint32_t));
588out:
589 g_free(propcells);
590 return ret;
591}
592
593char *qemu_devtree_get_node_name(void *fdt, const char *node_path)
594{
595 const char *ret = fdt_get_name(fdt, fdt_path_offset(fdt, node_path), NULL);
596 return ret ? strdup(ret) : NULL;
597}
598
599int qemu_devtree_get_node_depth(void *fdt, const char *node_path)
600{
601 return fdt_node_depth(fdt, fdt_path_offset(fdt, node_path));
602}
603
604
605int qemu_devtree_num_props(void *fdt, const char *node_path)
606{
607 int offset = fdt_path_offset(fdt, node_path);
608 int ret = 0;
609
610 for (offset = fdt_first_property_offset(fdt, offset);
611 offset != -FDT_ERR_NOTFOUND;
612 offset = fdt_next_property_offset(fdt, offset)) {
613 ret++;
614 }
615 return ret;
616}
617
618QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path)
619{
620 QEMUDevtreeProp *ret = g_new0(QEMUDevtreeProp,
621 qemu_devtree_num_props(fdt, node_path) + 1);
622 int offset = fdt_path_offset(fdt, node_path);
623 int i = 0;
624
625 for (offset = fdt_first_property_offset(fdt, offset);
626 offset != -FDT_ERR_NOTFOUND;
627 offset = fdt_next_property_offset(fdt, offset)) {
628 const char *propname;
629 const void *val = fdt_getprop_by_offset(fdt, offset, &propname,
630 &ret[i].len);
631
632 ret[i].name = g_strdup(propname);
633 ret[i].value = g_memdup(val, ret[i].len);
634 i++;
635 }
636 return ret;
637}
638
639static void qemu_devtree_children_info(void *fdt, const char *node_path,
640 int depth, int *num, char **returned_paths) {
641 int offset = fdt_path_offset(fdt, node_path);
642 int root_depth = fdt_node_depth(fdt, offset);
643 int cur_depth = root_depth;
644
645 if (num) {
646 *num = 0;
647 }
648 for (;;) {
649 offset = fdt_next_node(fdt, offset, &cur_depth);
650 if (cur_depth <= root_depth) {
651 break;
652 }
653 if (cur_depth <= root_depth + depth || depth == 0) {
654 if (returned_paths) {
655 returned_paths[*num] = g_malloc0(DT_PATH_LENGTH);
656 fdt_get_path(fdt, offset, returned_paths[*num], DT_PATH_LENGTH);
657 }
658 if (num) {
659 (*num)++;
660 }
661 }
662 }
663}
664
665char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth)
666{
667 int num_children = qemu_devtree_get_num_children(fdt, node_path, depth);
668 char **ret = g_malloc0(sizeof(*ret) * num_children);
669
670 qemu_devtree_children_info(fdt, node_path, depth, &num_children, ret);
671 return ret;
672}
673
674int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth)
675{
676 int ret;
677
678 qemu_devtree_children_info(fdt, node_path, depth, &ret, NULL);
679 return ret;
680}
681
682int qemu_devtree_node_by_compatible(void *fdt, char *node_path,
683 const char *compats)
684{
685 int offset = fdt_node_offset_by_compatible(fdt, 0, compats);
686 return offset > 0 ?
687 fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1;
688}
689
690int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
691 const char *cmpname) {
692 int offset = 0;
693 char *name = NULL;
694
695 do {
696 char *at;
697
698 offset = fdt_next_node(fdt, offset, NULL);
699 name = (void *)fdt_get_name(fdt, offset, NULL);
700 if (!name) {
701 continue;
702 }
703 at = memchr(name, '@', strlen(name));
704 if (!strncmp(name, cmpname, at ? at - name : strlen(name) )) {
705 break;
706 }
707 } while (offset > 0);
708 return offset > 0 ?
709 fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1;
710}
711
712int qemu_devtree_get_n_nodes_by_name(void *fdt, char ***array,
713 const char *cmpname)
714{
715 int offset = 0;
716 char *name = NULL;
717 uint32_t n = 0;
718 char node_p[DT_PATH_LENGTH];
719 char **node_path = NULL;
720
721 do {
722 char *at;
723
724 offset = fdt_next_node(fdt, offset, NULL);
725 name = (void *)fdt_get_name(fdt, offset, NULL);
726
727 if (!name) {
728 continue;
729 }
730
731 at = memchr(name, '@', strlen(name));
732 if (!strncmp(name, cmpname, at ? at - name : strlen(name))) {
733 if (fdt_get_path(fdt, offset, node_p, DT_PATH_LENGTH) >= 0) {
734 if (node_path == NULL) {
735 node_path = (char **) g_new(char *, 1);
736 } else {
737 node_path = (char **) g_renew(char *, *node_path, n);
738 }
739 node_path[n] = g_strdup(node_p);
740 n++;
741 }
742 }
743 } while (offset > 0);
744
745 *array = node_path;
746 return n;
747}
748
749char *qemu_devtree_get_child_by_name(void *fdt, char *parent_path,
750 const char *cmpname)
751{
752 int offset = 0;
753 int parent_offset;
754 int namelen = strlen(cmpname);
755 char child_path[DT_PATH_LENGTH];
756
757 parent_offset = fdt_path_offset(fdt, parent_path);
758
759 if (parent_offset > 0) {
760 offset = fdt_subnode_offset_namelen(fdt, parent_offset,
761 cmpname, namelen);
762 if (fdt_get_path(fdt, offset, child_path, DT_PATH_LENGTH) == 0) {
763 return g_strdup(child_path);
764 }
765 }
766
767 return NULL;
768}
769
770int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle)
771{
772 return fdt_get_path(fdt, fdt_node_offset_by_phandle(fdt, phandle),
773 node_path, DT_PATH_LENGTH);
774}
775
776int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
777{
778 int offset = fdt_path_offset(fdt, current);
779 int parent_offset = fdt_supernode_atdepth_offset(fdt, offset,
780 fdt_node_depth(fdt, offset) - 1, NULL);
781
782 return parent_offset >= 0 ?
783 fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1;
784}
785
786int qemu_devtree_get_root_node(void *fdt, char *node_path)
787{
788 return fdt_get_path(fdt, 0, node_path, DT_PATH_LENGTH);
789}
790
791static void devtree_scan(void *fdt, int *num_nodes, int info_dump)
792{
793 int depth = 0, offset = 0;
794
795 if (num_nodes) {
796 *num_nodes = 0;
797 }
798 for (;;) {
799 offset = fdt_next_node(fdt, offset, &depth);
800 if (num_nodes) {
801 (*num_nodes)++;
802 }
803 if (offset <= 0 || depth <= 0) {
804 break;
805 }
806
807 if (info_dump) {
808 char node_path[DT_PATH_LENGTH];
809 char *all_compats = NULL;
810 int compat_len;
811 Error *errp = NULL;
812
813 if (fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH)) {
814 sprintf(node_path, "(none)");
815 } else {
816 all_compats = qemu_fdt_getprop(fdt, node_path, "compatible",
817 &compat_len, false, &errp);
818 }
819 if (!errp) {
820 char *i = all_compats;
821 for (;;) {
822 char *j = memchr(i, '\0', DT_PATH_LENGTH);
823 compat_len -= ((j+1)-i);
824 if (!compat_len) {
825 break;
826 }
827 *j = ' ';
828 i = j+1;
829 }
830 }
831 printf("OFFSET: %d, DEPTH: %d, PATH: %s, COMPATS: %s\n",
832 offset, depth, node_path,
833 all_compats ? all_compats : "(none)");
834 }
835 }
836}
837
838void devtree_info_dump(void *fdt)
839{
840 devtree_scan(fdt, NULL, 1);
841}
842
843int devtree_get_num_nodes(void *fdt)
844{
845 int ret;
846
847 devtree_scan(fdt, &ret, 0);
848 return ret;
849}
850