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

spapr_rtas_ddw.c (8849B)


      1/*
      2 * QEMU sPAPR Dynamic DMA windows support
      3 *
      4 * Copyright (c) 2015 Alexey Kardashevskiy, IBM Corporation.
      5 *
      6 *  This program is free software; you can redistribute it and/or modify
      7 *  it under the terms of the GNU General Public License as published by
      8 *  the Free Software Foundation; either version 2 of the License,
      9 *  or (at your option) any later version.
     10 *
     11 *  This program is distributed in the hope that it will be useful,
     12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 *  GNU General Public License for more details.
     15 *
     16 *  You should have received a copy of the GNU General Public License
     17 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
     18 */
     19
     20#include "qemu/osdep.h"
     21#include "qemu/error-report.h"
     22#include "qemu/module.h"
     23#include "hw/ppc/spapr.h"
     24#include "hw/pci-host/spapr.h"
     25#include "trace.h"
     26
     27static int spapr_phb_get_active_win_num_cb(Object *child, void *opaque)
     28{
     29    SpaprTceTable *tcet;
     30
     31    tcet = (SpaprTceTable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
     32    if (tcet && tcet->nb_table) {
     33        ++*(unsigned *)opaque;
     34    }
     35    return 0;
     36}
     37
     38static unsigned spapr_phb_get_active_win_num(SpaprPhbState *sphb)
     39{
     40    unsigned ret = 0;
     41
     42    object_child_foreach(OBJECT(sphb), spapr_phb_get_active_win_num_cb, &ret);
     43
     44    return ret;
     45}
     46
     47static int spapr_phb_get_free_liobn_cb(Object *child, void *opaque)
     48{
     49    SpaprTceTable *tcet;
     50
     51    tcet = (SpaprTceTable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
     52    if (tcet && !tcet->nb_table) {
     53        *(uint32_t *)opaque = tcet->liobn;
     54        return 1;
     55    }
     56    return 0;
     57}
     58
     59static unsigned spapr_phb_get_free_liobn(SpaprPhbState *sphb)
     60{
     61    uint32_t liobn = 0;
     62
     63    object_child_foreach(OBJECT(sphb), spapr_phb_get_free_liobn_cb, &liobn);
     64
     65    return liobn;
     66}
     67
     68static uint32_t spapr_page_mask_to_query_mask(uint64_t page_mask)
     69{
     70    int i;
     71    uint32_t mask = 0;
     72    const struct { int shift; uint32_t mask; } masks[] = {
     73        { 12, RTAS_DDW_PGSIZE_4K },
     74        { 16, RTAS_DDW_PGSIZE_64K },
     75        { 24, RTAS_DDW_PGSIZE_16M },
     76        { 25, RTAS_DDW_PGSIZE_32M },
     77        { 26, RTAS_DDW_PGSIZE_64M },
     78        { 27, RTAS_DDW_PGSIZE_128M },
     79        { 28, RTAS_DDW_PGSIZE_256M },
     80        { 34, RTAS_DDW_PGSIZE_16G },
     81    };
     82
     83    for (i = 0; i < ARRAY_SIZE(masks); ++i) {
     84        if (page_mask & (1ULL << masks[i].shift)) {
     85            mask |= masks[i].mask;
     86        }
     87    }
     88
     89    return mask;
     90}
     91
     92static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu,
     93                                         SpaprMachineState *spapr,
     94                                         uint32_t token, uint32_t nargs,
     95                                         target_ulong args,
     96                                         uint32_t nret, target_ulong rets)
     97{
     98    SpaprPhbState *sphb;
     99    uint64_t buid;
    100    uint32_t avail, addr, pgmask = 0;
    101
    102    if ((nargs != 3) || (nret != 5)) {
    103        goto param_error_exit;
    104    }
    105
    106    buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
    107    addr = rtas_ld(args, 0);
    108    sphb = spapr_pci_find_phb(spapr, buid);
    109    if (!sphb || !sphb->ddw_enabled) {
    110        goto param_error_exit;
    111    }
    112
    113    /* Translate page mask to LoPAPR format */
    114    pgmask = spapr_page_mask_to_query_mask(sphb->page_size_mask);
    115
    116    avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb);
    117
    118    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
    119    rtas_st(rets, 1, avail);
    120    rtas_st(rets, 2, 0x80000000); /* The largest window we can possibly have */
    121    rtas_st(rets, 3, pgmask);
    122    rtas_st(rets, 4, 0); /* DMA migration mask, not supported */
    123
    124    trace_spapr_iommu_ddw_query(buid, addr, avail, 0x80000000, pgmask);
    125    return;
    126
    127param_error_exit:
    128    rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
    129}
    130
    131static void rtas_ibm_create_pe_dma_window(PowerPCCPU *cpu,
    132                                          SpaprMachineState *spapr,
    133                                          uint32_t token, uint32_t nargs,
    134                                          target_ulong args,
    135                                          uint32_t nret, target_ulong rets)
    136{
    137    SpaprPhbState *sphb;
    138    SpaprTceTable *tcet = NULL;
    139    uint32_t addr, page_shift, window_shift, liobn;
    140    uint64_t buid, win_addr;
    141    int windows;
    142
    143    if ((nargs != 5) || (nret != 4)) {
    144        goto param_error_exit;
    145    }
    146
    147    buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
    148    addr = rtas_ld(args, 0);
    149    sphb = spapr_pci_find_phb(spapr, buid);
    150    if (!sphb || !sphb->ddw_enabled) {
    151        goto param_error_exit;
    152    }
    153
    154    page_shift = rtas_ld(args, 3);
    155    window_shift = rtas_ld(args, 4);
    156    liobn = spapr_phb_get_free_liobn(sphb);
    157    windows = spapr_phb_get_active_win_num(sphb);
    158
    159    if (!(sphb->page_size_mask & (1ULL << page_shift)) ||
    160        (window_shift < page_shift)) {
    161        goto param_error_exit;
    162    }
    163
    164    if (!liobn || !sphb->ddw_enabled || windows == SPAPR_PCI_DMA_MAX_WINDOWS) {
    165        goto hw_error_exit;
    166    }
    167
    168    tcet = spapr_tce_find_by_liobn(liobn);
    169    if (!tcet) {
    170        goto hw_error_exit;
    171    }
    172
    173    win_addr = (windows == 0) ? sphb->dma_win_addr : sphb->dma64_win_addr;
    174    /*
    175     * We have just created a window, we know for the fact that it is empty,
    176     * use a hack to avoid iterating over the table as it is quite possible
    177     * to have billions of TCEs, all empty.
    178     * Note that we cannot delay this to the first H_PUT_TCE as this hcall is
    179     * mostly likely to be handled in KVM so QEMU just does not know if it
    180     * happened.
    181     */
    182    tcet->skipping_replay = true;
    183    spapr_tce_table_enable(tcet, page_shift, win_addr,
    184                           1ULL << (window_shift - page_shift));
    185    tcet->skipping_replay = false;
    186    if (!tcet->nb_table) {
    187        goto hw_error_exit;
    188    }
    189
    190    trace_spapr_iommu_ddw_create(buid, addr, 1ULL << page_shift,
    191                                 1ULL << window_shift, tcet->bus_offset, liobn);
    192
    193    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
    194    rtas_st(rets, 1, liobn);
    195    rtas_st(rets, 2, tcet->bus_offset >> 32);
    196    rtas_st(rets, 3, tcet->bus_offset & ((uint32_t) -1));
    197
    198    return;
    199
    200hw_error_exit:
    201    rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
    202    return;
    203
    204param_error_exit:
    205    rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
    206}
    207
    208static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu,
    209                                          SpaprMachineState *spapr,
    210                                          uint32_t token, uint32_t nargs,
    211                                          target_ulong args,
    212                                          uint32_t nret, target_ulong rets)
    213{
    214    SpaprPhbState *sphb;
    215    SpaprTceTable *tcet;
    216    uint32_t liobn;
    217
    218    if ((nargs != 1) || (nret != 1)) {
    219        goto param_error_exit;
    220    }
    221
    222    liobn = rtas_ld(args, 0);
    223    tcet = spapr_tce_find_by_liobn(liobn);
    224    if (!tcet) {
    225        goto param_error_exit;
    226    }
    227
    228    sphb = SPAPR_PCI_HOST_BRIDGE(OBJECT(tcet)->parent);
    229    if (!sphb || !sphb->ddw_enabled || !tcet->nb_table) {
    230        goto param_error_exit;
    231    }
    232
    233    spapr_tce_table_disable(tcet);
    234    trace_spapr_iommu_ddw_remove(liobn);
    235
    236    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
    237    return;
    238
    239param_error_exit:
    240    rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
    241}
    242
    243static void rtas_ibm_reset_pe_dma_window(PowerPCCPU *cpu,
    244                                         SpaprMachineState *spapr,
    245                                         uint32_t token, uint32_t nargs,
    246                                         target_ulong args,
    247                                         uint32_t nret, target_ulong rets)
    248{
    249    SpaprPhbState *sphb;
    250    uint64_t buid;
    251    uint32_t addr;
    252
    253    if ((nargs != 3) || (nret != 1)) {
    254        goto param_error_exit;
    255    }
    256
    257    buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
    258    addr = rtas_ld(args, 0);
    259    sphb = spapr_pci_find_phb(spapr, buid);
    260    if (!sphb || !sphb->ddw_enabled) {
    261        goto param_error_exit;
    262    }
    263
    264    spapr_phb_dma_reset(sphb);
    265    trace_spapr_iommu_ddw_reset(buid, addr);
    266
    267    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
    268
    269    return;
    270
    271param_error_exit:
    272    rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
    273}
    274
    275static void spapr_rtas_ddw_init(void)
    276{
    277    spapr_rtas_register(RTAS_IBM_QUERY_PE_DMA_WINDOW,
    278                        "ibm,query-pe-dma-window",
    279                        rtas_ibm_query_pe_dma_window);
    280    spapr_rtas_register(RTAS_IBM_CREATE_PE_DMA_WINDOW,
    281                        "ibm,create-pe-dma-window",
    282                        rtas_ibm_create_pe_dma_window);
    283    spapr_rtas_register(RTAS_IBM_REMOVE_PE_DMA_WINDOW,
    284                        "ibm,remove-pe-dma-window",
    285                        rtas_ibm_remove_pe_dma_window);
    286    spapr_rtas_register(RTAS_IBM_RESET_PE_DMA_WINDOW,
    287                        "ibm,reset-pe-dma-window",
    288                        rtas_ibm_reset_pe_dma_window);
    289}
    290
    291type_init(spapr_rtas_ddw_init)