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

exynos4210_combiner.c (15622B)


      1/*
      2 * Samsung exynos4210 Interrupt Combiner
      3 *
      4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
      5 * All rights reserved.
      6 *
      7 * Evgeny Voevodin <e.voevodin@samsung.com>
      8 *
      9 * This program is free software; you can redistribute it and/or modify it
     10 * under the terms of the GNU General Public License as published by the
     11 * Free Software Foundation; either version 2 of the License, or (at your
     12 * option) any later version.
     13 *
     14 * This program is distributed in the hope that it will be useful,
     15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     17 * See the GNU General Public License for more details.
     18 *
     19 * You should have received a copy of the GNU General Public License along
     20 * with this program; if not, see <http://www.gnu.org/licenses/>.
     21 */
     22
     23/*
     24 * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
     25 * IRQ sources into groups and provides signal output to GIC from each group. It
     26 * is driven by common mask and enable/disable logic. Take a note that not all
     27 * IRQs are passed to GIC through Combiner.
     28 */
     29
     30#include "qemu/osdep.h"
     31#include "hw/sysbus.h"
     32#include "migration/vmstate.h"
     33#include "qemu/module.h"
     34
     35#include "hw/arm/exynos4210.h"
     36#include "hw/hw.h"
     37#include "hw/irq.h"
     38#include "hw/qdev-properties.h"
     39#include "qom/object.h"
     40
     41//#define DEBUG_COMBINER
     42
     43#ifdef DEBUG_COMBINER
     44#define DPRINTF(fmt, ...) \
     45        do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
     46                ## __VA_ARGS__); } while (0)
     47#else
     48#define DPRINTF(fmt, ...) do {} while (0)
     49#endif
     50
     51#define    IIC_NGRP        64            /* Internal Interrupt Combiner
     52                                            Groups number */
     53#define    IIC_NIRQ        (IIC_NGRP * 8)/* Internal Interrupt Combiner
     54                                            Interrupts number */
     55#define IIC_REGION_SIZE    0x108         /* Size of memory mapped region */
     56#define IIC_REGSET_SIZE    0x41
     57
     58/*
     59 * State for each output signal of internal combiner
     60 */
     61typedef struct CombinerGroupState {
     62    uint8_t src_mask;            /* 1 - source enabled, 0 - disabled */
     63    uint8_t src_pending;        /* Pending source interrupts before masking */
     64} CombinerGroupState;
     65
     66#define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner"
     67OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210CombinerState, EXYNOS4210_COMBINER)
     68
     69struct Exynos4210CombinerState {
     70    SysBusDevice parent_obj;
     71
     72    MemoryRegion iomem;
     73
     74    struct CombinerGroupState group[IIC_NGRP];
     75    uint32_t reg_set[IIC_REGSET_SIZE];
     76    uint32_t icipsr[2];
     77    uint32_t external;          /* 1 means that this combiner is external */
     78
     79    qemu_irq output_irq[IIC_NGRP];
     80};
     81
     82static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
     83    .name = "exynos4210.combiner.groupstate",
     84    .version_id = 1,
     85    .minimum_version_id = 1,
     86    .fields = (VMStateField[]) {
     87        VMSTATE_UINT8(src_mask, CombinerGroupState),
     88        VMSTATE_UINT8(src_pending, CombinerGroupState),
     89        VMSTATE_END_OF_LIST()
     90    }
     91};
     92
     93static const VMStateDescription vmstate_exynos4210_combiner = {
     94    .name = "exynos4210.combiner",
     95    .version_id = 1,
     96    .minimum_version_id = 1,
     97    .fields = (VMStateField[]) {
     98        VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
     99                vmstate_exynos4210_combiner_group_state, CombinerGroupState),
    100        VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
    101                IIC_REGSET_SIZE),
    102        VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
    103        VMSTATE_UINT32(external, Exynos4210CombinerState),
    104        VMSTATE_END_OF_LIST()
    105    }
    106};
    107
    108/*
    109 * Get Combiner input GPIO into irqs structure
    110 */
    111void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
    112        int ext)
    113{
    114    int n;
    115    int bit;
    116    int max;
    117    qemu_irq *irq;
    118
    119    max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ :
    120        EXYNOS4210_MAX_INT_COMBINER_IN_IRQ;
    121    irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq;
    122
    123    /*
    124     * Some IRQs of Int/External Combiner are going to two Combiners groups,
    125     * so let split them.
    126     */
    127    for (n = 0; n < max; n++) {
    128
    129        bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
    130
    131        switch (n) {
    132        /* MDNIE_LCD1 INTG1 */
    133        case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ...
    134             EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3):
    135            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    136                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]);
    137            continue;
    138
    139        /* TMU INTG3 */
    140        case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4):
    141            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    142                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]);
    143            continue;
    144
    145        /* LCD1 INTG12 */
    146        case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ...
    147             EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3):
    148            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    149                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]);
    150            continue;
    151
    152        /* Multi-Core Timer INTG12 */
    153        case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ...
    154             EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8):
    155               irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    156                       irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
    157            continue;
    158
    159        /* Multi-Core Timer INTG35 */
    160        case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ...
    161             EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8):
    162            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    163                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
    164            continue;
    165
    166        /* Multi-Core Timer INTG51 */
    167        case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ...
    168             EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8):
    169            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    170                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
    171            continue;
    172
    173        /* Multi-Core Timer INTG53 */
    174        case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ...
    175             EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8):
    176            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
    177                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
    178            continue;
    179        }
    180
    181        irq[n] = qdev_get_gpio_in(dev, n);
    182    }
    183}
    184
    185static uint64_t
    186exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size)
    187{
    188    struct Exynos4210CombinerState *s =
    189            (struct Exynos4210CombinerState *)opaque;
    190    uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
    191                                   get a start of corresponding group quad */
    192    uint32_t grp_quad_base_n;    /* Base of group quad */
    193    uint32_t reg_n;              /* Register number inside the quad */
    194    uint32_t val;
    195
    196    req_quad_base_n = offset >> 4;
    197    grp_quad_base_n = req_quad_base_n << 2;
    198    reg_n = (offset - (req_quad_base_n << 4)) >> 2;
    199
    200    if (req_quad_base_n >= IIC_NGRP) {
    201        /* Read of ICIPSR register */
    202        return s->icipsr[reg_n];
    203    }
    204
    205    val = 0;
    206
    207    switch (reg_n) {
    208    /* IISTR */
    209    case 2:
    210        val |= s->group[grp_quad_base_n].src_pending;
    211        val |= s->group[grp_quad_base_n + 1].src_pending << 8;
    212        val |= s->group[grp_quad_base_n + 2].src_pending << 16;
    213        val |= s->group[grp_quad_base_n + 3].src_pending << 24;
    214        break;
    215    /* IIMSR */
    216    case 3:
    217        val |= s->group[grp_quad_base_n].src_mask &
    218        s->group[grp_quad_base_n].src_pending;
    219        val |= (s->group[grp_quad_base_n + 1].src_mask &
    220                s->group[grp_quad_base_n + 1].src_pending) << 8;
    221        val |= (s->group[grp_quad_base_n + 2].src_mask &
    222                s->group[grp_quad_base_n + 2].src_pending) << 16;
    223        val |= (s->group[grp_quad_base_n + 3].src_mask &
    224                s->group[grp_quad_base_n + 3].src_pending) << 24;
    225        break;
    226    default:
    227        if (offset >> 2 >= IIC_REGSET_SIZE) {
    228            hw_error("exynos4210.combiner: overflow of reg_set by 0x"
    229                    TARGET_FMT_plx "offset\n", offset);
    230        }
    231        val = s->reg_set[offset >> 2];
    232    }
    233    return val;
    234}
    235
    236static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
    237{
    238    struct Exynos4210CombinerState *s =
    239            (struct Exynos4210CombinerState *)opaque;
    240
    241    /* Send interrupt if needed */
    242    if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
    243#ifdef DEBUG_COMBINER
    244        if (group_n != 26) {
    245            /* skip uart */
    246            DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
    247        }
    248#endif
    249
    250        /* Set Combiner interrupt pending status after masking */
    251        if (group_n >= 32) {
    252            s->icipsr[1] |= 1 << (group_n - 32);
    253        } else {
    254            s->icipsr[0] |= 1 << group_n;
    255        }
    256
    257        qemu_irq_raise(s->output_irq[group_n]);
    258    } else {
    259#ifdef DEBUG_COMBINER
    260        if (group_n != 26) {
    261            /* skip uart */
    262            DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
    263        }
    264#endif
    265
    266        /* Set Combiner interrupt pending status after masking */
    267        if (group_n >= 32) {
    268            s->icipsr[1] &= ~(1 << (group_n - 32));
    269        } else {
    270            s->icipsr[0] &= ~(1 << group_n);
    271        }
    272
    273        qemu_irq_lower(s->output_irq[group_n]);
    274    }
    275}
    276
    277static void exynos4210_combiner_write(void *opaque, hwaddr offset,
    278        uint64_t val, unsigned size)
    279{
    280    struct Exynos4210CombinerState *s =
    281            (struct Exynos4210CombinerState *)opaque;
    282    uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
    283                                   get a start of corresponding group quad */
    284    uint32_t grp_quad_base_n;    /* Base of group quad */
    285    uint32_t reg_n;              /* Register number inside the quad */
    286
    287    req_quad_base_n = offset >> 4;
    288    grp_quad_base_n = req_quad_base_n << 2;
    289    reg_n = (offset - (req_quad_base_n << 4)) >> 2;
    290
    291    if (req_quad_base_n >= IIC_NGRP) {
    292        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
    293                TARGET_FMT_plx "\n", offset);
    294        return;
    295    }
    296
    297    if (reg_n > 1) {
    298        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
    299                TARGET_FMT_plx "\n", offset);
    300        return;
    301    }
    302
    303    if (offset >> 2 >= IIC_REGSET_SIZE) {
    304        hw_error("exynos4210.combiner: overflow of reg_set by 0x"
    305                TARGET_FMT_plx "offset\n", offset);
    306    }
    307    s->reg_set[offset >> 2] = val;
    308
    309    switch (reg_n) {
    310    /* IIESR */
    311    case 0:
    312        /* FIXME: what if irq is pending, allowed by mask, and we allow it
    313         * again. Interrupt will rise again! */
    314
    315        DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
    316                s->external ? "EXT" : "INT",
    317                grp_quad_base_n,
    318                grp_quad_base_n + 1,
    319                grp_quad_base_n + 2,
    320                grp_quad_base_n + 3);
    321
    322        /* Enable interrupt sources */
    323        s->group[grp_quad_base_n].src_mask |= val & 0xFF;
    324        s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
    325        s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
    326        s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
    327
    328        exynos4210_combiner_update(s, grp_quad_base_n);
    329        exynos4210_combiner_update(s, grp_quad_base_n + 1);
    330        exynos4210_combiner_update(s, grp_quad_base_n + 2);
    331        exynos4210_combiner_update(s, grp_quad_base_n + 3);
    332        break;
    333        /* IIECR */
    334    case 1:
    335        DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
    336                s->external ? "EXT" : "INT",
    337                grp_quad_base_n,
    338                grp_quad_base_n + 1,
    339                grp_quad_base_n + 2,
    340                grp_quad_base_n + 3);
    341
    342        /* Disable interrupt sources */
    343        s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
    344        s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
    345        s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
    346        s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
    347
    348        exynos4210_combiner_update(s, grp_quad_base_n);
    349        exynos4210_combiner_update(s, grp_quad_base_n + 1);
    350        exynos4210_combiner_update(s, grp_quad_base_n + 2);
    351        exynos4210_combiner_update(s, grp_quad_base_n + 3);
    352        break;
    353    default:
    354        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
    355                TARGET_FMT_plx "\n", offset);
    356        break;
    357    }
    358}
    359
    360/* Get combiner group and bit from irq number */
    361static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
    362{
    363    *bit = irq - ((irq >> 3) << 3);
    364    return irq >> 3;
    365}
    366
    367/* Process a change in an external IRQ input.  */
    368static void exynos4210_combiner_handler(void *opaque, int irq, int level)
    369{
    370    struct Exynos4210CombinerState *s =
    371            (struct Exynos4210CombinerState *)opaque;
    372    uint8_t bit_n, group_n;
    373
    374    group_n = get_combiner_group_and_bit(irq, &bit_n);
    375
    376    if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
    377        DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
    378                , group_n);
    379        return;
    380    }
    381
    382    if (level) {
    383        s->group[group_n].src_pending |= 1 << bit_n;
    384    } else {
    385        s->group[group_n].src_pending &= ~(1 << bit_n);
    386    }
    387
    388    exynos4210_combiner_update(s, group_n);
    389}
    390
    391static void exynos4210_combiner_reset(DeviceState *d)
    392{
    393    struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
    394
    395    memset(&s->group, 0, sizeof(s->group));
    396    memset(&s->reg_set, 0, sizeof(s->reg_set));
    397
    398    s->reg_set[0xC0 >> 2] = 0x01010101;
    399    s->reg_set[0xC4 >> 2] = 0x01010101;
    400    s->reg_set[0xD0 >> 2] = 0x01010101;
    401    s->reg_set[0xD4 >> 2] = 0x01010101;
    402}
    403
    404static const MemoryRegionOps exynos4210_combiner_ops = {
    405    .read = exynos4210_combiner_read,
    406    .write = exynos4210_combiner_write,
    407    .endianness = DEVICE_NATIVE_ENDIAN,
    408};
    409
    410/*
    411 * Internal Combiner initialization.
    412 */
    413static void exynos4210_combiner_init(Object *obj)
    414{
    415    DeviceState *dev = DEVICE(obj);
    416    Exynos4210CombinerState *s = EXYNOS4210_COMBINER(obj);
    417    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    418    unsigned int i;
    419
    420    /* Allocate general purpose input signals and connect a handler to each of
    421     * them */
    422    qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ);
    423
    424    /* Connect SysBusDev irqs to device specific irqs */
    425    for (i = 0; i < IIC_NGRP; i++) {
    426        sysbus_init_irq(sbd, &s->output_irq[i]);
    427    }
    428
    429    memory_region_init_io(&s->iomem, obj, &exynos4210_combiner_ops, s,
    430                          "exynos4210-combiner", IIC_REGION_SIZE);
    431    sysbus_init_mmio(sbd, &s->iomem);
    432}
    433
    434static Property exynos4210_combiner_properties[] = {
    435    DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
    436    DEFINE_PROP_END_OF_LIST(),
    437};
    438
    439static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
    440{
    441    DeviceClass *dc = DEVICE_CLASS(klass);
    442
    443    dc->reset = exynos4210_combiner_reset;
    444    device_class_set_props(dc, exynos4210_combiner_properties);
    445    dc->vmsd = &vmstate_exynos4210_combiner;
    446}
    447
    448static const TypeInfo exynos4210_combiner_info = {
    449    .name          = TYPE_EXYNOS4210_COMBINER,
    450    .parent        = TYPE_SYS_BUS_DEVICE,
    451    .instance_size = sizeof(Exynos4210CombinerState),
    452    .instance_init = exynos4210_combiner_init,
    453    .class_init    = exynos4210_combiner_class_init,
    454};
    455
    456static void exynos4210_combiner_register_types(void)
    457{
    458    type_register_static(&exynos4210_combiner_info);
    459}
    460
    461type_init(exynos4210_combiner_register_types)