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

sysbus-fdt.c (18880B)


      1/*
      2 * ARM Platform Bus device tree generation helpers
      3 *
      4 * Copyright (c) 2014 Linaro Limited
      5 *
      6 * Authors:
      7 *  Alex Graf <agraf@suse.de>
      8 *  Eric Auger <eric.auger@linaro.org>
      9 *
     10 * This program is free software; you can redistribute it and/or modify it
     11 * under the terms and conditions of the GNU General Public License,
     12 * version 2 or later, as published by the Free Software Foundation.
     13 *
     14 * This program is distributed in the hope it will be useful, but WITHOUT
     15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     17 * more details.
     18 *
     19 * You should have received a copy of the GNU General Public License along with
     20 * this program.  If not, see <http://www.gnu.org/licenses/>.
     21 *
     22 */
     23
     24#include "qemu/osdep.h"
     25#include "qapi/error.h"
     26#include <libfdt.h>
     27#ifdef CONFIG_LINUX
     28#include <linux/vfio.h>
     29#endif
     30#include "hw/arm/sysbus-fdt.h"
     31#include "qemu/error-report.h"
     32#include "sysemu/device_tree.h"
     33#include "sysemu/tpm.h"
     34#include "hw/platform-bus.h"
     35#include "hw/vfio/vfio-platform.h"
     36#include "hw/vfio/vfio-calxeda-xgmac.h"
     37#include "hw/vfio/vfio-amd-xgbe.h"
     38#include "hw/display/ramfb.h"
     39#include "hw/arm/fdt.h"
     40
     41/*
     42 * internal struct that contains the information to create dynamic
     43 * sysbus device node
     44 */
     45typedef struct PlatformBusFDTData {
     46    void *fdt; /* device tree handle */
     47    int irq_start; /* index of the first IRQ usable by platform bus devices */
     48    const char *pbus_node_name; /* name of the platform bus node */
     49    PlatformBusDevice *pbus;
     50} PlatformBusFDTData;
     51
     52/* struct that allows to match a device and create its FDT node */
     53typedef struct BindingEntry {
     54    const char *typename;
     55    const char *compat;
     56    int  (*add_fn)(SysBusDevice *sbdev, void *opaque);
     57    bool (*match_fn)(SysBusDevice *sbdev, const struct BindingEntry *combo);
     58} BindingEntry;
     59
     60/* helpers */
     61
     62typedef struct HostProperty {
     63    const char *name;
     64    bool optional;
     65} HostProperty;
     66
     67#ifdef CONFIG_LINUX
     68
     69/**
     70 * copy_properties_from_host
     71 *
     72 * copies properties listed in an array from host device tree to
     73 * guest device tree. If a non optional property is not found, the
     74 * function asserts. An optional property is ignored if not found
     75 * in the host device tree.
     76 * @props: array of HostProperty to copy
     77 * @nb_props: number of properties in the array
     78 * @host_dt: host device tree blob
     79 * @guest_dt: guest device tree blob
     80 * @node_path: host dt node path where the property is supposed to be
     81              found
     82 * @nodename: guest node name the properties should be added to
     83 */
     84static void copy_properties_from_host(HostProperty *props, int nb_props,
     85                                      void *host_fdt, void *guest_fdt,
     86                                      char *node_path, char *nodename)
     87{
     88    int i, prop_len;
     89    const void *r;
     90    Error *err = NULL;
     91
     92    for (i = 0; i < nb_props; i++) {
     93        r = qemu_fdt_getprop(host_fdt, node_path,
     94                             props[i].name,
     95                             &prop_len,
     96                             &err);
     97        if (r) {
     98            qemu_fdt_setprop(guest_fdt, nodename,
     99                             props[i].name, r, prop_len);
    100        } else {
    101            if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) {
    102                /* optional property does not exist */
    103                error_free(err);
    104            } else {
    105                error_report_err(err);
    106            }
    107            if (!props[i].optional) {
    108                /* mandatory property not found: bail out */
    109                exit(1);
    110            }
    111            err = NULL;
    112        }
    113    }
    114}
    115
    116/* clock properties whose values are copied/pasted from host */
    117static HostProperty clock_copied_properties[] = {
    118    {"compatible", false},
    119    {"#clock-cells", false},
    120    {"clock-frequency", true},
    121    {"clock-output-names", true},
    122};
    123
    124/**
    125 * fdt_build_clock_node
    126 *
    127 * Build a guest clock node, used as a dependency from a passthrough'ed
    128 * device. Most information are retrieved from the host clock node.
    129 * Also check the host clock is a fixed one.
    130 *
    131 * @host_fdt: host device tree blob from which info are retrieved
    132 * @guest_fdt: guest device tree blob where the clock node is added
    133 * @host_phandle: phandle of the clock in host device tree
    134 * @guest_phandle: phandle to assign to the guest node
    135 */
    136static void fdt_build_clock_node(void *host_fdt, void *guest_fdt,
    137                                uint32_t host_phandle,
    138                                uint32_t guest_phandle)
    139{
    140    char *node_path = NULL;
    141    char *nodename;
    142    const void *r;
    143    int ret, node_offset, prop_len, path_len = 16;
    144
    145    node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle);
    146    if (node_offset <= 0) {
    147        error_report("not able to locate clock handle %d in host device tree",
    148                     host_phandle);
    149        exit(1);
    150    }
    151    node_path = g_malloc(path_len);
    152    while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len))
    153            == -FDT_ERR_NOSPACE) {
    154        path_len += 16;
    155        node_path = g_realloc(node_path, path_len);
    156    }
    157    if (ret < 0) {
    158        error_report("not able to retrieve node path for clock handle %d",
    159                     host_phandle);
    160        exit(1);
    161    }
    162
    163    r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len,
    164                         &error_fatal);
    165    if (strcmp(r, "fixed-clock")) {
    166        error_report("clock handle %d is not a fixed clock", host_phandle);
    167        exit(1);
    168    }
    169
    170    nodename = strrchr(node_path, '/');
    171    qemu_fdt_add_subnode(guest_fdt, nodename);
    172
    173    copy_properties_from_host(clock_copied_properties,
    174                              ARRAY_SIZE(clock_copied_properties),
    175                              host_fdt, guest_fdt,
    176                              node_path, nodename);
    177
    178    qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle);
    179
    180    g_free(node_path);
    181}
    182
    183/**
    184 * sysfs_to_dt_name: convert the name found in sysfs into the node name
    185 * for instance e0900000.xgmac is converted into xgmac@e0900000
    186 * @sysfs_name: directory name in sysfs
    187 *
    188 * returns the device tree name upon success or NULL in case the sysfs name
    189 * does not match the expected format
    190 */
    191static char *sysfs_to_dt_name(const char *sysfs_name)
    192{
    193    gchar **substrings =  g_strsplit(sysfs_name, ".", 2);
    194    char *dt_name = NULL;
    195
    196    if (!substrings || !substrings[0] || !substrings[1]) {
    197        goto out;
    198    }
    199    dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]);
    200out:
    201    g_strfreev(substrings);
    202    return dt_name;
    203}
    204
    205/* Device Specific Code */
    206
    207/**
    208 * add_calxeda_midway_xgmac_fdt_node
    209 *
    210 * Generates a simple node with following properties:
    211 * compatible string, regs, interrupts, dma-coherent
    212 */
    213static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
    214{
    215    PlatformBusFDTData *data = opaque;
    216    PlatformBusDevice *pbus = data->pbus;
    217    void *fdt = data->fdt;
    218    const char *parent_node = data->pbus_node_name;
    219    int compat_str_len, i;
    220    char *nodename;
    221    uint32_t *irq_attr, *reg_attr;
    222    uint64_t mmio_base, irq_number;
    223    VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
    224    VFIODevice *vbasedev = &vdev->vbasedev;
    225
    226    mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
    227    nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
    228                               vbasedev->name, mmio_base);
    229    qemu_fdt_add_subnode(fdt, nodename);
    230
    231    compat_str_len = strlen(vdev->compat) + 1;
    232    qemu_fdt_setprop(fdt, nodename, "compatible",
    233                          vdev->compat, compat_str_len);
    234
    235    qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0);
    236
    237    reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
    238    for (i = 0; i < vbasedev->num_regions; i++) {
    239        mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
    240        reg_attr[2 * i] = cpu_to_be32(mmio_base);
    241        reg_attr[2 * i + 1] = cpu_to_be32(
    242                                memory_region_size(vdev->regions[i]->mem));
    243    }
    244    qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
    245                     vbasedev->num_regions * 2 * sizeof(uint32_t));
    246
    247    irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
    248    for (i = 0; i < vbasedev->num_irqs; i++) {
    249        irq_number = platform_bus_get_irqn(pbus, sbdev , i)
    250                         + data->irq_start;
    251        irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
    252        irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
    253        irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
    254    }
    255    qemu_fdt_setprop(fdt, nodename, "interrupts",
    256                     irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
    257    g_free(irq_attr);
    258    g_free(reg_attr);
    259    g_free(nodename);
    260    return 0;
    261}
    262
    263/* AMD xgbe properties whose values are copied/pasted from host */
    264static HostProperty amd_xgbe_copied_properties[] = {
    265    {"compatible", false},
    266    {"dma-coherent", true},
    267    {"amd,per-channel-interrupt", true},
    268    {"phy-mode", false},
    269    {"mac-address", true},
    270    {"amd,speed-set", false},
    271    {"amd,serdes-blwc", true},
    272    {"amd,serdes-cdr-rate", true},
    273    {"amd,serdes-pq-skew", true},
    274    {"amd,serdes-tx-amp", true},
    275    {"amd,serdes-dfe-tap-config", true},
    276    {"amd,serdes-dfe-tap-enable", true},
    277    {"clock-names", false},
    278};
    279
    280/**
    281 * add_amd_xgbe_fdt_node
    282 *
    283 * Generates the combined xgbe/phy node following kernel >=4.2
    284 * binding documentation:
    285 * Documentation/devicetree/bindings/net/amd-xgbe.txt:
    286 * Also 2 clock nodes are created (dma and ptp)
    287 *
    288 * Asserts in case of error
    289 */
    290static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
    291{
    292    PlatformBusFDTData *data = opaque;
    293    PlatformBusDevice *pbus = data->pbus;
    294    VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
    295    VFIODevice *vbasedev = &vdev->vbasedev;
    296    VFIOINTp *intp;
    297    const char *parent_node = data->pbus_node_name;
    298    char **node_path, *nodename, *dt_name;
    299    void *guest_fdt = data->fdt, *host_fdt;
    300    const void *r;
    301    int i, prop_len;
    302    uint32_t *irq_attr, *reg_attr, *host_clock_phandles;
    303    uint64_t mmio_base, irq_number;
    304    uint32_t guest_clock_phandles[2];
    305
    306    host_fdt = load_device_tree_from_sysfs();
    307
    308    dt_name = sysfs_to_dt_name(vbasedev->name);
    309    if (!dt_name) {
    310        error_report("%s incorrect sysfs device name %s",
    311                     __func__, vbasedev->name);
    312        exit(1);
    313    }
    314    node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat,
    315                                   &error_fatal);
    316    if (!node_path || !node_path[0]) {
    317        error_report("%s unable to retrieve node path for %s/%s",
    318                     __func__, dt_name, vdev->compat);
    319        exit(1);
    320    }
    321
    322    if (node_path[1]) {
    323        error_report("%s more than one node matching %s/%s!",
    324                     __func__, dt_name, vdev->compat);
    325        exit(1);
    326    }
    327
    328    g_free(dt_name);
    329
    330    if (vbasedev->num_regions != 5) {
    331        error_report("%s Does the host dt node combine XGBE/PHY?", __func__);
    332        exit(1);
    333    }
    334
    335    /* generate nodes for DMA_CLK and PTP_CLK */
    336    r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks",
    337                         &prop_len, &error_fatal);
    338    if (prop_len != 8) {
    339        error_report("%s clocks property should contain 2 handles", __func__);
    340        exit(1);
    341    }
    342    host_clock_phandles = (uint32_t *)r;
    343    guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt);
    344    guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt);
    345
    346    /**
    347     * clock handles fetched from host dt are in be32 layout whereas
    348     * rest of the code uses cpu layout. Also guest clock handles are
    349     * in cpu layout.
    350     */
    351    fdt_build_clock_node(host_fdt, guest_fdt,
    352                         be32_to_cpu(host_clock_phandles[0]),
    353                         guest_clock_phandles[0]);
    354
    355    fdt_build_clock_node(host_fdt, guest_fdt,
    356                         be32_to_cpu(host_clock_phandles[1]),
    357                         guest_clock_phandles[1]);
    358
    359    /* combined XGBE/PHY node */
    360    mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
    361    nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
    362                               vbasedev->name, mmio_base);
    363    qemu_fdt_add_subnode(guest_fdt, nodename);
    364
    365    copy_properties_from_host(amd_xgbe_copied_properties,
    366                       ARRAY_SIZE(amd_xgbe_copied_properties),
    367                       host_fdt, guest_fdt,
    368                       node_path[0], nodename);
    369
    370    qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks",
    371                           guest_clock_phandles[0],
    372                           guest_clock_phandles[1]);
    373
    374    reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
    375    for (i = 0; i < vbasedev->num_regions; i++) {
    376        mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
    377        reg_attr[2 * i] = cpu_to_be32(mmio_base);
    378        reg_attr[2 * i + 1] = cpu_to_be32(
    379                                memory_region_size(vdev->regions[i]->mem));
    380    }
    381    qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
    382                     vbasedev->num_regions * 2 * sizeof(uint32_t));
    383
    384    irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
    385    for (i = 0; i < vbasedev->num_irqs; i++) {
    386        irq_number = platform_bus_get_irqn(pbus, sbdev , i)
    387                         + data->irq_start;
    388        irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
    389        irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
    390        /*
    391          * General device interrupt and PCS auto-negotiation interrupts are
    392          * level-sensitive while the 4 per-channel interrupts are edge
    393          * sensitive
    394          */
    395        QLIST_FOREACH(intp, &vdev->intp_list, next) {
    396            if (intp->pin == i) {
    397                break;
    398            }
    399        }
    400        if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
    401            irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
    402        } else {
    403            irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
    404        }
    405    }
    406    qemu_fdt_setprop(guest_fdt, nodename, "interrupts",
    407                     irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
    408
    409    g_free(host_fdt);
    410    g_strfreev(node_path);
    411    g_free(irq_attr);
    412    g_free(reg_attr);
    413    g_free(nodename);
    414    return 0;
    415}
    416
    417/* DT compatible matching */
    418static bool vfio_platform_match(SysBusDevice *sbdev,
    419                                const BindingEntry *entry)
    420{
    421    VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
    422    const char *compat;
    423    unsigned int n;
    424
    425    for (n = vdev->num_compat, compat = vdev->compat; n > 0;
    426         n--, compat += strlen(compat) + 1) {
    427        if (!strcmp(entry->compat, compat)) {
    428            return true;
    429        }
    430    }
    431
    432    return false;
    433}
    434
    435#define VFIO_PLATFORM_BINDING(compat, add_fn) \
    436    {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match}
    437
    438#endif /* CONFIG_LINUX */
    439
    440#ifdef CONFIG_TPM
    441/*
    442 * add_tpm_tis_fdt_node: Create a DT node for TPM TIS
    443 *
    444 * See kernel documentation:
    445 * Documentation/devicetree/bindings/security/tpm/tpm_tis_mmio.txt
    446 * Optional interrupt for command completion is not exposed
    447 */
    448static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque)
    449{
    450    PlatformBusFDTData *data = opaque;
    451    PlatformBusDevice *pbus = data->pbus;
    452    void *fdt = data->fdt;
    453    const char *parent_node = data->pbus_node_name;
    454    char *nodename;
    455    uint32_t reg_attr[2];
    456    uint64_t mmio_base;
    457
    458    mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
    459    nodename = g_strdup_printf("%s/tpm_tis@%" PRIx64, parent_node, mmio_base);
    460    qemu_fdt_add_subnode(fdt, nodename);
    461
    462    qemu_fdt_setprop_string(fdt, nodename, "compatible", "tcg,tpm-tis-mmio");
    463
    464    reg_attr[0] = cpu_to_be32(mmio_base);
    465    reg_attr[1] = cpu_to_be32(0x5000);
    466    qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, 2 * sizeof(uint32_t));
    467
    468    g_free(nodename);
    469    return 0;
    470}
    471#endif
    472
    473static int no_fdt_node(SysBusDevice *sbdev, void *opaque)
    474{
    475    return 0;
    476}
    477
    478/* Device type based matching */
    479static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry)
    480{
    481    return !strcmp(object_get_typename(OBJECT(sbdev)), entry->typename);
    482}
    483
    484#define TYPE_BINDING(type, add_fn) {(type), NULL, (add_fn), NULL}
    485
    486/* list of supported dynamic sysbus bindings */
    487static const BindingEntry bindings[] = {
    488#ifdef CONFIG_LINUX
    489    TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node),
    490    TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node),
    491    VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node),
    492#endif
    493#ifdef CONFIG_TPM
    494    TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node),
    495#endif
    496    TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node),
    497    TYPE_BINDING("", NULL), /* last element */
    498};
    499
    500/* Generic Code */
    501
    502/**
    503 * add_fdt_node - add the device tree node of a dynamic sysbus device
    504 *
    505 * @sbdev: handle to the sysbus device
    506 * @opaque: handle to the PlatformBusFDTData
    507 *
    508 * Checks the sysbus type belongs to the list of device types that
    509 * are dynamically instantiable and if so call the node creation
    510 * function.
    511 */
    512static void add_fdt_node(SysBusDevice *sbdev, void *opaque)
    513{
    514    int i, ret;
    515
    516    for (i = 0; i < ARRAY_SIZE(bindings); i++) {
    517        const BindingEntry *iter = &bindings[i];
    518
    519        if (type_match(sbdev, iter)) {
    520            if (!iter->match_fn || iter->match_fn(sbdev, iter)) {
    521                ret = iter->add_fn(sbdev, opaque);
    522                assert(!ret);
    523                return;
    524            }
    525        }
    526    }
    527    error_report("Device %s can not be dynamically instantiated",
    528                     qdev_fw_name(DEVICE(sbdev)));
    529    exit(1);
    530}
    531
    532void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr,
    533                                    hwaddr bus_size, int irq_start)
    534{
    535    const char platcomp[] = "qemu,platform\0simple-bus";
    536    PlatformBusDevice *pbus;
    537    DeviceState *dev;
    538    gchar *node;
    539
    540    assert(fdt);
    541
    542    node = g_strdup_printf("/platform@%"PRIx64, addr);
    543
    544    /* Create a /platform node that we can put all devices into */
    545    qemu_fdt_add_subnode(fdt, node);
    546    qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
    547
    548    /* Our platform bus region is less than 32bits, so 1 cell is enough for
    549     * address and size
    550     */
    551    qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
    552    qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
    553    qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size);
    554
    555    qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
    556
    557    dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
    558    pbus = PLATFORM_BUS_DEVICE(dev);
    559
    560    PlatformBusFDTData data = {
    561        .fdt = fdt,
    562        .irq_start = irq_start,
    563        .pbus_node_name = node,
    564        .pbus = pbus,
    565    };
    566
    567    /* Loop through all dynamic sysbus devices and create their node */
    568    foreach_dynamic_sysbus_device(add_fdt_node, &data);
    569
    570    g_free(node);
    571}