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

mx_pic.c (10785B)


      1/*
      2 * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab.
      3 * All rights reserved.
      4 *
      5 * Redistribution and use in source and binary forms, with or without
      6 * modification, are permitted provided that the following conditions are met:
      7 *     * Redistributions of source code must retain the above copyright
      8 *       notice, this list of conditions and the following disclaimer.
      9 *     * Redistributions in binary form must reproduce the above copyright
     10 *       notice, this list of conditions and the following disclaimer in the
     11 *       documentation and/or other materials provided with the distribution.
     12 *     * Neither the name of the Open Source and Linux Lab nor the
     13 *       names of its contributors may be used to endorse or promote products
     14 *       derived from this software without specific prior written permission.
     15 *
     16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26 */
     27
     28#include "qemu/osdep.h"
     29#include "hw/irq.h"
     30#include "hw/xtensa/mx_pic.h"
     31#include "qemu/log.h"
     32
     33#define MX_MAX_CPU 32
     34#define MX_MAX_IRQ 32
     35
     36#define MIROUT 0x0
     37#define MIPICAUSE 0x100
     38#define MIPISET 0x140
     39#define MIENG 0x180
     40#define MIENGSET 0x184
     41#define MIASG 0x188
     42#define MIASGSET 0x18c
     43#define MIPIPART 0x190
     44#define SYSCFGID 0x1a0
     45#define MPSCORE 0x200
     46#define CCON 0x220
     47
     48struct XtensaMxPic {
     49    unsigned n_cpu;
     50    unsigned n_irq;
     51
     52    uint32_t ext_irq_state;
     53    uint32_t mieng;
     54    uint32_t miasg;
     55    uint32_t mirout[MX_MAX_IRQ];
     56    uint32_t mipipart;
     57    uint32_t runstall;
     58
     59    qemu_irq *irq_inputs;
     60    struct XtensaMxPicCpu {
     61        XtensaMxPic *mx;
     62        qemu_irq *irq;
     63        qemu_irq runstall;
     64        uint32_t mipicause;
     65        uint32_t mirout_cache;
     66        uint32_t irq_state_cache;
     67        uint32_t ccon;
     68        MemoryRegion reg;
     69    } cpu[MX_MAX_CPU];
     70};
     71
     72static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset,
     73                                           unsigned size)
     74{
     75    struct XtensaMxPicCpu *mx_cpu = opaque;
     76    struct XtensaMxPic *mx = mx_cpu->mx;
     77
     78    if (offset < MIROUT + MX_MAX_IRQ) {
     79        return mx->mirout[offset - MIROUT];
     80    } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) {
     81        return mx->cpu[offset - MIPICAUSE].mipicause;
     82    } else {
     83        switch (offset) {
     84        case MIENG:
     85            return mx->mieng;
     86
     87        case MIASG:
     88            return mx->miasg;
     89
     90        case MIPIPART:
     91            return mx->mipipart;
     92
     93        case SYSCFGID:
     94            return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu);
     95
     96        case MPSCORE:
     97            return mx->runstall;
     98
     99        case CCON:
    100            return mx_cpu->ccon;
    101
    102        default:
    103            qemu_log_mask(LOG_GUEST_ERROR,
    104                          "unknown RER in MX PIC range: 0x%08x\n",
    105                          (uint32_t)offset);
    106            return 0;
    107        }
    108    }
    109}
    110
    111static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx,
    112                                              unsigned cpu)
    113{
    114    uint32_t mipicause = mx->cpu[cpu].mipicause;
    115    uint32_t mipipart = mx->mipipart;
    116
    117    return (((mipicause & 1) << (mipipart & 3)) |
    118            ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) |
    119            ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) |
    120            ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7;
    121}
    122
    123static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx,
    124                                                  unsigned cpu)
    125{
    126    return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) &
    127             mx->cpu[cpu].mirout_cache) << 2) |
    128        xtensa_mx_pic_get_ipi_for_cpu(mx, cpu);
    129}
    130
    131static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu)
    132{
    133    uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu);
    134    uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq;
    135    unsigned i;
    136
    137    qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n",
    138                  __func__, cpu, irq, changed_irq);
    139    mx->cpu[cpu].irq_state_cache = irq;
    140    for (i = 0; changed_irq; ++i) {
    141        uint32_t mask = 1u << i;
    142
    143        if (changed_irq & mask) {
    144            changed_irq ^= mask;
    145            qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask);
    146        }
    147    }
    148}
    149
    150static void xtensa_mx_pic_update_all(XtensaMxPic *mx)
    151{
    152    unsigned cpu;
    153
    154    for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
    155        xtensa_mx_pic_update_cpu(mx, cpu);
    156    }
    157}
    158
    159static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset,
    160                                        uint64_t v, unsigned size)
    161{
    162    struct XtensaMxPicCpu *mx_cpu = opaque;
    163    struct XtensaMxPic *mx = mx_cpu->mx;
    164    unsigned cpu;
    165
    166    if (offset < MIROUT + mx->n_irq) {
    167        mx->mirout[offset - MIROUT] = v;
    168        for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
    169            uint32_t mask = 1u << (offset - MIROUT);
    170
    171            if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) {
    172                mx->cpu[cpu].mirout_cache ^= mask;
    173                xtensa_mx_pic_update_cpu(mx, cpu);
    174            }
    175        }
    176    } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) {
    177        cpu = offset - MIPICAUSE;
    178        mx->cpu[cpu].mipicause &= ~v;
    179        xtensa_mx_pic_update_cpu(mx, cpu);
    180    } else if (offset >= MIPISET && offset < MIPISET + 16) {
    181        for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
    182            if (v & (1u << cpu)) {
    183                mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET);
    184                xtensa_mx_pic_update_cpu(mx, cpu);
    185            }
    186        }
    187    } else {
    188        uint32_t change = 0;
    189        uint32_t oldv, newv;
    190        const char *name = "???";
    191
    192        switch (offset) {
    193        case MIENG:
    194            change = mx->mieng & v;
    195            oldv = mx->mieng;
    196            mx->mieng &= ~v;
    197            newv = mx->mieng;
    198            name = "MIENG";
    199            break;
    200
    201        case MIENGSET:
    202            change = ~mx->mieng & v;
    203            oldv = mx->mieng;
    204            mx->mieng |= v;
    205            newv = mx->mieng;
    206            name = "MIENG";
    207            break;
    208
    209        case MIASG:
    210            change = mx->miasg & v;
    211            oldv = mx->miasg;
    212            mx->miasg &= ~v;
    213            newv = mx->miasg;
    214            name = "MIASG";
    215            break;
    216
    217        case MIASGSET:
    218            change = ~mx->miasg & v;
    219            oldv = mx->miasg;
    220            mx->miasg |= v;
    221            newv = mx->miasg;
    222            name = "MIASG";
    223            break;
    224
    225        case MIPIPART:
    226            change = mx->mipipart ^ v;
    227            oldv = mx->mipipart;
    228            mx->mipipart = v;
    229            newv = mx->mipipart;
    230            name = "MIPIPART";
    231            break;
    232
    233        case MPSCORE:
    234            change = mx->runstall ^ v;
    235            oldv = mx->runstall;
    236            mx->runstall = v;
    237            newv = mx->runstall;
    238            name = "RUNSTALL";
    239            for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
    240                if (change & (1u << cpu)) {
    241                    qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu));
    242                }
    243            }
    244            break;
    245
    246        case CCON:
    247            mx_cpu->ccon = v & 0x1;
    248            break;
    249
    250        default:
    251            qemu_log_mask(LOG_GUEST_ERROR,
    252                          "unknown WER in MX PIC range: 0x%08x = 0x%08x\n",
    253                          (uint32_t)offset, (uint32_t)v);
    254            break;
    255        }
    256        if (change) {
    257            qemu_log_mask(CPU_LOG_INT,
    258                          "%s: %s changed by CPU %d: %08x -> %08x\n",
    259                          __func__, name, (int)(mx_cpu - mx->cpu),
    260                          oldv, newv);
    261            xtensa_mx_pic_update_all(mx);
    262        }
    263    }
    264}
    265
    266static const MemoryRegionOps xtensa_mx_pic_ops = {
    267    .read = xtensa_mx_pic_ext_reg_read,
    268    .write = xtensa_mx_pic_ext_reg_write,
    269    .endianness = DEVICE_NATIVE_ENDIAN,
    270    .valid = {
    271        .unaligned = true,
    272    },
    273};
    274
    275MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx,
    276                                         qemu_irq *irq,
    277                                         qemu_irq runstall)
    278{
    279    struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu;
    280
    281    mx_cpu->mx = mx;
    282    mx_cpu->irq = irq;
    283    mx_cpu->runstall = runstall;
    284
    285    memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu,
    286                          "mx_pic", 0x280);
    287
    288    ++mx->n_cpu;
    289    return &mx_cpu->reg;
    290}
    291
    292static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active)
    293{
    294    XtensaMxPic *mx = opaque;
    295
    296    if (irq < mx->n_irq) {
    297        uint32_t old_irq_state = mx->ext_irq_state;
    298
    299        if (active) {
    300            mx->ext_irq_state |= 1u << irq;
    301        } else {
    302            mx->ext_irq_state &= ~(1u << irq);
    303        }
    304        if (old_irq_state != mx->ext_irq_state) {
    305            qemu_log_mask(CPU_LOG_INT,
    306                          "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n",
    307                          __func__, irq, active,
    308                          old_irq_state, mx->ext_irq_state);
    309            xtensa_mx_pic_update_all(mx);
    310        }
    311    } else {
    312        qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n",
    313                      __func__, irq);
    314    }
    315}
    316
    317XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq)
    318{
    319    XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic));
    320
    321    mx->n_irq = n_irq + 1;
    322    mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx,
    323                                        mx->n_irq);
    324    return mx;
    325}
    326
    327void xtensa_mx_pic_reset(void *opaque)
    328{
    329    XtensaMxPic *mx = opaque;
    330    unsigned i;
    331
    332    mx->ext_irq_state = 0;
    333    mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u;
    334    mx->miasg = 0;
    335    mx->mipipart = 0;
    336    for (i = 0; i < mx->n_irq; ++i) {
    337        mx->mirout[i] = 1;
    338    }
    339    for (i = 0; i < mx->n_cpu; ++i) {
    340        mx->cpu[i].mipicause = 0;
    341        mx->cpu[i].mirout_cache = i ? 0 : mx->mieng;
    342        mx->cpu[i].irq_state_cache = 0;
    343        mx->cpu[i].ccon = 0;
    344    }
    345    mx->runstall = (1u << mx->n_cpu) - 2;
    346    for (i = 0; i < mx->n_cpu; ++i) {
    347        qemu_set_irq(mx->cpu[i].runstall, i > 0);
    348    }
    349}
    350
    351qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
    352{
    353    return mx->irq_inputs + 1;
    354}