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

pl190.c (8168B)


      1/*
      2 * Arm PrimeCell PL190 Vector Interrupt Controller
      3 *
      4 * Copyright (c) 2006 CodeSourcery.
      5 * Written by Paul Brook
      6 *
      7 * This code is licensed under the GPL.
      8 */
      9
     10#include "qemu/osdep.h"
     11#include "hw/irq.h"
     12#include "hw/sysbus.h"
     13#include "migration/vmstate.h"
     14#include "qemu/log.h"
     15#include "qemu/module.h"
     16#include "qom/object.h"
     17
     18/* The number of virtual priority levels.  16 user vectors plus the
     19   unvectored IRQ.  Chained interrupts would require an additional level
     20   if implemented.  */
     21
     22#define PL190_NUM_PRIO 17
     23
     24#define TYPE_PL190 "pl190"
     25OBJECT_DECLARE_SIMPLE_TYPE(PL190State, PL190)
     26
     27struct PL190State {
     28    SysBusDevice parent_obj;
     29
     30    MemoryRegion iomem;
     31    uint32_t level;
     32    uint32_t soft_level;
     33    uint32_t irq_enable;
     34    uint32_t fiq_select;
     35    uint8_t vect_control[16];
     36    uint32_t vect_addr[PL190_NUM_PRIO];
     37    /* Mask containing interrupts with higher priority than this one.  */
     38    uint32_t prio_mask[PL190_NUM_PRIO + 1];
     39    int protected;
     40    /* Current priority level.  */
     41    int priority;
     42    int prev_prio[PL190_NUM_PRIO];
     43    qemu_irq irq;
     44    qemu_irq fiq;
     45};
     46
     47static const unsigned char pl190_id[] =
     48{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
     49
     50static inline uint32_t pl190_irq_level(PL190State *s)
     51{
     52    return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
     53}
     54
     55/* Update interrupts.  */
     56static void pl190_update(PL190State *s)
     57{
     58    uint32_t level = pl190_irq_level(s);
     59    int set;
     60
     61    set = (level & s->prio_mask[s->priority]) != 0;
     62    qemu_set_irq(s->irq, set);
     63    set = ((s->level | s->soft_level) & s->fiq_select) != 0;
     64    qemu_set_irq(s->fiq, set);
     65}
     66
     67static void pl190_set_irq(void *opaque, int irq, int level)
     68{
     69    PL190State *s = (PL190State *)opaque;
     70
     71    if (level)
     72        s->level |= 1u << irq;
     73    else
     74        s->level &= ~(1u << irq);
     75    pl190_update(s);
     76}
     77
     78static void pl190_update_vectors(PL190State *s)
     79{
     80    uint32_t mask;
     81    int i;
     82    int n;
     83
     84    mask = 0;
     85    for (i = 0; i < 16; i++)
     86      {
     87        s->prio_mask[i] = mask;
     88        if (s->vect_control[i] & 0x20)
     89          {
     90            n = s->vect_control[i] & 0x1f;
     91            mask |= 1 << n;
     92          }
     93      }
     94    s->prio_mask[16] = mask;
     95    pl190_update(s);
     96}
     97
     98static uint64_t pl190_read(void *opaque, hwaddr offset,
     99                           unsigned size)
    100{
    101    PL190State *s = (PL190State *)opaque;
    102    int i;
    103
    104    if (offset >= 0xfe0 && offset < 0x1000) {
    105        return pl190_id[(offset - 0xfe0) >> 2];
    106    }
    107    if (offset >= 0x100 && offset < 0x140) {
    108        return s->vect_addr[(offset - 0x100) >> 2];
    109    }
    110    if (offset >= 0x200 && offset < 0x240) {
    111        return s->vect_control[(offset - 0x200) >> 2];
    112    }
    113    switch (offset >> 2) {
    114    case 0: /* IRQSTATUS */
    115        return pl190_irq_level(s);
    116    case 1: /* FIQSATUS */
    117        return (s->level | s->soft_level) & s->fiq_select;
    118    case 2: /* RAWINTR */
    119        return s->level | s->soft_level;
    120    case 3: /* INTSELECT */
    121        return s->fiq_select;
    122    case 4: /* INTENABLE */
    123        return s->irq_enable;
    124    case 6: /* SOFTINT */
    125        return s->soft_level;
    126    case 8: /* PROTECTION */
    127        return s->protected;
    128    case 12: /* VECTADDR */
    129        /* Read vector address at the start of an ISR.  Increases the
    130         * current priority level to that of the current interrupt.
    131         *
    132         * Since an enabled interrupt X at priority P causes prio_mask[Y]
    133         * to have bit X set for all Y > P, this loop will stop with
    134         * i == the priority of the highest priority set interrupt.
    135         */
    136        for (i = 0; i < s->priority; i++) {
    137            if ((s->level | s->soft_level) & s->prio_mask[i + 1]) {
    138                break;
    139            }
    140        }
    141
    142        /* Reading this value with no pending interrupts is undefined.
    143           We return the default address.  */
    144        if (i == PL190_NUM_PRIO)
    145          return s->vect_addr[16];
    146        if (i < s->priority)
    147          {
    148            s->prev_prio[i] = s->priority;
    149            s->priority = i;
    150            pl190_update(s);
    151          }
    152        return s->vect_addr[s->priority];
    153    case 13: /* DEFVECTADDR */
    154        return s->vect_addr[16];
    155    default:
    156        qemu_log_mask(LOG_GUEST_ERROR,
    157                      "pl190_read: Bad offset %x\n", (int)offset);
    158        return 0;
    159    }
    160}
    161
    162static void pl190_write(void *opaque, hwaddr offset,
    163                        uint64_t val, unsigned size)
    164{
    165    PL190State *s = (PL190State *)opaque;
    166
    167    if (offset >= 0x100 && offset < 0x140) {
    168        s->vect_addr[(offset - 0x100) >> 2] = val;
    169        pl190_update_vectors(s);
    170        return;
    171    }
    172    if (offset >= 0x200 && offset < 0x240) {
    173        s->vect_control[(offset - 0x200) >> 2] = val;
    174        pl190_update_vectors(s);
    175        return;
    176    }
    177    switch (offset >> 2) {
    178    case 0: /* SELECT */
    179        /* This is a readonly register, but linux tries to write to it
    180           anyway.  Ignore the write.  */
    181        break;
    182    case 3: /* INTSELECT */
    183        s->fiq_select = val;
    184        break;
    185    case 4: /* INTENABLE */
    186        s->irq_enable |= val;
    187        break;
    188    case 5: /* INTENCLEAR */
    189        s->irq_enable &= ~val;
    190        break;
    191    case 6: /* SOFTINT */
    192        s->soft_level |= val;
    193        break;
    194    case 7: /* SOFTINTCLEAR */
    195        s->soft_level &= ~val;
    196        break;
    197    case 8: /* PROTECTION */
    198        /* TODO: Protection (supervisor only access) is not implemented.  */
    199        s->protected = val & 1;
    200        break;
    201    case 12: /* VECTADDR */
    202        /* Restore the previous priority level.  The value written is
    203           ignored.  */
    204        if (s->priority < PL190_NUM_PRIO)
    205            s->priority = s->prev_prio[s->priority];
    206        break;
    207    case 13: /* DEFVECTADDR */
    208        s->vect_addr[16] = val;
    209        break;
    210    case 0xc0: /* ITCR */
    211        if (val) {
    212            qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n");
    213        }
    214        break;
    215    default:
    216        qemu_log_mask(LOG_GUEST_ERROR,
    217                     "pl190_write: Bad offset %x\n", (int)offset);
    218        return;
    219    }
    220    pl190_update(s);
    221}
    222
    223static const MemoryRegionOps pl190_ops = {
    224    .read = pl190_read,
    225    .write = pl190_write,
    226    .endianness = DEVICE_NATIVE_ENDIAN,
    227};
    228
    229static void pl190_reset(DeviceState *d)
    230{
    231    PL190State *s = PL190(d);
    232    int i;
    233
    234    for (i = 0; i < 16; i++) {
    235        s->vect_addr[i] = 0;
    236        s->vect_control[i] = 0;
    237    }
    238    s->vect_addr[16] = 0;
    239    s->prio_mask[17] = 0xffffffff;
    240    s->priority = PL190_NUM_PRIO;
    241    pl190_update_vectors(s);
    242}
    243
    244static void pl190_init(Object *obj)
    245{
    246    DeviceState *dev = DEVICE(obj);
    247    PL190State *s = PL190(obj);
    248    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    249
    250    memory_region_init_io(&s->iomem, obj, &pl190_ops, s, "pl190", 0x1000);
    251    sysbus_init_mmio(sbd, &s->iomem);
    252    qdev_init_gpio_in(dev, pl190_set_irq, 32);
    253    sysbus_init_irq(sbd, &s->irq);
    254    sysbus_init_irq(sbd, &s->fiq);
    255}
    256
    257static const VMStateDescription vmstate_pl190 = {
    258    .name = "pl190",
    259    .version_id = 1,
    260    .minimum_version_id = 1,
    261    .fields = (VMStateField[]) {
    262        VMSTATE_UINT32(level, PL190State),
    263        VMSTATE_UINT32(soft_level, PL190State),
    264        VMSTATE_UINT32(irq_enable, PL190State),
    265        VMSTATE_UINT32(fiq_select, PL190State),
    266        VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16),
    267        VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO),
    268        VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1),
    269        VMSTATE_INT32(protected, PL190State),
    270        VMSTATE_INT32(priority, PL190State),
    271        VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO),
    272        VMSTATE_END_OF_LIST()
    273    }
    274};
    275
    276static void pl190_class_init(ObjectClass *klass, void *data)
    277{
    278    DeviceClass *dc = DEVICE_CLASS(klass);
    279
    280    dc->reset = pl190_reset;
    281    dc->vmsd = &vmstate_pl190;
    282}
    283
    284static const TypeInfo pl190_info = {
    285    .name          = TYPE_PL190,
    286    .parent        = TYPE_SYS_BUS_DEVICE,
    287    .instance_size = sizeof(PL190State),
    288    .instance_init = pl190_init,
    289    .class_init    = pl190_class_init,
    290};
    291
    292static void pl190_register_types(void)
    293{
    294    type_register_static(&pl190_info);
    295}
    296
    297type_init(pl190_register_types)