cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

device_tree.c (15821B)


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