qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

device_tree.c (18630B)


      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 
     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 
     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  */
    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 */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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 len = strlen(array[i]) + 1;
    422         pstrcpy(p, len, array[i]);
    423         p += len;
    424     }
    425 
    426     ret = qemu_fdt_setprop(fdt, node_path, prop, str, total_len);
    427     g_free(str);
    428     return ret;
    429 }
    430 
    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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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 
    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 
    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 
    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 
    671 void hmp_dumpdtb(Monitor *mon, const QDict *qdict)
    672 {
    673     const char *filename = qdict_get_str(qdict, "filename");
    674     Error *local_err = NULL;
    675 
    676     qmp_dumpdtb(filename, &local_err);
    677 
    678     if (hmp_handle_error(mon, local_err)) {
    679         return;
    680     }
    681 
    682     info_report("dtb dumped to %s", filename);
    683 }
    684 
    685 void qemu_fdt_randomize_seeds(void *fdt)
    686 {
    687     int noffset, poffset, len;
    688     const char *name;
    689     uint8_t *data;
    690 
    691     for (noffset = fdt_next_node(fdt, 0, NULL);
    692          noffset >= 0;
    693          noffset = fdt_next_node(fdt, noffset, NULL)) {
    694         for (poffset = fdt_first_property_offset(fdt, noffset);
    695              poffset >= 0;
    696              poffset = fdt_next_property_offset(fdt, poffset)) {
    697             data = (uint8_t *)fdt_getprop_by_offset(fdt, poffset, &name, &len);
    698             if (!data || strcmp(name, "rng-seed"))
    699                 continue;
    700             qemu_guest_getrandom_nofail(data, len);
    701         }
    702     }
    703 }