device_tree.c 12.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * Functions to help device tree manipulation using libfdt.
 * It also provides functions to read entries from device tree proc
 * interface.
 *
 * Copyright 2008 IBM Corporation.
 * Authors: Jerone Young <jyoung5@us.ibm.com>
 *          Hollis Blanchard <hollisb@us.ibm.com>
 *
 * This work is licensed under the GNU GPL license version 2 or later.
 *
 */

Peter Maydell's avatar
Peter Maydell committed
14
#include "qemu/osdep.h"
15

16 17 18 19
#ifdef CONFIG_LINUX
#include <dirent.h>
#endif

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 30 31

#include <libfdt.h>

32 33 34 35 36 37 38 39 40 41 42 43 44
#define FDT_MAX_SIZE  0x10000

void *create_device_tree(int *sizep)
{
    void *fdt;
    int ret;

    *sizep = FDT_MAX_SIZE;
    fdt = g_malloc0(FDT_MAX_SIZE);
    ret = fdt_create(fdt, FDT_MAX_SIZE);
    if (ret < 0) {
        goto fail;
    }
45 46 47 48
    ret = fdt_finish_reservemap(fdt);
    if (ret < 0) {
        goto fail;
    }
49 50 51 52 53 54 55 56 57 58 59 60 61 62
    ret = fdt_begin_node(fdt, "");
    if (ret < 0) {
        goto fail;
    }
    ret = fdt_end_node(fdt);
    if (ret < 0) {
        goto fail;
    }
    ret = fdt_finish(fdt);
    if (ret < 0) {
        goto fail;
    }
    ret = fdt_open_into(fdt, fdt, *sizep);
    if (ret) {
63
        error_report("Unable to copy device tree in memory");
64 65 66 67 68
        exit(1);
    }

    return fdt;
fail:
69
    error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
70 71 72
    exit(1);
}

73
void *load_device_tree(const char *filename_path, int *sizep)
74
{
75
    int dt_size;
76 77
    int dt_file_load_size;
    int ret;
78
    void *fdt = NULL;
79

80 81 82
    *sizep = 0;
    dt_size = get_image_size(filename_path);
    if (dt_size < 0) {
83 84
        error_report("Unable to get size of device tree file '%s'",
                     filename_path);
85 86 87
        goto fail;
    }

88
    /* Expand to 2x size to give enough room for manipulation.  */
89
    dt_size += 10000;
90
    dt_size *= 2;
91
    /* First allocate space in qemu for device tree */
92
    fdt = g_malloc0(dt_size);
93

94 95
    dt_file_load_size = load_image(filename_path, fdt);
    if (dt_file_load_size < 0) {
96 97
        error_report("Unable to open device tree file '%s'",
                     filename_path);
98 99
        goto fail;
    }
100

101
    ret = fdt_open_into(fdt, fdt, dt_size);
102
    if (ret) {
103
        error_report("Unable to copy device tree in memory");
104 105 106 107 108
        goto fail;
    }

    /* Check sanity of device tree */
    if (fdt_check_header(fdt)) {
109 110
        error_report("Device tree file loaded into memory is invalid: %s",
                     filename_path);
111 112
        goto fail;
    }
113
    *sizep = dt_size;
114 115 116
    return fdt;

fail:
117
    g_free(fdt);
118 119 120
    return NULL;
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
#ifdef CONFIG_LINUX

#define SYSFS_DT_BASEDIR "/proc/device-tree"

/**
 * read_fstree: this function is inspired from dtc read_fstree
 * @fdt: preallocated fdt blob buffer, to be populated
 * @dirname: directory to scan under SYSFS_DT_BASEDIR
 * the search is recursive and the tree is searched down to the
 * leaves (property files).
 *
 * the function asserts in case of error
 */
static void read_fstree(void *fdt, const char *dirname)
{
    DIR *d;
    struct dirent *de;
    struct stat st;
    const char *root_dir = SYSFS_DT_BASEDIR;
    const char *parent_node;

    if (strstr(dirname, root_dir) != dirname) {
        error_setg(&error_fatal, "%s: %s must be searched within %s",
                   __func__, dirname, root_dir);
    }
    parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];

    d = opendir(dirname);
    if (!d) {
        error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
151
        return;
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
    }

    while ((de = readdir(d)) != NULL) {
        char *tmpnam;

        if (!g_strcmp0(de->d_name, ".")
            || !g_strcmp0(de->d_name, "..")) {
            continue;
        }

        tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);

        if (lstat(tmpnam, &st) < 0) {
            error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam);
        }

        if (S_ISREG(st.st_mode)) {
            gchar *val;
            gsize len;

            if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
                error_setg(&error_fatal, "%s not able to extract info from %s",
                           __func__, tmpnam);
            }

            if (strlen(parent_node) > 0) {
                qemu_fdt_setprop(fdt, parent_node,
                                 de->d_name, val, len);
            } else {
                qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
            }
            g_free(val);
        } else if (S_ISDIR(st.st_mode)) {
            char *node_name;

            node_name = g_strdup_printf("%s/%s",
                                        parent_node, de->d_name);
            qemu_fdt_add_subnode(fdt, node_name);
            g_free(node_name);
            read_fstree(fdt, tmpnam);
        }

        g_free(tmpnam);
    }

    closedir(d);
}

