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