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 "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    /* Expand to 2x size to give enough room for manipulation.  */
  92    dt_size += 10000;
  93    dt_size *= 2;
  94    /* First allocate space in qemu for device tree */
  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    /* Check sanity of device tree */
 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 * read_fstree: this function is inspired from dtc read_fstree
 132 * @fdt: preallocated fdt blob buffer, to be populated
 133 * @dirname: directory to scan under SYSFS_DT_BASEDIR
 134 * the search is recursive and the tree is searched down to the
 135 * leaves (property files).
 136 *
 137 * the function asserts in case of error
 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/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
 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 /* CONFIG_LINUX */
 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     * We need to find out if the user gave us special instruction at
 481     * which phandle id to start allocating phandles.
 482     */
 483    if (!phandle) {
 484        phandle = machine_phandle_start(current_machine);
 485    }
 486
 487    if (!phandle) {
 488        /*
 489         * None or invalid phandle given on the command line, so fall back to
 490         * default starting point.
 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        /* Dump the dtb to a file and quit */
 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