xref: /qemu/system/device_tree.c (revision 1935b7ea)
1 /*
2  * Functions to help device tree manipulation using libfdt.
3  * It also provides functions to read entries from device tree proc
4  * interface.
5  *
6  * Copyright 2008 IBM Corporation.
7  * Authors: Jerone Young <jyoung5@us.ibm.com>
8  *          Hollis Blanchard <hollisb@us.ibm.com>
9  *
10  * This work is licensed under the GNU GPL license version 2 or later.
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/error-report.h"
22 #include "qemu/option.h"
23 #include "qemu/bswap.h"
24 #include "qemu/cutils.h"
25 #include "qemu/guest-random.h"
26 #include "sysemu/device_tree.h"
27 #include "hw/loader.h"
28 #include "hw/boards.h"
29 #include "qemu/config-file.h"
30 #include "qapi/qapi-commands-machine.h"
31 #include "qapi/qmp/qdict.h"
32 #include "monitor/hmp.h"
33 
34 #include <libfdt.h>
35 
36 #define FDT_MAX_SIZE  0x100000
37 
create_device_tree(int * sizep)38 void *create_device_tree(int *sizep)
39 {
40     void *fdt;
41     int ret;
42 
43     *sizep = FDT_MAX_SIZE;
44     fdt = g_malloc0(FDT_MAX_SIZE);
45     ret = fdt_create(fdt, FDT_MAX_SIZE);
46     if (ret < 0) {
47         goto fail;
48     }
49     ret = fdt_finish_reservemap(fdt);
50     if (ret < 0) {
51         goto fail;
52     }
53     ret = fdt_begin_node(fdt, "");
54     if (ret < 0) {
55         goto fail;
56     }
57     ret = fdt_end_node(fdt);
58     if (ret < 0) {
59         goto fail;
60     }
61     ret = fdt_finish(fdt);
62     if (ret < 0) {
63         goto fail;
64     }
65     ret = fdt_open_into(fdt, fdt, *sizep);
66     if (ret) {
67         error_report("%s: Unable to copy device tree into memory: %s",
68                      __func__, fdt_strerror(ret));
69         exit(1);
70     }
71 
72     return fdt;
73 fail:
74     error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
75     exit(1);
76 }
77 
load_device_tree(const char * filename_path,int * sizep)78 void *load_device_tree(const char *filename_path, int *sizep)
79 {
80     int dt_size;
81     int dt_file_load_size;
82     int ret;
83     void *fdt = NULL;
84 
85     *sizep = 0;
86     dt_size = get_image_size(filename_path);
87     if (dt_size < 0) {
88         error_report("Unable to get size of device tree file '%s'",
89                      filename_path);
90         goto fail;
91     }
92     if (dt_size > INT_MAX / 2 - 10000) {
93         error_report("Device tree file '%s' is too large", filename_path);
94         goto fail;
95     }
96 
97     /* Expand to 2x size to give enough room for manipulation.  */
98     dt_size += 10000;
99     dt_size *= 2;
100     /* First allocate space in qemu for device tree */
101     fdt = g_malloc0(dt_size);
102 
103     dt_file_load_size = load_image_size(filename_path, fdt, dt_size);
104     if (dt_file_load_size < 0) {
105         error_report("Unable to open device tree file '%s'",
106                      filename_path);
107         goto fail;
108     }
109 
110     ret = fdt_open_into(fdt, fdt, dt_size);
111     if (ret) {
112         error_report("%s: Unable to copy device tree into memory: %s",
113                      __func__, fdt_strerror(ret));
114         goto fail;
115     }
116 
117     /* Check sanity of device tree */
118     if (fdt_check_header(fdt)) {
119         error_report("Device tree file loaded into memory is invalid: %s",
120                      filename_path);
121         goto fail;
122     }
123     *sizep = dt_size;
124     return fdt;
125 
126 fail:
127     g_free(fdt);
128     return NULL;
129 }
130 
131 #ifdef CONFIG_LINUX
132 
133 #define SYSFS_DT_BASEDIR "/proc/device-tree"
134 
135 /**
136  * read_fstree: this function is inspired from dtc read_fstree
137  * @fdt: preallocated fdt blob buffer, to be populated
138  * @dirname: directory to scan under SYSFS_DT_BASEDIR
139  * the search is recursive and the tree is searched down to the
140  * leaves (property files).
141  *
142  * the function asserts in case of error
143  */
read_fstree(void * fdt,const char * dirname)144 static void read_fstree(void *fdt, const char *dirname)
145 {
146     DIR *d;
147     struct dirent *de;
148     struct stat st;
149     const char *root_dir = SYSFS_DT_BASEDIR;
150     const char *parent_node;
151 
152     if (strstr(dirname, root_dir) != dirname) {
153         error_report("%s: %s must be searched within %s",
154                      __func__, dirname, root_dir);
155         exit(1);
156     }
157     parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];
158 
159     d = opendir(dirname);
160     if (!d) {
161         error_report("%s cannot open %s", __func__, dirname);
162         exit(1);
163     }
164 
165     while ((de = readdir(d)) != NULL) {
166         char *tmpnam;
167 
168         if (!g_strcmp0(de->d_name, ".")
169             || !g_strcmp0(de->d_name, "..")) {
170             continue;
171         }
172 
173         tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
174 
175         if (lstat(tmpnam, &st) < 0) {
176             error_report("%s cannot lstat %s", __func__, tmpnam);
177             exit(1);
178         }
179 
180         if (S_ISREG(st.st_mode)) {
181             gchar *val;
182             gsize len;
183 
184             if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
185                 error_report("%s not able to extract info from %s",
186                              __func__, tmpnam);
187                 exit(1);
188             }
189 
190             if (strlen(parent_node) > 0) {
191                 qemu_fdt_setprop(fdt, parent_node,
192                                  de->d_name, val, len);
193             } else {
194                 qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
195             }
196             g_free(val);
197         } else if (S_ISDIR(st.st_mode)) {
198             char *node_name;
199 
200             node_name = g_strdup_printf("%s/%s",
201                                         parent_node, de->d_name);
202             qemu_fdt_add_subnode(fdt, node_name);
203             g_free(node_name);
204             read_fstree(fdt, tmpnam);
205         }
206 
207         g_free(tmpnam);
208     }
209 
210     closedir(d);
211 }
212 
213 /* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
load_device_tree_from_sysfs(void)214 void *load_device_tree_from_sysfs(void)
215 {
216     void *host_fdt;
217     int host_fdt_size;
218 
219     host_fdt = create_device_tree(&host_fdt_size);
220     read_fstree(host_fdt, SYSFS_DT_BASEDIR);
221     if (fdt_check_header(host_fdt)) {
222         error_report("%s host device tree extracted into memory is invalid",
223                      __func__);
224         exit(1);
225     }
226     return host_fdt;
227 }
228 
229 #endif /* CONFIG_LINUX */
230 
findnode_nofail(void * fdt,const char * node_path)231 static int findnode_nofail(void *fdt, const char *node_path)
232 {
233     int offset;
234 
235     offset = fdt_path_offset(fdt, node_path);
236     if (offset < 0) {
237         error_report("%s Couldn't find node %s: %s", __func__, node_path,
238                      fdt_strerror(offset));
239         exit(1);
240     }
241 
242     return offset;
243 }
244 
qemu_fdt_node_unit_path(void * fdt,const char * name,Error ** errp)245 char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp)
246 {
247     char *prefix =  g_strdup_printf("%s@", name);
248     unsigned int path_len = 16, n = 0;
249     GSList *path_list = NULL, *iter;
250     const char *iter_name;
251     int offset, len, ret;
252     char **path_array;
253 
254     offset = fdt_next_node(fdt, -1, NULL);
255 
256     while (offset >= 0) {
257         iter_name = fdt_get_name(fdt, offset, &len);
258         if (!iter_name) {
259             offset = len;
260             break;
261         }
262         if (!strcmp(iter_name, name) || g_str_has_prefix(iter_name, prefix)) {
263             char *path;
264 
265             path = g_malloc(path_len);
266             while ((ret = fdt_get_path(fdt, offset, path, path_len))
267                   == -FDT_ERR_NOSPACE) {
268                 path_len += 16;
269                 path = g_realloc(path, path_len);
270             }
271             path_list = g_slist_prepend(path_list, path);
272             n++;
273         }
274         offset = fdt_next_node(fdt, offset, NULL);
275     }
276     g_free(prefix);
277 
278     if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
279         error_setg(errp, "%s: abort parsing dt for %s node units: %s",
280                    __func__, name, fdt_strerror(offset));
281         for (iter = path_list; iter; iter = iter->next) {
282             g_free(iter->data);
283         }
284         g_slist_free(path_list);
285         return NULL;
286     }
287 
288     path_array = g_new(char *, n + 1);
289     path_array[n--] = NULL;
290 
291     for (iter = path_list; iter; iter = iter->next) {
292         path_array[n--] = iter->data;
293     }
294 
295     g_slist_free(path_list);
296 
297     return path_array;
298 }
299 
qemu_fdt_node_path(void * fdt,const char * name,const char * compat,Error ** errp)300 char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
301                           Error **errp)
302 {
303     int offset, len, ret;
304     const char *iter_name;
305     unsigned int path_len = 16, n = 0;
306     GSList *path_list = NULL, *iter;
307     char **path_array;
308 
309     offset = fdt_node_offset_by_compatible(fdt, -1, compat);
310 
311     while (offset >= 0) {
312         iter_name = fdt_get_name(fdt, offset, &len);
313         if (!iter_name) {
314             offset = len;
315             break;
316         }
317         if (!name || !strcmp(iter_name, name)) {
318             char *path;
319 
320             path = g_malloc(path_len);
321             while ((ret = fdt_get_path(fdt, offset, path, path_len))
322                   == -FDT_ERR_NOSPACE) {
323                 path_len += 16;
324                 path = g_realloc(path, path_len);
325             }
326             path_list = g_slist_prepend(path_list, path);
327             n++;
328         }
329         offset = fdt_node_offset_by_compatible(fdt, offset, compat);
330     }
331 
332     if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
333         error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
334                    __func__, name, compat, fdt_strerror(offset));
335         for (iter = path_list; iter; iter = iter->next) {
336             g_free(iter->data);
337         }
338         g_slist_free(path_list);
339         return NULL;
340     }
341 
342     path_array = g_new(char *, n + 1);
343     path_array[n--] = NULL;
344 
345     for (iter = path_list; iter; iter = iter->next) {
346         path_array[n--] = iter->data;
347     }
348 
349     g_slist_free(path_list);
350 
351     return path_array;
352 }
353 
qemu_fdt_setprop(void * fdt,const char * node_path,const char * property,const void * val,int size)354 int qemu_fdt_setprop(void *fdt, const char *node_path,
355                      const char *property, const void *val, int size)
356 {
357     int r;
358 
359     r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
360     if (r < 0) {
361         error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
362                      property, fdt_strerror(r));
363         exit(1);
364     }
365 
366     return r;
367 }
368 
qemu_fdt_setprop_cell(void * fdt,const char * node_path,const char * property,uint32_t val)369 int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
370                           const char *property, uint32_t val)
371 {
372     int r;
373 
374     r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
375     if (r < 0) {
376         error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
377                      node_path, property, val, fdt_strerror(r));
378         exit(1);
379     }
380 
381     return r;
382 }
383 
qemu_fdt_setprop_u64(void * fdt,const char * node_path,const char * property,uint64_t val)384 int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
385                          const char *property, uint64_t val)
386 {
387     val = cpu_to_be64(val);
388     return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
389 }
390 
qemu_fdt_setprop_string(void * fdt,const char * node_path,const char * property,const char * string)391 int qemu_fdt_setprop_string(void *fdt, const char *node_path,
392                             const char *property, const char *string)
393 {
394     int r;
395 
396     r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
397     if (r < 0) {
398         error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
399                      node_path, property, string, fdt_strerror(r));
400         exit(1);
401     }
402 
403     return r;
404 }
405 
406 /*
407  * libfdt doesn't allow us to add string arrays directly but they are
408  * test a series of null terminated strings with a length. We build
409  * the string up here so we can calculate the final length.
410  */
qemu_fdt_setprop_string_array(void * fdt,const char * node_path,const char * prop,char ** array,int len)411 int qemu_fdt_setprop_string_array(void *fdt, const char *node_path,
412                                   const char *prop, char **array, int len)
413 {
414     int ret, i, total_len = 0;
415     char *str, *p;
416     for (i = 0; i < len; i++) {
417         total_len += strlen(array[i]) + 1;
418     }
419     p = str = g_malloc0(total_len);
420     for (i = 0; i < len; i++) {
421         int offset = strlen(array[i]) + 1;
422         pstrcpy(p, offset, array[i]);
423         p += offset;
424     }
425 
426     ret = qemu_fdt_setprop(fdt, node_path, prop, str, total_len);
427     g_free(str);
428     return ret;
429 }
430 
qemu_fdt_getprop(void * fdt,const char * node_path,const char * property,int * lenp,Error ** errp)431 const void *qemu_fdt_getprop(void *fdt, const char *node_path,
432                              const char *property, int *lenp, Error **errp)
433 {
434     int len;
435     const void *r;
436 
437     if (!lenp) {
438         lenp = &len;
439     }
440     r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
441     if (!r) {
442         error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
443                   node_path, property, fdt_strerror(*lenp));
444     }
445     return r;
446 }
447 
qemu_fdt_getprop_cell(void * fdt,const char * node_path,const char * property,int * lenp,Error ** errp)448 uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
449                                const char *property, int *lenp, Error **errp)
450 {
451     int len;
452     const uint32_t *p;
453 
454     if (!lenp) {
455         lenp = &len;
456     }
457     p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
458     if (!p) {
459         return 0;
460     } else if (*lenp != 4) {
461         error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
462                    __func__, node_path, property);
463         *lenp = -EINVAL;
464         return 0;
465     }
466     return be32_to_cpu(*p);
467 }
468 
qemu_fdt_get_phandle(void * fdt,const char * path)469 uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
470 {
471     uint32_t r;
472 
473     r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
474     if (r == 0) {
475         error_report("%s: Couldn't get phandle for %s: %s", __func__,
476                      path, fdt_strerror(r));
477         exit(1);
478     }
479 
480     return r;
481 }
482 
qemu_fdt_setprop_phandle(void * fdt,const char * node_path,const char * property,const char * target_node_path)483 int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
484                              const char *property,
485                              const char *target_node_path)
486 {
487     uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
488     return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
489 }
490 
qemu_fdt_alloc_phandle(void * fdt)491 uint32_t qemu_fdt_alloc_phandle(void *fdt)
492 {
493     static int phandle = 0x0;
494 
495     /*
496      * We need to find out if the user gave us special instruction at
497      * which phandle id to start allocating phandles.
498      */
499     if (!phandle) {
500         phandle = machine_phandle_start(current_machine);
501     }
502 
503     if (!phandle) {
504         /*
505          * None or invalid phandle given on the command line, so fall back to
506          * default starting point.
507          */
508         phandle = 0x8000;
509     }
510 
511     return phandle++;
512 }
513 
qemu_fdt_nop_node(void * fdt,const char * node_path)514 int qemu_fdt_nop_node(void *fdt, const char *node_path)
515 {
516     int r;
517 
518     r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
519     if (r < 0) {
520         error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
521                      fdt_strerror(r));
522         exit(1);
523     }
524 
525     return r;
526 }
527 
qemu_fdt_add_subnode(void * fdt,const char * name)528 int qemu_fdt_add_subnode(void *fdt, const char *name)
529 {
530     char *dupname = g_strdup(name);
531     char *basename = strrchr(dupname, '/');
532     int retval;
533     int parent = 0;
534 
535     if (!basename) {
536         g_free(dupname);
537         return -1;
538     }
539 
540     basename[0] = '\0';
541     basename++;
542 
543     if (dupname[0]) {
544         parent = findnode_nofail(fdt, dupname);
545     }
546 
547     retval = fdt_add_subnode(fdt, parent, basename);
548     if (retval < 0) {
549         error_report("%s: Failed to create subnode %s: %s",
550                      __func__, name, fdt_strerror(retval));
551         exit(1);
552     }
553 
554     g_free(dupname);
555     return retval;
556 }
557 
558 /*
559  * qemu_fdt_add_path: Like qemu_fdt_add_subnode(), but will add
560  * all missing subnodes from the given path.
561  */
qemu_fdt_add_path(void * fdt,const char * path)562 int qemu_fdt_add_path(void *fdt, const char *path)
563 {
564     const char *name;
565     int namelen, retval;
566     int parent = 0;
567 
568     if (path[0] != '/') {
569         return -1;
570     }
571 
572     do {
573         name = path + 1;
574         path = strchr(name, '/');
575         namelen = path != NULL ? path - name : strlen(name);
576 
577         retval = fdt_subnode_offset_namelen(fdt, parent, name, namelen);
578         if (retval < 0 && retval != -FDT_ERR_NOTFOUND) {
579             error_report("%s: Unexpected error in finding subnode %.*s: %s",
580                          __func__, namelen, name, fdt_strerror(retval));
581             exit(1);
582         } else if (retval == -FDT_ERR_NOTFOUND) {
583             retval = fdt_add_subnode_namelen(fdt, parent, name, namelen);
584             if (retval < 0) {
585                 error_report("%s: Failed to create subnode %.*s: %s",
586                              __func__, namelen, name, fdt_strerror(retval));
587                 exit(1);
588             }
589         }
590 
591         parent = retval;
592     } while (path);
593 
594     return retval;
595 }
596 
qemu_fdt_dumpdtb(void * fdt,int size)597 void qemu_fdt_dumpdtb(void *fdt, int size)
598 {
599     const char *dumpdtb = current_machine->dumpdtb;
600 
601     if (dumpdtb) {
602         /* Dump the dtb to a file and quit */
603         if (g_file_set_contents(dumpdtb, fdt, size, NULL)) {
604             info_report("dtb dumped to %s. Exiting.", dumpdtb);
605             exit(0);
606         }
607         error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb);
608         exit(1);
609     }
610 }
611 
qemu_fdt_setprop_sized_cells_from_array(void * fdt,const char * node_path,const char * property,int numvalues,uint64_t * values)612 int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
613                                             const char *node_path,
614                                             const char *property,
615                                             int numvalues,
616                                             uint64_t *values)
617 {
618     uint32_t *propcells;
619     uint64_t value;
620     int cellnum, vnum, ncells;
621     uint32_t hival;
622     int ret;
623 
624     propcells = g_new0(uint32_t, numvalues * 2);
625 
626     cellnum = 0;
627     for (vnum = 0; vnum < numvalues; vnum++) {
628         ncells = values[vnum * 2];
629         if (ncells != 1 && ncells != 2) {
630             ret = -1;
631             goto out;
632         }
633         value = values[vnum * 2 + 1];
634         hival = cpu_to_be32(value >> 32);
635         if (ncells > 1) {
636             propcells[cellnum++] = hival;
637         } else if (hival != 0) {
638             ret = -1;
639             goto out;
640         }
641         propcells[cellnum++] = cpu_to_be32(value);
642     }
643 
644     ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
645                            cellnum * sizeof(uint32_t));
646 out:
647     g_free(propcells);
648     return ret;
649 }
650 
qmp_dumpdtb(const char * filename,Error ** errp)651 void qmp_dumpdtb(const char *filename, Error **errp)
652 {
653     g_autoptr(GError) err = NULL;
654     uint32_t size;
655 
656     if (!current_machine->fdt) {
657         error_setg(errp, "This machine doesn't have a FDT");
658         return;
659     }
660 
661     size = fdt_totalsize(current_machine->fdt);
662 
663     g_assert(size > 0);
664 
665     if (!g_file_set_contents(filename, current_machine->fdt, size, &err)) {
666         error_setg(errp, "Error saving FDT to file %s: %s",
667                    filename, err->message);
668     }
669 }
670 
qemu_fdt_randomize_seeds(void * fdt)671 void qemu_fdt_randomize_seeds(void *fdt)
672 {
673     int noffset, poffset, len;
674     const char *name;
675     uint8_t *data;
676 
677     for (noffset = fdt_next_node(fdt, 0, NULL);
678          noffset >= 0;
679          noffset = fdt_next_node(fdt, noffset, NULL)) {
680         for (poffset = fdt_first_property_offset(fdt, noffset);
681              poffset >= 0;
682              poffset = fdt_next_property_offset(fdt, poffset)) {
683             data = (uint8_t *)fdt_getprop_by_offset(fdt, poffset, &name, &len);
684             if (!data || strcmp(name, "rng-seed"))
685                 continue;
686             qemu_guest_getrandom_nofail(data, len);
687         }
688     }
689 }
690