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

cmsdk-apb-timer.c (8699B)


      1/*
      2 * ARM CMSDK APB timer emulation
      3 *
      4 * Copyright (c) 2017 Linaro Limited
      5 * Written by Peter Maydell
      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 version 2 or
      9 *  (at your option) any later version.
     10 */
     11
     12/* This is a model of the "APB timer" which is part of the Cortex-M
     13 * System Design Kit (CMSDK) and documented in the Cortex-M System
     14 * Design Kit Technical Reference Manual (ARM DDI0479C):
     15 * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
     16 *
     17 * The hardware has an EXTIN input wire, which can be configured
     18 * by the guest to act either as a 'timer enable' (timer does not run
     19 * when EXTIN is low), or as a 'timer clock' (timer runs at frequency
     20 * of EXTIN clock, not PCLK frequency). We don't model this.
     21 *
     22 * The documentation is not very clear about the exact behaviour;
     23 * we choose to implement that the interrupt is triggered when
     24 * the counter goes from 1 to 0, that the counter then holds at 0
     25 * for one clock cycle before reloading from the RELOAD register,
     26 * and that if the RELOAD register is 0 this does not cause an
     27 * interrupt (as there is no further 1->0 transition).
     28 */
     29
     30#include "qemu/osdep.h"
     31#include "qemu/log.h"
     32#include "qemu/module.h"
     33#include "qapi/error.h"
     34#include "trace.h"
     35#include "hw/sysbus.h"
     36#include "hw/irq.h"
     37#include "hw/registerfields.h"
     38#include "hw/qdev-clock.h"
     39#include "hw/timer/cmsdk-apb-timer.h"
     40#include "migration/vmstate.h"
     41
     42REG32(CTRL, 0)
     43    FIELD(CTRL, EN, 0, 1)
     44    FIELD(CTRL, SELEXTEN, 1, 1)
     45    FIELD(CTRL, SELEXTCLK, 2, 1)
     46    FIELD(CTRL, IRQEN, 3, 1)
     47REG32(VALUE, 4)
     48REG32(RELOAD, 8)
     49REG32(INTSTATUS, 0xc)
     50    FIELD(INTSTATUS, IRQ, 0, 1)
     51REG32(PID4, 0xFD0)
     52REG32(PID5, 0xFD4)
     53REG32(PID6, 0xFD8)
     54REG32(PID7, 0xFDC)
     55REG32(PID0, 0xFE0)
     56REG32(PID1, 0xFE4)
     57REG32(PID2, 0xFE8)
     58REG32(PID3, 0xFEC)
     59REG32(CID0, 0xFF0)
     60REG32(CID1, 0xFF4)
     61REG32(CID2, 0xFF8)
     62REG32(CID3, 0xFFC)
     63
     64/* PID/CID values */
     65static const int timer_id[] = {
     66    0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
     67    0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
     68    0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
     69};
     70
     71static void cmsdk_apb_timer_update(CMSDKAPBTimer *s)
     72{
     73    qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
     74}
     75
     76static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
     77{
     78    CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
     79    uint64_t r;
     80
     81    switch (offset) {
     82    case A_CTRL:
     83        r = s->ctrl;
     84        break;
     85    case A_VALUE:
     86        r = ptimer_get_count(s->timer);
     87        break;
     88    case A_RELOAD:
     89        r = ptimer_get_limit(s->timer);
     90        break;
     91    case A_INTSTATUS:
     92        r = s->intstatus;
     93        break;
     94    case A_PID4 ... A_CID3:
     95        r = timer_id[(offset - A_PID4) / 4];
     96        break;
     97    default:
     98        qemu_log_mask(LOG_GUEST_ERROR,
     99                      "CMSDK APB timer read: bad offset %x\n", (int) offset);
    100        r = 0;
    101        break;
    102    }
    103    trace_cmsdk_apb_timer_read(offset, r, size);
    104    return r;
    105}
    106
    107static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
    108                                  unsigned size)
    109{
    110    CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
    111
    112    trace_cmsdk_apb_timer_write(offset, value, size);
    113
    114    switch (offset) {
    115    case A_CTRL:
    116        if (value & 6) {
    117            /* Bits [1] and [2] enable using EXTIN as either clock or
    118             * an enable line. We don't model this.
    119             */
    120            qemu_log_mask(LOG_UNIMP,
    121                          "CMSDK APB timer: EXTIN input not supported\n");
    122        }
    123        s->ctrl = value & 0xf;
    124        ptimer_transaction_begin(s->timer);
    125        if (s->ctrl & R_CTRL_EN_MASK) {
    126            ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
    127        } else {
    128            ptimer_stop(s->timer);
    129        }
    130        ptimer_transaction_commit(s->timer);
    131        break;
    132    case A_RELOAD:
    133        /* Writing to reload also sets the current timer value */
    134        ptimer_transaction_begin(s->timer);
    135        if (!value) {
    136            ptimer_stop(s->timer);
    137        }
    138        ptimer_set_limit(s->timer, value, 1);
    139        if (value && (s->ctrl & R_CTRL_EN_MASK)) {
    140            /*
    141             * Make sure timer is running (it might have stopped if this
    142             * was an expired one-shot timer)
    143             */
    144            ptimer_run(s->timer, 0);
    145        }
    146        ptimer_transaction_commit(s->timer);
    147        break;
    148    case A_VALUE:
    149        ptimer_transaction_begin(s->timer);
    150        if (!value && !ptimer_get_limit(s->timer)) {
    151            ptimer_stop(s->timer);
    152        }
    153        ptimer_set_count(s->timer, value);
    154        if (value && (s->ctrl & R_CTRL_EN_MASK)) {
    155            ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
    156        }
    157        ptimer_transaction_commit(s->timer);
    158        break;
    159    case A_INTSTATUS:
    160        /* Just one bit, which is W1C. */
    161        value &= 1;
    162        s->intstatus &= ~value;
    163        cmsdk_apb_timer_update(s);
    164        break;
    165    case A_PID4 ... A_CID3:
    166        qemu_log_mask(LOG_GUEST_ERROR,
    167                      "CMSDK APB timer write: write to RO offset 0x%x\n",
    168                      (int)offset);
    169        break;
    170    default:
    171        qemu_log_mask(LOG_GUEST_ERROR,
    172                      "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
    173        break;
    174    }
    175}
    176
    177static const MemoryRegionOps cmsdk_apb_timer_ops = {
    178    .read = cmsdk_apb_timer_read,
    179    .write = cmsdk_apb_timer_write,
    180    .endianness = DEVICE_LITTLE_ENDIAN,
    181};
    182
    183static void cmsdk_apb_timer_tick(void *opaque)
    184{
    185    CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
    186
    187    if (s->ctrl & R_CTRL_IRQEN_MASK) {
    188        s->intstatus |= R_INTSTATUS_IRQ_MASK;
    189        cmsdk_apb_timer_update(s);
    190    }
    191}
    192
    193static void cmsdk_apb_timer_reset(DeviceState *dev)
    194{
    195    CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
    196
    197    trace_cmsdk_apb_timer_reset();
    198    s->ctrl = 0;
    199    s->intstatus = 0;
    200    ptimer_transaction_begin(s->timer);
    201    ptimer_stop(s->timer);
    202    /* Set the limit and the count */
    203    ptimer_set_limit(s->timer, 0, 1);
    204    ptimer_transaction_commit(s->timer);
    205}
    206
    207static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
    208{
    209    CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
    210
    211    ptimer_transaction_begin(s->timer);
    212    ptimer_set_period_from_clock(s->timer, s->pclk, 1);
    213    ptimer_transaction_commit(s->timer);
    214}
    215
    216static void cmsdk_apb_timer_init(Object *obj)
    217{
    218    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    219    CMSDKAPBTimer *s = CMSDK_APB_TIMER(obj);
    220
    221    memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
    222                          s, "cmsdk-apb-timer", 0x1000);
    223    sysbus_init_mmio(sbd, &s->iomem);
    224    sysbus_init_irq(sbd, &s->timerint);
    225    s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
    226                                 cmsdk_apb_timer_clk_update, s, ClockUpdate);
    227}
    228
    229static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
    230{
    231    CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
    232
    233    if (!clock_has_source(s->pclk)) {
    234        error_setg(errp, "CMSDK APB timer: pclk clock must be connected");
    235        return;
    236    }
    237
    238    s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
    239                           PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
    240                           PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
    241                           PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
    242                           PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
    243
    244    ptimer_transaction_begin(s->timer);
    245    ptimer_set_period_from_clock(s->timer, s->pclk, 1);
    246    ptimer_transaction_commit(s->timer);
    247}
    248
    249static const VMStateDescription cmsdk_apb_timer_vmstate = {
    250    .name = "cmsdk-apb-timer",
    251    .version_id = 2,
    252    .minimum_version_id = 2,
    253    .fields = (VMStateField[]) {
    254        VMSTATE_PTIMER(timer, CMSDKAPBTimer),
    255        VMSTATE_CLOCK(pclk, CMSDKAPBTimer),
    256        VMSTATE_UINT32(ctrl, CMSDKAPBTimer),
    257        VMSTATE_UINT32(value, CMSDKAPBTimer),
    258        VMSTATE_UINT32(reload, CMSDKAPBTimer),
    259        VMSTATE_UINT32(intstatus, CMSDKAPBTimer),
    260        VMSTATE_END_OF_LIST()
    261    }
    262};
    263
    264static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
    265{
    266    DeviceClass *dc = DEVICE_CLASS(klass);
    267
    268    dc->realize = cmsdk_apb_timer_realize;
    269    dc->vmsd = &cmsdk_apb_timer_vmstate;
    270    dc->reset = cmsdk_apb_timer_reset;
    271}
    272
    273static const TypeInfo cmsdk_apb_timer_info = {
    274    .name = TYPE_CMSDK_APB_TIMER,
    275    .parent = TYPE_SYS_BUS_DEVICE,
    276    .instance_size = sizeof(CMSDKAPBTimer),
    277    .instance_init = cmsdk_apb_timer_init,
    278    .class_init = cmsdk_apb_timer_class_init,
    279};
    280
    281static void cmsdk_apb_timer_register_types(void)
    282{
    283    type_register_static(&cmsdk_apb_timer_info);
    284}
    285
    286type_init(cmsdk_apb_timer_register_types);