qemu/device_tree.c
<<
>>
Prefs
   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-common.h"
  22#include "qemu/error-report.h"
  23#include "sysemu/device_tree.h"
  24#include "sysemu/sysemu.h"
  25#include "hw/loader.h"
  26#include "hw/boards.h"
  27#include "qemu/config-file.h"
  28
  29#include <libfdt.h>
  30
  31#define FDT_MAX_SIZE  0x10000
  32
  33void *create_device_tree(int *sizep)
  34{
  35    void *fdt;
  36    int ret;
  37
  38    *sizep = FDT_MAX_SIZE;
  39    fdt = g_malloc0(FDT_MAX_SIZE);
  40    ret = fdt_create(fdt, FDT_MAX_SIZE);
  41    if (ret < 0) {
  42        goto fail;
  43    }
  44    ret = fdt_finish_reservemap(fdt);
  45    if (ret < 0) {
  46        goto fail;
  47    }
  48    ret = fdt_begin_node(fdt, "");
  49    if (ret < 0) {
  50        goto fail;
  51    }
  52    ret = fdt_end_node(fdt);
  53    if (ret < 0) {
  54        goto fail;
  55    }
  56    ret = fdt_finish(fdt);
  57    if (ret < 0) {
  58        goto fail;
  59    }
  60    ret = fdt_open_into(fdt, fdt, *sizep);
  61    if (ret) {
  62        error_report("Unable to copy device tree in memory");
  63        exit(1);
  64    }
  65
  66    return fdt;
  67fail:
  68    error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
  69    exit(1);
  70}
  71
  72void *load_device_tree(const char *filename_path, int *sizep)
  73{
  74    int dt_size;
  75    int dt_file_load_size;
  76    int ret;
  77    void *fdt = NULL;
  78
  79    *sizep = 0;
  80    dt_size = get_image_size(filename_path);
  81    if (dt_size < 0) {
  82        error_report("Unable to get size of device tree file '%s'",
  83                     filename_path);
  84        goto fail;
  85    }
  86
  87    /* Expand to 2x size to give enough room for manipulation.  */
  88    dt_size += 10000;
  89    dt_size *= 2;
  90    /* First allocate space in qemu for device tree */
  91    fdt = g_malloc0(dt_size);
  92
  93    dt_file_load_size = load_image(filename_path, fdt);
  94    if (dt_file_load_size < 0) {
  95        error_report("Unable to open device tree file '%s'",
  96                     filename_path);
  97        goto fail;
  98    }
  99
 100    ret = fdt_open_into(fdt, fdt, dt_size);
 101    if (ret) {
 102        error_report("Unable to copy device tree in memory");
 103        goto fail;
 104    }
 105
 106    /* Check sanity of device tree */
 107    if (fdt_check_header(fdt)) {
 108        error_report("Device tree file loaded into memory is invalid: %s",
 109                     filename_path);
 110        goto fail;
 111    }
 112    *sizep = dt_size;
 113    return fdt;
 114
 115fail:
 116    g_free(fdt);
 117    return NULL;
 118}
 119
 120#ifdef CONFIG_LINUX
 121
 122#define SYSFS_DT_BASEDIR "/proc/device-tree"
 123
 124/**
 125 * read_fstree: this function is inspired from dtc read_fstree
 126 * @fdt: preallocated fdt blob buffer, to be populated
 127 * @dirname: directory to scan under SYSFS_DT_BASEDIR
 128 * the search is recursive and the tree is searched down to the
 129 * leaves (property files).
 130 *
 131 * the function asserts in case of error
 132 */
 133static void read_fstree(void *fdt, const char *dirname)
 134{
 135    DIR *d;
 136    struct dirent *de;
 137    struct stat st;
 138    const char *root_dir = SYSFS_DT_BASEDIR;
 139    const char *parent_node;
 140
 141    if (strstr(dirname, root_dir) != dirname) {
 142        error_setg(&error_fatal, "%s: %s must be searched within %s",
 143                   __func__, dirname, root_dir);
 144    }
 145    parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];
 146
 147    d = opendir(dirname);
 148    if (!d) {
 149        error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
 150    }
 151
 152    while ((de = readdir(d)) != NULL) {
 153        char *tmpnam;
 154
 155        if (!g_strcmp0(de->d_name, ".")
 156            || !g_strcmp0(de->d_name, "..")) {
 157            continue;
 158        }
 159
 160        tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
 161
 162        if (lstat(tmpnam, &st) < 0) {
 163            error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam);
 164        }
 165
 166        if (S_ISREG(st.st_mode)) {
 167            gchar *val;
 168            gsize len;
 169
 170            if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
 171                error_setg(&error_fatal, "%s not able to extract info from %s",
 172                           __func__, tmpnam);
 173            }
 174
 175            if (strlen(parent_node) > 0) {
 176                qemu_fdt_setprop(fdt, parent_node,
 177                                 de->d_name, val, len);
 178            } else {
 179                qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
 180            }
 181            g_free(val);
 182        } else if (S_ISDIR(st.st_mode)) {
 183            char *node_name;
 184
 185            node_name = g_strdup_printf("%s/%s",
 186                                        parent_node, de->d_name);
 187            qemu_fdt_add_subnode(fdt, node_name);
 188            g_free(node_name);
 189            read_fstree(fdt, tmpnam);
 190        }
 191
 192        g_free(tmpnam);
 193    }
 194
 195    closedir(d);
 196}
 197
 198/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
 199void *load_device_tree_from_sysfs(void)
 200{
 201    void *host_fdt;
 202    int host_fdt_size;
 203
 204    host_fdt = create_device_tree(&host_fdt_size);
 205    read_fstree(host_fdt, SYSFS_DT_BASEDIR);
 206    if (fdt_check_header(host_fdt)) {
 207        error_setg(&error_fatal,
 208                   "%s host device tree extracted into memory is invalid",
 209                   __func__);
 210    }
 211    return host_fdt;
 212}
 213
 214#endif /* CONFIG_LINUX */
 215
 216static int findnode_nofail(void *fdt, const char *node_path)
 217{
 218    int offset;
 219
 220    offset = fdt_path_offset(fdt, node_path);
 221    if (offset < 0) {
 222        error_report("%s Couldn't find node %s: %s", __func__, node_path,
 223                     fdt_strerror(offset));
 224        exit(1);
 225    }
 226
 227    return offset;
 228}
 229
 230char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
 231                          Error **errp)
 232{
 233    int offset, len, ret;
 234    const char *iter_name;
 235    unsigned int path_len = 16, n = 0;
 236    GSList *path_list = NULL, *iter;
 237    char **path_array;
 238
 239    offset = fdt_node_offset_by_compatible(fdt, -1, compat);
 240
 241    while (offset >= 0) {
 242        iter_name = fdt_get_name(fdt, offset, &len);
 243        if (!iter_name) {
 244            offset = len;
 245            break;
 246        }
 247        if (!strcmp(iter_name, name)) {
 248            char *path;
 249
 250            path = g_malloc(path_len);
 251            while ((ret = fdt_get_path(fdt, offset, path, path_len))
 252                  == -FDT_ERR_NOSPACE) {
 253                path_len += 16;
 254                path = g_realloc(path, path_len);
 255            }
 256            path_list = g_slist_prepend(path_list, path);
 257            n++;
 258        }
 259        offset = fdt_node_offset_by_compatible(fdt, offset, compat);
 260    }
 261
 262    if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
 263        error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
 264                   __func__, name, compat, fdt_strerror(offset));
 265        for (iter = path_list; iter; iter = iter->next) {
 266            g_free(iter->data);
 267        }
 268        g_slist_free(path_list);
 269        return NULL;
 270    }
 271
 272    path_array = g_new(char *, n + 1);
 273    path_array[n--] = NULL;
 274
 275    for (iter = path_list; iter; iter = iter->next) {
 276        path_array[n--] = iter->data;
 277    }
 278
 279    g_slist_free(path_list);
 280
 281    return path_array;
 282}
 283
 284int qemu_fdt_setprop(void *fdt, const char *node_path,
 285                     const char *property, const void *val, int size)
 286{
 287    int r;
 288
 289    r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
 290    if (r < 0) {
 291        error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
 292                     property, fdt_strerror(r));
 293        exit(1);
 294    }
 295
 296    return r;
 297}
 298
 299int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
 300                          const char *property, uint32_t val)
 301{
 302    int r;
 303
 304    r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
 305    if (r < 0) {
 306        error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
 307                     node_path, property, val, fdt_strerror(r));
 308        exit(1);
 309    }
 310
 311    return r;
 312}
 313
 314int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
 315                         const char *property, uint64_t val)
 316{
 317    val = cpu_to_be64(val);
 318    return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
 319}
 320
 321int qemu_fdt_setprop_string(void *fdt, const char *node_path,
 322                            const char *property, const char *string)
 323{
 324    int r;
 325
 326    r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
 327    if (r < 0) {
 328        error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
 329                     node_path, property, string, fdt_strerror(r));
 330        exit(1);
 331    }
 332
 333    return r;
 334}
 335
 336const void *qemu_fdt_getprop(void *fdt, const char *node_path,
 337                             const char *property, int *lenp, Error **errp)
 338{
 339    int len;
 340    const void *r;
 341
 342    if (!lenp) {
 343        lenp = &len;
 344    }
 345    r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
 346    if (!r) {
 347        error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
 348                  node_path, property, fdt_strerror(*lenp));
 349    }
 350    return r;
 351}
 352
 353uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
 354                               const char *property, int *lenp, Error **errp)
 355{
 356    int len;
 357    const uint32_t *p;
 358
 359    if (!lenp) {
 360        lenp = &len;
 361    }
 362    p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
 363    if (!p) {
 364        return 0;
 365    } else if (*lenp != 4) {
 366        error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
 367                   __func__, node_path, property);
 368        *lenp = -EINVAL;
 369        return 0;
 370    }
 371    return be32_to_cpu(*p);
 372}
 373
 374uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
 375{
 376    uint32_t r;
 377
 378    r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
 379    if (r == 0) {
 380        error_report("%s: Couldn't get phandle for %s: %s", __func__,
 381                     path, fdt_strerror(r));
 382        exit(1);
 383    }
 384
 385    return r;
 386}
 387
 388int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
 389                             const char *property,
 390                             const char *target_node_path)
 391{
 392    uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
 393    return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
 394}
 395
 396uint32_t qemu_fdt_alloc_phandle(void *fdt)
 397{
 398    static int phandle = 0x0;
 399
 400    /*
 401     * We need to find out if the user gave us special instruction at
 402     * which phandle id to start allocating phandles.
 403     */
 404    if (!phandle) {
 405        phandle = machine_phandle_start(current_machine);
 406    }
 407
 408    if (!phandle) {
 409        /*
 410         * None or invalid phandle given on the command line, so fall back to
 411         * default starting point.
 412         */
 413        phandle = 0x8000;
 414    }
 415
 416    return phandle++;
 417}
 418
 419int qemu_fdt_nop_node(void *fdt, const char *node_path)
 420{
 421    int r;
 422
 423    r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
 424    if (r < 0) {
 425        error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
 426                     fdt_strerror(r));
 427        exit(1);
 428    }
 429
 430    return r;
 431}
 432
 433int qemu_fdt_add_subnode(void *fdt, const char *name)
 434{
 435    char *dupname = g_strdup(name);
 436    char *basename = strrchr(dupname, '/');
 437    int retval;
 438    int parent = 0;
 439
 440    if (!basename) {
 441        g_free(dupname);
 442        return -1;
 443    }
 444
 445    basename[0] = '\0';
 446    basename++;
 447
 448    if (dupname[0]) {
 449        parent = findnode_nofail(fdt, dupname);
 450    }
 451
 452    retval = fdt_add_subnode(fdt, parent, basename);
 453    if (retval < 0) {
 454        error_report("FDT: Failed to create subnode %s: %s", name,
 455                     fdt_strerror(retval));
 456        exit(1);
 457    }
 458
 459    g_free(dupname);
 460    return retval;
 461}
 462
 463void qemu_fdt_dumpdtb(void *fdt, int size)
 464{
 465    const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
 466
 467    if (dumpdtb) {
 468        /* Dump the dtb to a file and quit */
 469        exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
 470    }
 471}
 472
 473int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
 474                                            const char *node_path,
 475                                            const char *property,
 476                                            int numvalues,
 477                                            uint64_t *values)
 478{
 479    uint32_t *propcells;
 480    uint64_t value;
 481    int cellnum, vnum, ncells;
 482    uint32_t hival;
 483    int ret;
 484
 485    propcells = g_new0(uint32_t, numvalues * 2);
 486
 487    cellnum = 0;
 488    for (vnum = 0; vnum < numvalues; vnum++) {
 489        ncells = values[vnum * 2];
 490        if (ncells != 1 && ncells != 2) {
 491            ret = -1;
 492            goto out;
 493        }
 494        value = values[vnum * 2 + 1];
 495        hival = cpu_to_be32(value >> 32);
 496        if (ncells > 1) {
 497            propcells[cellnum++] = hival;
 498        } else if (hival != 0) {
 499            ret = -1;
 500            goto out;
 501        }
 502        propcells[cellnum++] = cpu_to_be32(value);
 503    }
 504
 505    ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
 506                           cellnum * sizeof(uint32_t));
 507out:
 508    g_free(propcells);
 509    return ret;
 510}
 511