xref: /openbmc/qemu/system/device_tree.c (revision 9360070196789cc8b9404b2efaf319384e64b107)
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