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

allwinner-a10-pit.c (9640B)


      1/*
      2 * Allwinner A10 timer device emulation
      3 *
      4 * Copyright (C) 2013 Li Guang
      5 * Written by Li Guang <lig.fnst@cn.fujitsu.com>
      6 *
      7 * This program is free software; you can redistribute it and/or modify it
      8 * under the terms of the GNU General Public License as published by the
      9 * Free Software Foundation; either version 2 of the License, or
     10 * (at your option) any later version.
     11 *
     12 * This program is distributed in the hope that it will be useful, but WITHOUT
     13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
     15 * for more details.
     16 */
     17
     18#include "qemu/osdep.h"
     19#include "hw/irq.h"
     20#include "hw/qdev-properties.h"
     21#include "hw/sysbus.h"
     22#include "hw/timer/allwinner-a10-pit.h"
     23#include "migration/vmstate.h"
     24#include "qemu/log.h"
     25#include "qemu/module.h"
     26
     27static void a10_pit_update_irq(AwA10PITState *s)
     28{
     29    int i;
     30
     31    for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
     32        qemu_set_irq(s->irq[i], !!(s->irq_status & s->irq_enable & (1 << i)));
     33    }
     34}
     35
     36static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
     37{
     38    AwA10PITState *s = AW_A10_PIT(opaque);
     39    uint8_t index;
     40
     41    switch (offset) {
     42    case AW_A10_PIT_TIMER_IRQ_EN:
     43        return s->irq_enable;
     44    case AW_A10_PIT_TIMER_IRQ_ST:
     45        return s->irq_status;
     46    case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
     47        index = offset & 0xf0;
     48        index >>= 4;
     49        index -= 1;
     50        switch (offset & 0x0f) {
     51        case AW_A10_PIT_TIMER_CONTROL:
     52            return s->control[index];
     53        case AW_A10_PIT_TIMER_INTERVAL:
     54            return s->interval[index];
     55        case AW_A10_PIT_TIMER_COUNT:
     56            s->count[index] = ptimer_get_count(s->timer[index]);
     57            return s->count[index];
     58        default:
     59            qemu_log_mask(LOG_GUEST_ERROR,
     60                          "%s: Bad offset 0x%x\n",  __func__, (int)offset);
     61            break;
     62        }
     63    case AW_A10_PIT_WDOG_CONTROL:
     64        break;
     65    case AW_A10_PIT_WDOG_MODE:
     66        break;
     67    case AW_A10_PIT_COUNT_LO:
     68        return s->count_lo;
     69    case AW_A10_PIT_COUNT_HI:
     70        return s->count_hi;
     71    case AW_A10_PIT_COUNT_CTL:
     72        return s->count_ctl;
     73    default:
     74        qemu_log_mask(LOG_GUEST_ERROR,
     75                      "%s: Bad offset 0x%x\n",  __func__, (int)offset);
     76        break;
     77    }
     78
     79    return 0;
     80}
     81
     82/* Must be called inside a ptimer transaction block for s->timer[index] */
     83static void a10_pit_set_freq(AwA10PITState *s, int index)
     84{
     85    uint32_t prescaler, source, source_freq;
     86
     87    prescaler = 1 << extract32(s->control[index], 4, 3);
     88    source = extract32(s->control[index], 2, 2);
     89    source_freq = s->clk_freq[source];
     90
     91    if (source_freq) {
     92        ptimer_set_freq(s->timer[index], source_freq / prescaler);
     93    } else {
     94        qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n",
     95                      __func__, source);
     96    }
     97}
     98
     99static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
    100                            unsigned size)
    101{
    102     AwA10PITState *s = AW_A10_PIT(opaque);
    103     uint8_t index;
    104
    105    switch (offset) {
    106    case AW_A10_PIT_TIMER_IRQ_EN:
    107        s->irq_enable = value;
    108        a10_pit_update_irq(s);
    109        break;
    110    case AW_A10_PIT_TIMER_IRQ_ST:
    111        s->irq_status &= ~value;
    112        a10_pit_update_irq(s);
    113        break;
    114    case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
    115        index = offset & 0xf0;
    116        index >>= 4;
    117        index -= 1;
    118        switch (offset & 0x0f) {
    119        case AW_A10_PIT_TIMER_CONTROL:
    120            s->control[index] = value;
    121            ptimer_transaction_begin(s->timer[index]);
    122            a10_pit_set_freq(s, index);
    123            if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
    124                ptimer_set_count(s->timer[index], s->interval[index]);
    125            }
    126            if (s->control[index] & AW_A10_PIT_TIMER_EN) {
    127                int oneshot = 0;
    128                if (s->control[index] & AW_A10_PIT_TIMER_MODE) {
    129                    oneshot = 1;
    130                }
    131                ptimer_run(s->timer[index], oneshot);
    132            } else {
    133                ptimer_stop(s->timer[index]);
    134            }
    135            ptimer_transaction_commit(s->timer[index]);
    136            break;
    137        case AW_A10_PIT_TIMER_INTERVAL:
    138            s->interval[index] = value;
    139            ptimer_transaction_begin(s->timer[index]);
    140            ptimer_set_limit(s->timer[index], s->interval[index], 1);
    141            ptimer_transaction_commit(s->timer[index]);
    142            break;
    143        case AW_A10_PIT_TIMER_COUNT:
    144            s->count[index] = value;
    145            break;
    146        default:
    147            qemu_log_mask(LOG_GUEST_ERROR,
    148                          "%s: Bad offset 0x%x\n",  __func__, (int)offset);
    149        }
    150        break;
    151    case AW_A10_PIT_WDOG_CONTROL:
    152        s->watch_dog_control = value;
    153        break;
    154    case AW_A10_PIT_WDOG_MODE:
    155        s->watch_dog_mode = value;
    156        break;
    157    case AW_A10_PIT_COUNT_LO:
    158        s->count_lo = value;
    159        break;
    160    case AW_A10_PIT_COUNT_HI:
    161        s->count_hi = value;
    162        break;
    163    case AW_A10_PIT_COUNT_CTL:
    164        s->count_ctl = value;
    165        if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) {
    166            uint64_t  tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    167
    168            s->count_lo = tmp_count;
    169            s->count_hi = tmp_count >> 32;
    170            s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN;
    171        }
    172        if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) {
    173            s->count_lo = 0;
    174            s->count_hi = 0;
    175            s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN;
    176        }
    177        break;
    178    default:
    179        qemu_log_mask(LOG_GUEST_ERROR,
    180                      "%s: Bad offset 0x%x\n",  __func__, (int)offset);
    181        break;
    182    }
    183}
    184
    185static const MemoryRegionOps a10_pit_ops = {
    186    .read = a10_pit_read,
    187    .write = a10_pit_write,
    188    .endianness = DEVICE_NATIVE_ENDIAN,
    189};
    190
    191static Property a10_pit_properties[] = {
    192    DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0),
    193    DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0),
    194    DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0),
    195    DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0),
    196    DEFINE_PROP_END_OF_LIST(),
    197};
    198
    199static const VMStateDescription vmstate_a10_pit = {
    200    .name = "a10.pit",
    201    .version_id = 1,
    202    .minimum_version_id = 1,
    203    .fields = (VMStateField[]) {
    204        VMSTATE_UINT32(irq_enable, AwA10PITState),
    205        VMSTATE_UINT32(irq_status, AwA10PITState),
    206        VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR),
    207        VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR),
    208        VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR),
    209        VMSTATE_UINT32(watch_dog_mode, AwA10PITState),
    210        VMSTATE_UINT32(watch_dog_control, AwA10PITState),
    211        VMSTATE_UINT32(count_lo, AwA10PITState),
    212        VMSTATE_UINT32(count_hi, AwA10PITState),
    213        VMSTATE_UINT32(count_ctl, AwA10PITState),
    214        VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR),
    215        VMSTATE_END_OF_LIST()
    216    }
    217};
    218
    219static void a10_pit_reset(DeviceState *dev)
    220{
    221    AwA10PITState *s = AW_A10_PIT(dev);
    222    uint8_t i;
    223
    224    s->irq_enable = 0;
    225    s->irq_status = 0;
    226    a10_pit_update_irq(s);
    227
    228    for (i = 0; i < 6; i++) {
    229        s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
    230        s->interval[i] = 0;
    231        s->count[i] = 0;
    232        ptimer_transaction_begin(s->timer[i]);
    233        ptimer_stop(s->timer[i]);
    234        a10_pit_set_freq(s, i);
    235        ptimer_transaction_commit(s->timer[i]);
    236    }
    237    s->watch_dog_mode = 0;
    238    s->watch_dog_control = 0;
    239    s->count_lo = 0;
    240    s->count_hi = 0;
    241    s->count_ctl = 0;
    242}
    243
    244static void a10_pit_timer_cb(void *opaque)
    245{
    246    AwA10TimerContext *tc = opaque;
    247    AwA10PITState *s = tc->container;
    248    uint8_t i = tc->index;
    249
    250    if (s->control[i] & AW_A10_PIT_TIMER_EN) {
    251        s->irq_status |= 1 << i;
    252        if (s->control[i] & AW_A10_PIT_TIMER_MODE) {
    253            ptimer_stop(s->timer[i]);
    254            s->control[i] &= ~AW_A10_PIT_TIMER_EN;
    255        }
    256        a10_pit_update_irq(s);
    257    }
    258}
    259
    260static void a10_pit_init(Object *obj)
    261{
    262    AwA10PITState *s = AW_A10_PIT(obj);
    263    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    264    uint8_t i;
    265
    266    for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
    267        sysbus_init_irq(sbd, &s->irq[i]);
    268    }
    269    memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s,
    270                          TYPE_AW_A10_PIT, 0x400);
    271    sysbus_init_mmio(sbd, &s->iomem);
    272
    273    for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
    274        AwA10TimerContext *tc = &s->timer_context[i];
    275
    276        tc->container = s;
    277        tc->index = i;
    278        s->timer[i] = ptimer_init(a10_pit_timer_cb, tc, PTIMER_POLICY_DEFAULT);
    279    }
    280}
    281
    282static void a10_pit_finalize(Object *obj)
    283{
    284    AwA10PITState *s = AW_A10_PIT(obj);
    285    int i;
    286
    287    for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
    288        ptimer_free(s->timer[i]);
    289    }
    290}
    291
    292static void a10_pit_class_init(ObjectClass *klass, void *data)
    293{
    294    DeviceClass *dc = DEVICE_CLASS(klass);
    295
    296    dc->reset = a10_pit_reset;
    297    device_class_set_props(dc, a10_pit_properties);
    298    dc->desc = "allwinner a10 timer";
    299    dc->vmsd = &vmstate_a10_pit;
    300}
    301
    302static const TypeInfo a10_pit_info = {
    303    .name = TYPE_AW_A10_PIT,
    304    .parent = TYPE_SYS_BUS_DEVICE,
    305    .instance_size = sizeof(AwA10PITState),
    306    .instance_init = a10_pit_init,
    307    .instance_finalize = a10_pit_finalize,
    308    .class_init = a10_pit_class_init,
    309};
    310
    311static void a10_register_types(void)
    312{
    313    type_register_static(&a10_pit_info);
    314}
    315
    316type_init(a10_register_types);