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

sifive_plic.c (19710B)


      1/*
      2 * SiFive PLIC (Platform Level Interrupt Controller)
      3 *
      4 * Copyright (c) 2017 SiFive, Inc.
      5 *
      6 * This provides a parameterizable interrupt controller based on SiFive's PLIC.
      7 *
      8 * This program is free software; you can redistribute it and/or modify it
      9 * under the terms and conditions of the GNU General Public License,
     10 * version 2 or later, as published by the Free Software Foundation.
     11 *
     12 * This program is distributed in the hope it will be useful, but WITHOUT
     13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     15 * more details.
     16 *
     17 * You should have received a copy of the GNU General Public License along with
     18 * this program.  If not, see <http://www.gnu.org/licenses/>.
     19 */
     20
     21#include "qemu/osdep.h"
     22#include "qapi/error.h"
     23#include "qemu/log.h"
     24#include "qemu/module.h"
     25#include "qemu/error-report.h"
     26#include "hw/sysbus.h"
     27#include "hw/pci/msi.h"
     28#include "hw/qdev-properties.h"
     29#include "hw/intc/sifive_plic.h"
     30#include "target/riscv/cpu.h"
     31#include "migration/vmstate.h"
     32#include "hw/irq.h"
     33
     34#define RISCV_DEBUG_PLIC 0
     35
     36static PLICMode char_to_mode(char c)
     37{
     38    switch (c) {
     39    case 'U': return PLICMode_U;
     40    case 'S': return PLICMode_S;
     41    case 'H': return PLICMode_H;
     42    case 'M': return PLICMode_M;
     43    default:
     44        error_report("plic: invalid mode '%c'", c);
     45        exit(1);
     46    }
     47}
     48
     49static char mode_to_char(PLICMode m)
     50{
     51    switch (m) {
     52    case PLICMode_U: return 'U';
     53    case PLICMode_S: return 'S';
     54    case PLICMode_H: return 'H';
     55    case PLICMode_M: return 'M';
     56    default: return '?';
     57    }
     58}
     59
     60static void sifive_plic_print_state(SiFivePLICState *plic)
     61{
     62    int i;
     63    int addrid;
     64
     65    /* pending */
     66    qemu_log("pending       : ");
     67    for (i = plic->bitfield_words - 1; i >= 0; i--) {
     68        qemu_log("%08x", plic->pending[i]);
     69    }
     70    qemu_log("\n");
     71
     72    /* pending */
     73    qemu_log("claimed       : ");
     74    for (i = plic->bitfield_words - 1; i >= 0; i--) {
     75        qemu_log("%08x", plic->claimed[i]);
     76    }
     77    qemu_log("\n");
     78
     79    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
     80        qemu_log("hart%d-%c enable: ",
     81            plic->addr_config[addrid].hartid,
     82            mode_to_char(plic->addr_config[addrid].mode));
     83        for (i = plic->bitfield_words - 1; i >= 0; i--) {
     84            qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
     85        }
     86        qemu_log("\n");
     87    }
     88}
     89
     90static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
     91{
     92    uint32_t old, new, cmp = qatomic_read(a);
     93
     94    do {
     95        old = cmp;
     96        new = (old & ~mask) | (value & mask);
     97        cmp = qatomic_cmpxchg(a, old, new);
     98    } while (old != cmp);
     99
    100    return old;
    101}
    102
    103static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
    104{
    105    atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
    106}
    107
    108static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
    109{
    110    atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
    111}
    112
    113static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
    114{
    115    int i, j;
    116    for (i = 0; i < plic->bitfield_words; i++) {
    117        uint32_t pending_enabled_not_claimed =
    118            (plic->pending[i] & ~plic->claimed[i]) &
    119            plic->enable[addrid * plic->bitfield_words + i];
    120        if (!pending_enabled_not_claimed) {
    121            continue;
    122        }
    123        for (j = 0; j < 32; j++) {
    124            int irq = (i << 5) + j;
    125            uint32_t prio = plic->source_priority[irq];
    126            int enabled = pending_enabled_not_claimed & (1 << j);
    127            if (enabled && prio > plic->target_priority[addrid]) {
    128                return 1;
    129            }
    130        }
    131    }
    132    return 0;
    133}
    134
    135static void sifive_plic_update(SiFivePLICState *plic)
    136{
    137    int addrid;
    138
    139    /* raise irq on harts where this irq is enabled */
    140    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
    141        uint32_t hartid = plic->addr_config[addrid].hartid;
    142        PLICMode mode = plic->addr_config[addrid].mode;
    143        int level = sifive_plic_irqs_pending(plic, addrid);
    144
    145        switch (mode) {
    146        case PLICMode_M:
    147            qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level);
    148            break;
    149        case PLICMode_S:
    150            qemu_set_irq(plic->s_external_irqs[hartid - plic->hartid_base], level);
    151            break;
    152        default:
    153            break;
    154        }
    155    }
    156
    157    if (RISCV_DEBUG_PLIC) {
    158        sifive_plic_print_state(plic);
    159    }
    160}
    161
    162static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
    163{
    164    int i, j;
    165    uint32_t max_irq = 0;
    166    uint32_t max_prio = plic->target_priority[addrid];
    167
    168    for (i = 0; i < plic->bitfield_words; i++) {
    169        uint32_t pending_enabled_not_claimed =
    170            (plic->pending[i] & ~plic->claimed[i]) &
    171            plic->enable[addrid * plic->bitfield_words + i];
    172        if (!pending_enabled_not_claimed) {
    173            continue;
    174        }
    175        for (j = 0; j < 32; j++) {
    176            int irq = (i << 5) + j;
    177            uint32_t prio = plic->source_priority[irq];
    178            int enabled = pending_enabled_not_claimed & (1 << j);
    179            if (enabled && prio > max_prio) {
    180                max_irq = irq;
    181                max_prio = prio;
    182            }
    183        }
    184    }
    185
    186    if (max_irq) {
    187        sifive_plic_set_pending(plic, max_irq, false);
    188        sifive_plic_set_claimed(plic, max_irq, true);
    189    }
    190    return max_irq;
    191}
    192
    193static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
    194{
    195    SiFivePLICState *plic = opaque;
    196
    197    /* writes must be 4 byte words */
    198    if ((addr & 0x3) != 0) {
    199        goto err;
    200    }
    201
    202    if (addr >= plic->priority_base && /* 4 bytes per source */
    203        addr < plic->priority_base + (plic->num_sources << 2))
    204    {
    205        uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
    206        if (RISCV_DEBUG_PLIC) {
    207            qemu_log("plic: read priority: irq=%d priority=%d\n",
    208                irq, plic->source_priority[irq]);
    209        }
    210        return plic->source_priority[irq];
    211    } else if (addr >= plic->pending_base && /* 1 bit per source */
    212               addr < plic->pending_base + (plic->num_sources >> 3))
    213    {
    214        uint32_t word = (addr - plic->pending_base) >> 2;
    215        if (RISCV_DEBUG_PLIC) {
    216            qemu_log("plic: read pending: word=%d value=%d\n",
    217                word, plic->pending[word]);
    218        }
    219        return plic->pending[word];
    220    } else if (addr >= plic->enable_base && /* 1 bit per source */
    221             addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
    222    {
    223        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
    224        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
    225        if (wordid < plic->bitfield_words) {
    226            if (RISCV_DEBUG_PLIC) {
    227                qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
    228                    plic->addr_config[addrid].hartid,
    229                    mode_to_char(plic->addr_config[addrid].mode), wordid,
    230                    plic->enable[addrid * plic->bitfield_words + wordid]);
    231            }
    232            return plic->enable[addrid * plic->bitfield_words + wordid];
    233        }
    234    } else if (addr >= plic->context_base && /* 1 bit per source */
    235             addr < plic->context_base + plic->num_addrs * plic->context_stride)
    236    {
    237        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
    238        uint32_t contextid = (addr & (plic->context_stride - 1));
    239        if (contextid == 0) {
    240            if (RISCV_DEBUG_PLIC) {
    241                qemu_log("plic: read priority: hart%d-%c priority=%x\n",
    242                    plic->addr_config[addrid].hartid,
    243                    mode_to_char(plic->addr_config[addrid].mode),
    244                    plic->target_priority[addrid]);
    245            }
    246            return plic->target_priority[addrid];
    247        } else if (contextid == 4) {
    248            uint32_t value = sifive_plic_claim(plic, addrid);
    249            if (RISCV_DEBUG_PLIC) {
    250                qemu_log("plic: read claim: hart%d-%c irq=%x\n",
    251                    plic->addr_config[addrid].hartid,
    252                    mode_to_char(plic->addr_config[addrid].mode),
    253                    value);
    254            }
    255            sifive_plic_update(plic);
    256            return value;
    257        }
    258    }
    259
    260err:
    261    qemu_log_mask(LOG_GUEST_ERROR,
    262                  "%s: Invalid register read 0x%" HWADDR_PRIx "\n",
    263                  __func__, addr);
    264    return 0;
    265}
    266
    267static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
    268        unsigned size)
    269{
    270    SiFivePLICState *plic = opaque;
    271
    272    /* writes must be 4 byte words */
    273    if ((addr & 0x3) != 0) {
    274        goto err;
    275    }
    276
    277    if (addr >= plic->priority_base && /* 4 bytes per source */
    278        addr < plic->priority_base + (plic->num_sources << 2))
    279    {
    280        uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
    281        plic->source_priority[irq] = value & 7;
    282        if (RISCV_DEBUG_PLIC) {
    283            qemu_log("plic: write priority: irq=%d priority=%d\n",
    284                irq, plic->source_priority[irq]);
    285        }
    286        sifive_plic_update(plic);
    287        return;
    288    } else if (addr >= plic->pending_base && /* 1 bit per source */
    289               addr < plic->pending_base + (plic->num_sources >> 3))
    290    {
    291        qemu_log_mask(LOG_GUEST_ERROR,
    292                      "%s: invalid pending write: 0x%" HWADDR_PRIx "",
    293                      __func__, addr);
    294        return;
    295    } else if (addr >= plic->enable_base && /* 1 bit per source */
    296        addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
    297    {
    298        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
    299        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
    300        if (wordid < plic->bitfield_words) {
    301            plic->enable[addrid * plic->bitfield_words + wordid] = value;
    302            if (RISCV_DEBUG_PLIC) {
    303                qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
    304                    plic->addr_config[addrid].hartid,
    305                    mode_to_char(plic->addr_config[addrid].mode), wordid,
    306                    plic->enable[addrid * plic->bitfield_words + wordid]);
    307            }
    308            return;
    309        }
    310    } else if (addr >= plic->context_base && /* 4 bytes per reg */
    311        addr < plic->context_base + plic->num_addrs * plic->context_stride)
    312    {
    313        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
    314        uint32_t contextid = (addr & (plic->context_stride - 1));
    315        if (contextid == 0) {
    316            if (RISCV_DEBUG_PLIC) {
    317                qemu_log("plic: write priority: hart%d-%c priority=%x\n",
    318                    plic->addr_config[addrid].hartid,
    319                    mode_to_char(plic->addr_config[addrid].mode),
    320                    plic->target_priority[addrid]);
    321            }
    322            if (value <= plic->num_priorities) {
    323                plic->target_priority[addrid] = value;
    324                sifive_plic_update(plic);
    325            }
    326            return;
    327        } else if (contextid == 4) {
    328            if (RISCV_DEBUG_PLIC) {
    329                qemu_log("plic: write claim: hart%d-%c irq=%x\n",
    330                    plic->addr_config[addrid].hartid,
    331                    mode_to_char(plic->addr_config[addrid].mode),
    332                    (uint32_t)value);
    333            }
    334            if (value < plic->num_sources) {
    335                sifive_plic_set_claimed(plic, value, false);
    336                sifive_plic_update(plic);
    337            }
    338            return;
    339        }
    340    }
    341
    342err:
    343    qemu_log_mask(LOG_GUEST_ERROR,
    344                  "%s: Invalid register write 0x%" HWADDR_PRIx "\n",
    345                  __func__, addr);
    346}
    347
    348static const MemoryRegionOps sifive_plic_ops = {
    349    .read = sifive_plic_read,
    350    .write = sifive_plic_write,
    351    .endianness = DEVICE_LITTLE_ENDIAN,
    352    .valid = {
    353        .min_access_size = 4,
    354        .max_access_size = 4
    355    }
    356};
    357
    358static Property sifive_plic_properties[] = {
    359    DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
    360    DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0),
    361    DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
    362    DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
    363    DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
    364    DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
    365    DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
    366    DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
    367    DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
    368    DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
    369    DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
    370    DEFINE_PROP_END_OF_LIST(),
    371};
    372
    373/*
    374 * parse PLIC hart/mode address offset config
    375 *
    376 * "M"              1 hart with M mode
    377 * "MS,MS"          2 harts, 0-1 with M and S mode
    378 * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
    379 */
    380static void parse_hart_config(SiFivePLICState *plic)
    381{
    382    int addrid, hartid, modes;
    383    const char *p;
    384    char c;
    385
    386    /* count and validate hart/mode combinations */
    387    addrid = 0, hartid = 0, modes = 0;
    388    p = plic->hart_config;
    389    while ((c = *p++)) {
    390        if (c == ',') {
    391            addrid += ctpop8(modes);
    392            modes = 0;
    393            hartid++;
    394        } else {
    395            int m = 1 << char_to_mode(c);
    396            if (modes == (modes | m)) {
    397                error_report("plic: duplicate mode '%c' in config: %s",
    398                             c, plic->hart_config);
    399                exit(1);
    400            }
    401            modes |= m;
    402        }
    403    }
    404    if (modes) {
    405        addrid += ctpop8(modes);
    406    }
    407    hartid++;
    408
    409    plic->num_addrs = addrid;
    410    plic->num_harts = hartid;
    411
    412    /* store hart/mode combinations */
    413    plic->addr_config = g_new(PLICAddr, plic->num_addrs);
    414    addrid = 0, hartid = plic->hartid_base;
    415    p = plic->hart_config;
    416    while ((c = *p++)) {
    417        if (c == ',') {
    418            hartid++;
    419        } else {
    420            plic->addr_config[addrid].addrid = addrid;
    421            plic->addr_config[addrid].hartid = hartid;
    422            plic->addr_config[addrid].mode = char_to_mode(c);
    423            addrid++;
    424        }
    425    }
    426}
    427
    428static void sifive_plic_irq_request(void *opaque, int irq, int level)
    429{
    430    SiFivePLICState *plic = opaque;
    431    if (RISCV_DEBUG_PLIC) {
    432        qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
    433    }
    434    sifive_plic_set_pending(plic, irq, level > 0);
    435    sifive_plic_update(plic);
    436}
    437
    438static void sifive_plic_realize(DeviceState *dev, Error **errp)
    439{
    440    SiFivePLICState *plic = SIFIVE_PLIC(dev);
    441    int i;
    442
    443    memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
    444                          TYPE_SIFIVE_PLIC, plic->aperture_size);
    445    parse_hart_config(plic);
    446    plic->bitfield_words = (plic->num_sources + 31) >> 5;
    447    plic->num_enables = plic->bitfield_words * plic->num_addrs;
    448    plic->source_priority = g_new0(uint32_t, plic->num_sources);
    449    plic->target_priority = g_new(uint32_t, plic->num_addrs);
    450    plic->pending = g_new0(uint32_t, plic->bitfield_words);
    451    plic->claimed = g_new0(uint32_t, plic->bitfield_words);
    452    plic->enable = g_new0(uint32_t, plic->num_enables);
    453    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
    454    qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
    455
    456    plic->s_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts);
    457    qdev_init_gpio_out(dev, plic->s_external_irqs, plic->num_harts);
    458
    459    plic->m_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts);
    460    qdev_init_gpio_out(dev, plic->m_external_irqs, plic->num_harts);
    461
    462    /* We can't allow the supervisor to control SEIP as this would allow the
    463     * supervisor to clear a pending external interrupt which will result in
    464     * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
    465     * hardware controlled when a PLIC is attached.
    466     */
    467    for (i = 0; i < plic->num_harts; i++) {
    468        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(plic->hartid_base + i));
    469        if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
    470            error_report("SEIP already claimed");
    471            exit(1);
    472        }
    473    }
    474
    475    msi_nonbroken = true;
    476}
    477
    478static const VMStateDescription vmstate_sifive_plic = {
    479    .name = "riscv_sifive_plic",
    480    .version_id = 1,
    481    .minimum_version_id = 1,
    482    .fields = (VMStateField[]) {
    483            VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState,
    484                                  num_sources, 0,
    485                                  vmstate_info_uint32, uint32_t),
    486            VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState,
    487                                  num_addrs, 0,
    488                                  vmstate_info_uint32, uint32_t),
    489            VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0,
    490                                  vmstate_info_uint32, uint32_t),
    491            VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0,
    492                                  vmstate_info_uint32, uint32_t),
    493            VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0,
    494                                  vmstate_info_uint32, uint32_t),
    495            VMSTATE_END_OF_LIST()
    496        }
    497};
    498
    499static void sifive_plic_class_init(ObjectClass *klass, void *data)
    500{
    501    DeviceClass *dc = DEVICE_CLASS(klass);
    502
    503    device_class_set_props(dc, sifive_plic_properties);
    504    dc->realize = sifive_plic_realize;
    505    dc->vmsd = &vmstate_sifive_plic;
    506}
    507
    508static const TypeInfo sifive_plic_info = {
    509    .name          = TYPE_SIFIVE_PLIC,
    510    .parent        = TYPE_SYS_BUS_DEVICE,
    511    .instance_size = sizeof(SiFivePLICState),
    512    .class_init    = sifive_plic_class_init,
    513};
    514
    515static void sifive_plic_register_types(void)
    516{
    517    type_register_static(&sifive_plic_info);
    518}
    519
    520type_init(sifive_plic_register_types)
    521
    522/*
    523 * Create PLIC device.
    524 */
    525DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
    526    uint32_t num_harts,
    527    uint32_t hartid_base, uint32_t num_sources,
    528    uint32_t num_priorities, uint32_t priority_base,
    529    uint32_t pending_base, uint32_t enable_base,
    530    uint32_t enable_stride, uint32_t context_base,
    531    uint32_t context_stride, uint32_t aperture_size)
    532{
    533    DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
    534    int i;
    535
    536    assert(enable_stride == (enable_stride & -enable_stride));
    537    assert(context_stride == (context_stride & -context_stride));
    538    qdev_prop_set_string(dev, "hart-config", hart_config);
    539    qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
    540    qdev_prop_set_uint32(dev, "num-sources", num_sources);
    541    qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
    542    qdev_prop_set_uint32(dev, "priority-base", priority_base);
    543    qdev_prop_set_uint32(dev, "pending-base", pending_base);
    544    qdev_prop_set_uint32(dev, "enable-base", enable_base);
    545    qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
    546    qdev_prop_set_uint32(dev, "context-base", context_base);
    547    qdev_prop_set_uint32(dev, "context-stride", context_stride);
    548    qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
    549    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
    550    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
    551
    552    for (i = 0; i < num_harts; i++) {
    553        CPUState *cpu = qemu_get_cpu(hartid_base + i);
    554
    555        qdev_connect_gpio_out(dev, i,
    556                              qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
    557        qdev_connect_gpio_out(dev, num_harts + i,
    558                              qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
    559    }
    560
    561    return dev;
    562}