/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
void *load_device_tree_from_sysfs(void)
{
    void *host_fdt;
    int host_fdt_size;

    host_fdt = create_device_tree(&host_fdt_size);
    read_fstree(host_fdt, SYSFS_DT_BASEDIR);
    if (fdt_check_header(host_fdt)) {
        error_setg(&error_fatal,
                   "%s host device tree extracted into memory is invalid",
                   __func__);
    }
    return host_fdt;
}

#endif /* CONFIG_LINUX */

218
static int findnode_nofail(void *fdt, const char *node_path)
219 220 221 222
{
    int offset;

    offset = fdt_path_offset(fdt, node_path);
223
    if (offset < 0) {
224 225
        error_report("%s Couldn't find node %s: %s", __func__, node_path,
                     fdt_strerror(offset));
226 227 228 229 230 231
        exit(1);
    }

    return offset;
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
                          Error **errp)
{
    int offset, len, ret;
    const char *iter_name;
    unsigned int path_len = 16, n = 0;
    GSList *path_list = NULL, *iter;
    char **path_array;

    offset = fdt_node_offset_by_compatible(fdt, -1, compat);

    while (offset >= 0) {
        iter_name = fdt_get_name(fdt, offset, &len);
        if (!iter_name) {
            offset = len;
            break;
        }
        if (!strcmp(iter_name, name)) {
            char *path;

            path = g_malloc(path_len);
            while ((ret = fdt_get_path(fdt, offset, path, path_len))
                  == -FDT_ERR_NOSPACE) {
                path_len += 16;
                path = g_realloc(path, path_len);
            }
            path_list = g_slist_prepend(path_list, path);
            n++;
        }
        offset = fdt_node_offset_by_compatible(fdt, offset, compat);
    }

    if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
        error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
                   __func__, name, compat, fdt_strerror(offset));
        for (iter = path_list; iter; iter = iter->next) {
            g_free(iter->data);
        }
        g_slist_free(path_list);
        return NULL;
    }

    path_array = g_new(char *, n + 1);
    path_array[n--] = NULL;

    for (iter = path_list; iter; iter = iter->next) {
        path_array[n--] = iter->data;
    }

    g_slist_free(path_list);

    return path_array;
}

286
int qemu_fdt_setprop(void *fdt, const char *node_path,
287
                     const char *property, const void *val, int size)
288 289 290
{
    int r;

291
    r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
292
    if (r < 0) {
293 294
        error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
                     property, fdt_strerror(r));
295 296
        exit(1);
    }
297

298
    return r;
299 300
}

301 302
int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
                          const char *property, uint32_t val)
303
{
304
    int r;
305

306 307
    r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
    if (r < 0) {
308 309
        error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
                     node_path, property, val, fdt_strerror(r));
310 311
        exit(1);
    }
312

313
    return r;
314 315
}

316 317
int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
                         const char *property, uint64_t val)
318 319
{
    val = cpu_to_be64(val);
320
    return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
321 322
}

323 324
int qemu_fdt_setprop_string(void *fdt, const char *node_path,
                            const char *property, const char *string)
325
{
326
    int r;
327

328 329
    r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
    if (r < 0) {
330 331
        error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
                     node_path, property, string, fdt_strerror(r));
332 333
        exit(1);
    }
334

335
    return r;
336
}
337

