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

armv7m_systick.c (9375B)


      1/*
      2 * ARMv7M SysTick timer
      3 *
      4 * Copyright (c) 2006-2007 CodeSourcery.
      5 * Written by Paul Brook
      6 * Copyright (c) 2017 Linaro Ltd
      7 * Written by Peter Maydell
      8 *
      9 * This code is licensed under the GPL (version 2 or later).
     10 */
     11
     12#include "qemu/osdep.h"
     13#include "hw/timer/armv7m_systick.h"
     14#include "migration/vmstate.h"
     15#include "hw/irq.h"
     16#include "hw/sysbus.h"
     17#include "hw/qdev-clock.h"
     18#include "qemu/timer.h"
     19#include "qemu/log.h"
     20#include "qemu/module.h"
     21#include "qapi/error.h"
     22#include "trace.h"
     23
     24#define SYSTICK_ENABLE    (1 << 0)
     25#define SYSTICK_TICKINT   (1 << 1)
     26#define SYSTICK_CLKSOURCE (1 << 2)
     27#define SYSTICK_COUNTFLAG (1 << 16)
     28
     29#define SYSCALIB_NOREF (1U << 31)
     30#define SYSCALIB_SKEW (1U << 30)
     31#define SYSCALIB_TENMS ((1U << 24) - 1)
     32
     33static void systick_set_period_from_clock(SysTickState *s)
     34{
     35    /*
     36     * Set the ptimer period from whichever clock is selected.
     37     * Must be called from within a ptimer transaction block.
     38     */
     39    if (s->control & SYSTICK_CLKSOURCE) {
     40        ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
     41    } else {
     42        ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
     43    }
     44}
     45
     46static void systick_timer_tick(void *opaque)
     47{
     48    SysTickState *s = (SysTickState *)opaque;
     49
     50    trace_systick_timer_tick();
     51
     52    s->control |= SYSTICK_COUNTFLAG;
     53    if (s->control & SYSTICK_TICKINT) {
     54        /* Tell the NVIC to pend the SysTick exception */
     55        qemu_irq_pulse(s->irq);
     56    }
     57    if (ptimer_get_limit(s->ptimer) == 0) {
     58        /*
     59         * Timer expiry with SYST_RVR zero disables the timer
     60         * (but doesn't clear SYST_CSR.ENABLE)
     61         */
     62        ptimer_stop(s->ptimer);
     63    }
     64}
     65
     66static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
     67                                unsigned size, MemTxAttrs attrs)
     68{
     69    SysTickState *s = opaque;
     70    uint32_t val;
     71
     72    if (attrs.user) {
     73        /* Generate BusFault for unprivileged accesses */
     74        return MEMTX_ERROR;
     75    }
     76
     77    switch (addr) {
     78    case 0x0: /* SysTick Control and Status.  */
     79        val = s->control;
     80        s->control &= ~SYSTICK_COUNTFLAG;
     81        break;
     82    case 0x4: /* SysTick Reload Value.  */
     83        val = ptimer_get_limit(s->ptimer);
     84        break;
     85    case 0x8: /* SysTick Current Value.  */
     86        val = ptimer_get_count(s->ptimer);
     87        break;
     88    case 0xc: /* SysTick Calibration Value.  */
     89        /*
     90         * In real hardware it is possible to make this register report
     91         * a different value from what the reference clock is actually
     92         * running at. We don't model that (which usually happens due
     93         * to integration errors in the real hardware) and instead always
     94         * report the theoretical correct value as described in the
     95         * knowledgebase article at
     96         * https://developer.arm.com/documentation/ka001325/latest
     97         * If necessary, we could implement an extra QOM property on this
     98         * device to force the STCALIB value to something different from
     99         * the "correct" value.
    100         */
    101        if (!clock_has_source(s->refclk)) {
    102            val = SYSCALIB_NOREF;
    103            break;
    104        }
    105        val = clock_ns_to_ticks(s->refclk, 10 * SCALE_MS) - 1;
    106        val &= SYSCALIB_TENMS;
    107        if (clock_ticks_to_ns(s->refclk, val + 1) != 10 * SCALE_MS) {
    108            /* report that tick count does not yield exactly 10ms */
    109            val |= SYSCALIB_SKEW;
    110        }
    111        break;
    112    default:
    113        val = 0;
    114        qemu_log_mask(LOG_GUEST_ERROR,
    115                      "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
    116        break;
    117    }
    118
    119    trace_systick_read(addr, val, size);
    120    *data = val;
    121    return MEMTX_OK;
    122}
    123
    124static MemTxResult systick_write(void *opaque, hwaddr addr,
    125                                 uint64_t value, unsigned size,
    126                                 MemTxAttrs attrs)
    127{
    128    SysTickState *s = opaque;
    129
    130    if (attrs.user) {
    131        /* Generate BusFault for unprivileged accesses */
    132        return MEMTX_ERROR;
    133    }
    134
    135    trace_systick_write(addr, value, size);
    136
    137    switch (addr) {
    138    case 0x0: /* SysTick Control and Status.  */
    139    {
    140        uint32_t oldval;
    141
    142        if (!clock_has_source(s->refclk)) {
    143            /* This bit is always 1 if there is no external refclk */
    144            value |= SYSTICK_CLKSOURCE;
    145        }
    146
    147        ptimer_transaction_begin(s->ptimer);
    148        oldval = s->control;
    149        s->control &= 0xfffffff8;
    150        s->control |= value & 7;
    151
    152        if ((oldval ^ value) & SYSTICK_ENABLE) {
    153            if (value & SYSTICK_ENABLE) {
    154                ptimer_run(s->ptimer, 0);
    155            } else {
    156                ptimer_stop(s->ptimer);
    157            }
    158        }
    159
    160        if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
    161            systick_set_period_from_clock(s);
    162        }
    163        ptimer_transaction_commit(s->ptimer);
    164        break;
    165    }
    166    case 0x4: /* SysTick Reload Value.  */
    167        ptimer_transaction_begin(s->ptimer);
    168        ptimer_set_limit(s->ptimer, value & 0xffffff, 0);
    169        ptimer_transaction_commit(s->ptimer);
    170        break;
    171    case 0x8: /* SysTick Current Value. */
    172        /*
    173         * Writing any value clears SYST_CVR to zero and clears
    174         * SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR
    175         * on the next clock edge unless SYST_RVR is zero.
    176         */
    177        ptimer_transaction_begin(s->ptimer);
    178        if (ptimer_get_limit(s->ptimer) == 0) {
    179            ptimer_stop(s->ptimer);
    180        }
    181        ptimer_set_count(s->ptimer, 0);
    182        s->control &= ~SYSTICK_COUNTFLAG;
    183        ptimer_transaction_commit(s->ptimer);
    184        break;
    185    default:
    186        qemu_log_mask(LOG_GUEST_ERROR,
    187                      "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
    188    }
    189    return MEMTX_OK;
    190}
    191
    192static const MemoryRegionOps systick_ops = {
    193    .read_with_attrs = systick_read,
    194    .write_with_attrs = systick_write,
    195    .endianness = DEVICE_NATIVE_ENDIAN,
    196    .valid.min_access_size = 4,
    197    .valid.max_access_size = 4,
    198};
    199
    200static void systick_reset(DeviceState *dev)
    201{
    202    SysTickState *s = SYSTICK(dev);
    203
    204    ptimer_transaction_begin(s->ptimer);
    205    s->control = 0;
    206    if (!clock_has_source(s->refclk)) {
    207        /* This bit is always 1 if there is no external refclk */
    208        s->control |= SYSTICK_CLKSOURCE;
    209    }
    210    ptimer_stop(s->ptimer);
    211    ptimer_set_count(s->ptimer, 0);
    212    ptimer_set_limit(s->ptimer, 0, 0);
    213    systick_set_period_from_clock(s);
    214    ptimer_transaction_commit(s->ptimer);
    215}
    216
    217static void systick_cpuclk_update(void *opaque, ClockEvent event)
    218{
    219    SysTickState *s = SYSTICK(opaque);
    220
    221    if (!(s->control & SYSTICK_CLKSOURCE)) {
    222        /* currently using refclk, we can ignore cpuclk changes */
    223    }
    224
    225    ptimer_transaction_begin(s->ptimer);
    226    ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
    227    ptimer_transaction_commit(s->ptimer);
    228}
    229
    230static void systick_refclk_update(void *opaque, ClockEvent event)
    231{
    232    SysTickState *s = SYSTICK(opaque);
    233
    234    if (s->control & SYSTICK_CLKSOURCE) {
    235        /* currently using cpuclk, we can ignore refclk changes */
    236    }
    237
    238    ptimer_transaction_begin(s->ptimer);
    239    ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
    240    ptimer_transaction_commit(s->ptimer);
    241}
    242
    243static void systick_instance_init(Object *obj)
    244{
    245    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    246    SysTickState *s = SYSTICK(obj);
    247
    248    memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
    249    sysbus_init_mmio(sbd, &s->iomem);
    250    sysbus_init_irq(sbd, &s->irq);
    251
    252    s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
    253                                   systick_refclk_update, s, ClockUpdate);
    254    s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk",
    255                                   systick_cpuclk_update, s, ClockUpdate);
    256}
    257
    258static void systick_realize(DeviceState *dev, Error **errp)
    259{
    260    SysTickState *s = SYSTICK(dev);
    261    s->ptimer = ptimer_init(systick_timer_tick, s,
    262                            PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
    263                            PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
    264                            PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
    265                            PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
    266
    267    if (!clock_has_source(s->cpuclk)) {
    268        error_setg(errp, "systick: cpuclk must be connected");
    269        return;
    270    }
    271    /* It's OK not to connect the refclk */
    272}
    273
    274static const VMStateDescription vmstate_systick = {
    275    .name = "armv7m_systick",
    276    .version_id = 3,
    277    .minimum_version_id = 3,
    278    .fields = (VMStateField[]) {
    279        VMSTATE_CLOCK(refclk, SysTickState),
    280        VMSTATE_CLOCK(cpuclk, SysTickState),
    281        VMSTATE_UINT32(control, SysTickState),
    282        VMSTATE_INT64(tick, SysTickState),
    283        VMSTATE_PTIMER(ptimer, SysTickState),
    284        VMSTATE_END_OF_LIST()
    285    }
    286};
    287
    288static void systick_class_init(ObjectClass *klass, void *data)
    289{
    290    DeviceClass *dc = DEVICE_CLASS(klass);
    291
    292    dc->vmsd = &vmstate_systick;
    293    dc->reset = systick_reset;
    294    dc->realize = systick_realize;
    295}
    296
    297static const TypeInfo armv7m_systick_info = {
    298    .name = TYPE_SYSTICK,
    299    .parent = TYPE_SYS_BUS_DEVICE,
    300    .instance_init = systick_instance_init,
    301    .instance_size = sizeof(SysTickState),
    302    .class_init = systick_class_init,
    303};
    304
    305static void armv7m_systick_register_types(void)
    306{
    307    type_register_static(&armv7m_systick_info);
    308}
    309
    310type_init(armv7m_systick_register_types)