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

stellaris-gptm.c (9237B)


      1/*
      2 * Luminary Micro Stellaris General Purpose Timer Module
      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 "qemu/log.h"
     12#include "qemu/timer.h"
     13#include "qapi/error.h"
     14#include "migration/vmstate.h"
     15#include "hw/qdev-clock.h"
     16#include "hw/timer/stellaris-gptm.h"
     17
     18static void gptm_update_irq(gptm_state *s)
     19{
     20    int level;
     21    level = (s->state & s->mask) != 0;
     22    qemu_set_irq(s->irq, level);
     23}
     24
     25static void gptm_stop(gptm_state *s, int n)
     26{
     27    timer_del(s->timer[n]);
     28}
     29
     30static void gptm_reload(gptm_state *s, int n, int reset)
     31{
     32    int64_t tick;
     33    if (reset) {
     34        tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     35    } else {
     36        tick = s->tick[n];
     37    }
     38
     39    if (s->config == 0) {
     40        /* 32-bit CountDown.  */
     41        uint32_t count;
     42        count = s->load[0] | (s->load[1] << 16);
     43        tick += clock_ticks_to_ns(s->clk, count);
     44    } else if (s->config == 1) {
     45        /* 32-bit RTC.  1Hz tick.  */
     46        tick += NANOSECONDS_PER_SECOND;
     47    } else if (s->mode[n] == 0xa) {
     48        /* PWM mode.  Not implemented.  */
     49    } else {
     50        qemu_log_mask(LOG_UNIMP,
     51                      "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
     52                      s->mode[n]);
     53        return;
     54    }
     55    s->tick[n] = tick;
     56    timer_mod(s->timer[n], tick);
     57}
     58
     59static void gptm_tick(void *opaque)
     60{
     61    gptm_state **p = (gptm_state **)opaque;
     62    gptm_state *s;
     63    int n;
     64
     65    s = *p;
     66    n = p - s->opaque;
     67    if (s->config == 0) {
     68        s->state |= 1;
     69        if ((s->control & 0x20)) {
     70            /* Output trigger.  */
     71            qemu_irq_pulse(s->trigger);
     72        }
     73        if (s->mode[0] & 1) {
     74            /* One-shot.  */
     75            s->control &= ~1;
     76        } else {
     77            /* Periodic.  */
     78            gptm_reload(s, 0, 0);
     79        }
     80    } else if (s->config == 1) {
     81        /* RTC.  */
     82        uint32_t match;
     83        s->rtc++;
     84        match = s->match[0] | (s->match[1] << 16);
     85        if (s->rtc > match)
     86            s->rtc = 0;
     87        if (s->rtc == 0) {
     88            s->state |= 8;
     89        }
     90        gptm_reload(s, 0, 0);
     91    } else if (s->mode[n] == 0xa) {
     92        /* PWM mode.  Not implemented.  */
     93    } else {
     94        qemu_log_mask(LOG_UNIMP,
     95                      "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
     96                      s->mode[n]);
     97    }
     98    gptm_update_irq(s);
     99}
    100
    101static uint64_t gptm_read(void *opaque, hwaddr offset,
    102                          unsigned size)
    103{
    104    gptm_state *s = (gptm_state *)opaque;
    105
    106    switch (offset) {
    107    case 0x00: /* CFG */
    108        return s->config;
    109    case 0x04: /* TAMR */
    110        return s->mode[0];
    111    case 0x08: /* TBMR */
    112        return s->mode[1];
    113    case 0x0c: /* CTL */
    114        return s->control;
    115    case 0x18: /* IMR */
    116        return s->mask;
    117    case 0x1c: /* RIS */
    118        return s->state;
    119    case 0x20: /* MIS */
    120        return s->state & s->mask;
    121    case 0x24: /* CR */
    122        return 0;
    123    case 0x28: /* TAILR */
    124        return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
    125    case 0x2c: /* TBILR */
    126        return s->load[1];
    127    case 0x30: /* TAMARCHR */
    128        return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
    129    case 0x34: /* TBMATCHR */
    130        return s->match[1];
    131    case 0x38: /* TAPR */
    132        return s->prescale[0];
    133    case 0x3c: /* TBPR */
    134        return s->prescale[1];
    135    case 0x40: /* TAPMR */
    136        return s->match_prescale[0];
    137    case 0x44: /* TBPMR */
    138        return s->match_prescale[1];
    139    case 0x48: /* TAR */
    140        if (s->config == 1) {
    141            return s->rtc;
    142        }
    143        qemu_log_mask(LOG_UNIMP,
    144                      "GPTM: read of TAR but timer read not supported\n");
    145        return 0;
    146    case 0x4c: /* TBR */
    147        qemu_log_mask(LOG_UNIMP,
    148                      "GPTM: read of TBR but timer read not supported\n");
    149        return 0;
    150    default:
    151        qemu_log_mask(LOG_GUEST_ERROR,
    152                      "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
    153                      offset);
    154        return 0;
    155    }
    156}
    157
    158static void gptm_write(void *opaque, hwaddr offset,
    159                       uint64_t value, unsigned size)
    160{
    161    gptm_state *s = (gptm_state *)opaque;
    162    uint32_t oldval;
    163
    164    /*
    165     * The timers should be disabled before changing the configuration.
    166     * We take advantage of this and defer everything until the timer
    167     * is enabled.
    168     */
    169    switch (offset) {
    170    case 0x00: /* CFG */
    171        s->config = value;
    172        break;
    173    case 0x04: /* TAMR */
    174        s->mode[0] = value;
    175        break;
    176    case 0x08: /* TBMR */
    177        s->mode[1] = value;
    178        break;
    179    case 0x0c: /* CTL */
    180        oldval = s->control;
    181        s->control = value;
    182        /* TODO: Implement pause.  */
    183        if ((oldval ^ value) & 1) {
    184            if (value & 1) {
    185                gptm_reload(s, 0, 1);
    186            } else {
    187                gptm_stop(s, 0);
    188            }
    189        }
    190        if (((oldval ^ value) & 0x100) && s->config >= 4) {
    191            if (value & 0x100) {
    192                gptm_reload(s, 1, 1);
    193            } else {
    194                gptm_stop(s, 1);
    195            }
    196        }
    197        break;
    198    case 0x18: /* IMR */
    199        s->mask = value & 0x77;
    200        gptm_update_irq(s);
    201        break;
    202    case 0x24: /* CR */
    203        s->state &= ~value;
    204        break;
    205    case 0x28: /* TAILR */
    206        s->load[0] = value & 0xffff;
    207        if (s->config < 4) {
    208            s->load[1] = value >> 16;
    209        }
    210        break;
    211    case 0x2c: /* TBILR */
    212        s->load[1] = value & 0xffff;
    213        break;
    214    case 0x30: /* TAMARCHR */
    215        s->match[0] = value & 0xffff;
    216        if (s->config < 4) {
    217            s->match[1] = value >> 16;
    218        }
    219        break;
    220    case 0x34: /* TBMATCHR */
    221        s->match[1] = value >> 16;
    222        break;
    223    case 0x38: /* TAPR */
    224        s->prescale[0] = value;
    225        break;
    226    case 0x3c: /* TBPR */
    227        s->prescale[1] = value;
    228        break;
    229    case 0x40: /* TAPMR */
    230        s->match_prescale[0] = value;
    231        break;
    232    case 0x44: /* TBPMR */
    233        s->match_prescale[0] = value;
    234        break;
    235    default:
    236        qemu_log_mask(LOG_GUEST_ERROR,
    237                      "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
    238                      offset);
    239    }
    240    gptm_update_irq(s);
    241}
    242
    243static const MemoryRegionOps gptm_ops = {
    244    .read = gptm_read,
    245    .write = gptm_write,
    246    .endianness = DEVICE_NATIVE_ENDIAN,
    247};
    248
    249static const VMStateDescription vmstate_stellaris_gptm = {
    250    .name = "stellaris_gptm",
    251    .version_id = 2,
    252    .minimum_version_id = 2,
    253    .fields = (VMStateField[]) {
    254        VMSTATE_UINT32(config, gptm_state),
    255        VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
    256        VMSTATE_UINT32(control, gptm_state),
    257        VMSTATE_UINT32(state, gptm_state),
    258        VMSTATE_UINT32(mask, gptm_state),
    259        VMSTATE_UNUSED(8),
    260        VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
    261        VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
    262        VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
    263        VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
    264        VMSTATE_UINT32(rtc, gptm_state),
    265        VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
    266        VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
    267        VMSTATE_CLOCK(clk, gptm_state),
    268        VMSTATE_END_OF_LIST()
    269    }
    270};
    271
    272static void stellaris_gptm_init(Object *obj)
    273{
    274    DeviceState *dev = DEVICE(obj);
    275    gptm_state *s = STELLARIS_GPTM(obj);
    276    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    277
    278    sysbus_init_irq(sbd, &s->irq);
    279    qdev_init_gpio_out(dev, &s->trigger, 1);
    280
    281    memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
    282                          "gptm", 0x1000);
    283    sysbus_init_mmio(sbd, &s->iomem);
    284
    285    s->opaque[0] = s->opaque[1] = s;
    286
    287    /*
    288     * TODO: in an ideal world we would model the effects of changing
    289     * the input clock frequency while the countdown timer is active.
    290     * The best way to do this would be to convert the device to use
    291     * ptimer instead of hand-rolling its own timer. This would also
    292     * make it easy to implement reading the current count from the
    293     * TAR and TBR registers.
    294     */
    295    s->clk = qdev_init_clock_in(dev, "clk", NULL, NULL, 0);
    296}
    297
    298static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
    299{
    300    gptm_state *s = STELLARIS_GPTM(dev);
    301
    302    if (!clock_has_source(s->clk)) {
    303        error_setg(errp, "stellaris-gptm: clk must be connected");
    304        return;
    305    }
    306
    307    s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
    308    s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
    309}
    310
    311static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
    312{
    313    DeviceClass *dc = DEVICE_CLASS(klass);
    314
    315    dc->vmsd = &vmstate_stellaris_gptm;
    316    dc->realize = stellaris_gptm_realize;
    317}
    318
    319static const TypeInfo stellaris_gptm_info = {
    320    .name          = TYPE_STELLARIS_GPTM,
    321    .parent        = TYPE_SYS_BUS_DEVICE,
    322    .instance_size = sizeof(gptm_state),
    323    .instance_init = stellaris_gptm_init,
    324    .class_init    = stellaris_gptm_class_init,
    325};
    326
    327static void stellaris_gptm_register_types(void)
    328{
    329    type_register_static(&stellaris_gptm_info);
    330}
    331
    332type_init(stellaris_gptm_register_types)