338
const void *qemu_fdt_getprop(void *fdt, const char *node_path,
339
                             const char *property, int *lenp, Error **errp)
340 341 342
{
    int len;
    const void *r;
343

344 345 346 347 348
    if (!lenp) {
        lenp = &len;
    }
    r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
    if (!r) {
349 350
        error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
                  node_path, property, fdt_strerror(*lenp));
351 352 353 354
    }
    return r;
}

355
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
356
                               const char *property, int *lenp, Error **errp)
357 358
{
    int len;
359 360 361 362 363 364 365 366 367 368 369 370 371
    const uint32_t *p;

    if (!lenp) {
        lenp = &len;
    }
    p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
    if (!p) {
        return 0;
    } else if (*lenp != 4) {
        error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
                   __func__, node_path, property);
        *lenp = -EINVAL;
        return 0;
372 373 374 375
    }
    return be32_to_cpu(*p);
}

376
uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
377 378 379 380
{
    uint32_t r;

    r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
381
    if (r == 0) {
382 383
        error_report("%s: Couldn't get phandle for %s: %s", __func__,
                     path, fdt_strerror(r));
384 385 386 387 388 389
        exit(1);
    }

    return r;
}

390 391 392
int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
                             const char *property,
                             const char *target_node_path)
393
{
394 395
    uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
    return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
396 397
}

398
uint32_t qemu_fdt_alloc_phandle(void *fdt)
399
{
400 401 402 403
    static int phandle = 0x0;

    /*
     * We need to find out if the user gave us special instruction at
404
     * which phandle id to start allocating phandles.
405 406
     */
    if (!phandle) {
407
        phandle = machine_phandle_start(current_machine);
408 409 410 411 412 413 414 415 416
    }

    if (!phandle) {
        /*
         * None or invalid phandle given on the command line, so fall back to
         * default starting point.
         */
        phandle = 0x8000;
    }
417 418 419 420

    return phandle++;
}

421
int qemu_fdt_nop_node(void *fdt, const char *node_path)
422
{
423
    int r;
424

425 426
    r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
    if (r < 0) {
427 428
        error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
                     fdt_strerror(r));
429 430
        exit(1);
    }
431

432
    return r;
433
}
434

435
int qemu_fdt_add_subnode(void *fdt, const char *name)
436 437 438 439
{
    char *dupname = g_strdup(name);
    char *basename = strrchr(dupname, '/');
    int retval;
440
    int parent = 0;
441 442

    if (!basename) {
443
        g_free(dupname);
444 445 446 447 448 449
        return -1;
    }

    basename[0] = '\0';
    basename++;

450 451 452 453 454
    if (dupname[0]) {
        parent = findnode_nofail(fdt, dupname);
    }

    retval = fdt_add_subnode(fdt, parent, basename);
455
    if (retval < 0) {
456 457
        error_report("FDT: Failed to create subnode %s: %s", name,
                     fdt_strerror(retval));
458
        exit(1);
459 460 461 462 463
    }

    g_free(dupname);
    return retval;
}
464

465
void qemu_fdt_dumpdtb(void *fdt, int size)
466
{
467
    const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
468

469 470 471 472
    if (dumpdtb) {
        /* Dump the dtb to a file and quit */
        exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
    }
473
}
474

475 476 477 478 479
int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
                                            const char *node_path,
                                            const char *property,
                                            int numvalues,
                                            uint64_t *values)
480 481 482 483 484
{
    uint32_t *propcells;
    uint64_t value;
    int cellnum, vnum, ncells;
    uint32_t hival;
485
    int ret;
486 487 488 489 490 491 492

    propcells = g_new0(uint32_t, numvalues * 2);

    cellnum = 0;
    for (vnum = 0; vnum < numvalues; vnum++) {
        ncells = values[vnum * 2];
        if (ncells != 1 && ncells != 2) {
493 494
            ret = -1;
            goto out;
495 496 497 498 499 500
        }
        value = values[vnum * 2 + 1];
        hival = cpu_to_be32(value >> 32);
        if (ncells > 1) {
            propcells[cellnum++] = hival;
        } else if (hival != 0) {
501 502
            ret = -1;
            goto out;
503 504 505 506
        }
        propcells[cellnum++] = cpu_to_be32(value);
    }

507 508 509 510 511
    ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
                           cellnum * sizeof(uint32_t));
out:
    g_free(propcells);
    return ret;
512
}