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

loongson_liointc.c (6371B)


      1/*
      2 * QEMU Loongson Local I/O interrupt controler.
      3 *
      4 * Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
      5 * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
      6 *
      7 * This program is free software: you can redistribute it and/or modify
      8 * it under the terms of the GNU General Public License as published by
      9 * the Free Software Foundation, either version 2 of the License, or
     10 * (at your option) any later version.
     11 *
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     15 * GNU General Public License for more details.
     16 *
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
     19 *
     20 */
     21
     22#include "qemu/osdep.h"
     23#include "qemu/module.h"
     24#include "qemu/log.h"
     25#include "hw/irq.h"
     26#include "hw/qdev-properties.h"
     27#include "hw/intc/loongson_liointc.h"
     28
     29#define NUM_IRQS                32
     30
     31#define NUM_CORES               4
     32#define NUM_IPS                 4
     33#define NUM_PARENTS             (NUM_CORES * NUM_IPS)
     34#define PARENT_COREx_IPy(x, y)  (NUM_IPS * x + y)
     35
     36#define R_MAPPER_START          0x0
     37#define R_MAPPER_END            0x20
     38#define R_ISR                   R_MAPPER_END
     39#define R_IEN                   0x24
     40#define R_IEN_SET               0x28
     41#define R_IEN_CLR               0x2c
     42#define R_ISR_SIZE              0x8
     43#define R_START                 0x40
     44#define R_END                   (R_START + R_ISR_SIZE * NUM_CORES)
     45
     46struct loongson_liointc {
     47    SysBusDevice parent_obj;
     48
     49    MemoryRegion mmio;
     50    qemu_irq parent_irq[NUM_PARENTS];
     51
     52    uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */
     53    uint32_t isr;
     54    uint32_t ien;
     55    uint32_t per_core_isr[NUM_CORES];
     56
     57    /* state of the interrupt input pins */
     58    uint32_t pin_state;
     59    bool parent_state[NUM_PARENTS];
     60};
     61
     62static void update_irq(struct loongson_liointc *p)
     63{
     64    uint32_t irq, core, ip;
     65    uint32_t per_ip_isr[NUM_IPS] = {0};
     66
     67    /* level triggered interrupt */
     68    p->isr = p->pin_state;
     69
     70    /* Clear disabled IRQs */
     71    p->isr &= p->ien;
     72
     73    /* Clear per_core_isr */
     74    for (core = 0; core < NUM_CORES; core++) {
     75        p->per_core_isr[core] = 0;
     76    }
     77
     78    /* Update per_core_isr and per_ip_isr */
     79    for (irq = 0; irq < NUM_IRQS; irq++) {
     80        if (!(p->isr & (1 << irq))) {
     81            continue;
     82        }
     83
     84        for (core = 0; core < NUM_CORES; core++) {
     85            if ((p->mapper[irq] & (1 << core))) {
     86                p->per_core_isr[core] |= (1 << irq);
     87            }
     88        }
     89
     90        for (ip = 0; ip < NUM_IPS; ip++) {
     91            if ((p->mapper[irq] & (1 << (ip + 4)))) {
     92                per_ip_isr[ip] |= (1 << irq);
     93            }
     94        }
     95    }
     96
     97    /* Emit IRQ to parent! */
     98    for (core = 0; core < NUM_CORES; core++) {
     99        for (ip = 0; ip < NUM_IPS; ip++) {
    100            int parent = PARENT_COREx_IPy(core, ip);
    101            if (p->parent_state[parent] !=
    102                (!!p->per_core_isr[core] && !!per_ip_isr[ip])) {
    103                p->parent_state[parent] = !p->parent_state[parent];
    104                qemu_set_irq(p->parent_irq[parent], p->parent_state[parent]);
    105            }
    106        }
    107    }
    108}
    109
    110static uint64_t
    111liointc_read(void *opaque, hwaddr addr, unsigned int size)
    112{
    113    struct loongson_liointc *p = opaque;
    114    uint32_t r = 0;
    115
    116    /* Mapper is 1 byte */
    117    if (size == 1 && addr < R_MAPPER_END) {
    118        r = p->mapper[addr];
    119        goto out;
    120    }
    121
    122    /* Rest are 4 bytes */
    123    if (size != 4 || (addr % 4)) {
    124        goto out;
    125    }
    126
    127    if (addr >= R_START && addr < R_END) {
    128        hwaddr offset = addr - R_START;
    129        int core = offset / R_ISR_SIZE;
    130
    131        if (offset % R_ISR_SIZE) {
    132            goto out;
    133        }
    134        r = p->per_core_isr[core];
    135        goto out;
    136    }
    137
    138    switch (addr) {
    139    case R_ISR:
    140        r = p->isr;
    141        break;
    142    case R_IEN:
    143        r = p->ien;
    144        break;
    145    default:
    146        break;
    147    }
    148
    149out:
    150    qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
    151                  __func__, size, addr, r);
    152    return r;
    153}
    154
    155static void
    156liointc_write(void *opaque, hwaddr addr,
    157          uint64_t val64, unsigned int size)
    158{
    159    struct loongson_liointc *p = opaque;
    160    uint32_t value = val64;
    161
    162    qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
    163                  __func__, size, addr, value);
    164
    165    /* Mapper is 1 byte */
    166    if (size == 1 && addr < R_MAPPER_END) {
    167        p->mapper[addr] = value;
    168        goto out;
    169    }
    170
    171    /* Rest are 4 bytes */
    172    if (size != 4 || (addr % 4)) {
    173        goto out;
    174    }
    175
    176    if (addr >= R_START && addr < R_END) {
    177        hwaddr offset = addr - R_START;
    178        int core = offset / R_ISR_SIZE;
    179
    180        if (offset % R_ISR_SIZE) {
    181            goto out;
    182        }
    183        p->per_core_isr[core] = value;
    184        goto out;
    185    }
    186
    187    switch (addr) {
    188    case R_IEN_SET:
    189        p->ien |= value;
    190        break;
    191    case R_IEN_CLR:
    192        p->ien &= ~value;
    193        break;
    194    default:
    195        break;
    196    }
    197
    198out:
    199    update_irq(p);
    200}
    201
    202static const MemoryRegionOps pic_ops = {
    203    .read = liointc_read,
    204    .write = liointc_write,
    205    .endianness = DEVICE_NATIVE_ENDIAN,
    206    .valid = {
    207        .min_access_size = 1,
    208        .max_access_size = 4
    209    }
    210};
    211
    212static void irq_handler(void *opaque, int irq, int level)
    213{
    214    struct loongson_liointc *p = opaque;
    215
    216    p->pin_state &= ~(1 << irq);
    217    p->pin_state |= level << irq;
    218    update_irq(p);
    219}
    220
    221static void loongson_liointc_init(Object *obj)
    222{
    223    struct loongson_liointc *p = LOONGSON_LIOINTC(obj);
    224    int i;
    225
    226    qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
    227
    228    for (i = 0; i < NUM_PARENTS; i++) {
    229        sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]);
    230    }
    231
    232    memory_region_init_io(&p->mmio, obj, &pic_ops, p,
    233                         TYPE_LOONGSON_LIOINTC, R_END);
    234    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
    235}
    236
    237static const TypeInfo loongson_liointc_info = {
    238    .name          = TYPE_LOONGSON_LIOINTC,
    239    .parent        = TYPE_SYS_BUS_DEVICE,
    240    .instance_size = sizeof(struct loongson_liointc),
    241    .instance_init = loongson_liointc_init,
    242};
    243
    244static void loongson_liointc_register_types(void)
    245{
    246    type_register_static(&loongson_liointc_info);
    247}
    248
    249type_init(loongson_liointc_register_types)