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

sbsa_gwdt.c (8115B)


      1/*
      2 * Generic watchdog device model for SBSA
      3 *
      4 * The watchdog device has been implemented as revision 1 variant of
      5 * the ARM SBSA specification v6.0
      6 * (https://developer.arm.com/documentation/den0029/d?lang=en)
      7 *
      8 * Copyright Linaro.org 2020
      9 *
     10 * Authors:
     11 *  Shashi Mallela <shashi.mallela@linaro.org>
     12 *
     13 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
     14 * option) any later version.  See the COPYING file in the top-level directory.
     15 *
     16 */
     17
     18#include "qemu/osdep.h"
     19#include "sysemu/reset.h"
     20#include "sysemu/watchdog.h"
     21#include "hw/watchdog/sbsa_gwdt.h"
     22#include "qemu/timer.h"
     23#include "migration/vmstate.h"
     24#include "qemu/log.h"
     25#include "qemu/module.h"
     26
     27static WatchdogTimerModel model = {
     28    .wdt_name = TYPE_WDT_SBSA,
     29    .wdt_description = "SBSA-compliant generic watchdog device",
     30};
     31
     32static const VMStateDescription vmstate_sbsa_gwdt = {
     33    .name = "sbsa-gwdt",
     34    .version_id = 1,
     35    .minimum_version_id = 1,
     36    .fields = (VMStateField[]) {
     37        VMSTATE_TIMER_PTR(timer, SBSA_GWDTState),
     38        VMSTATE_UINT32(wcs, SBSA_GWDTState),
     39        VMSTATE_UINT32(worl, SBSA_GWDTState),
     40        VMSTATE_UINT32(woru, SBSA_GWDTState),
     41        VMSTATE_UINT32(wcvl, SBSA_GWDTState),
     42        VMSTATE_UINT32(wcvu, SBSA_GWDTState),
     43        VMSTATE_END_OF_LIST()
     44    }
     45};
     46
     47typedef enum WdtRefreshType {
     48    EXPLICIT_REFRESH = 0,
     49    TIMEOUT_REFRESH = 1,
     50} WdtRefreshType;
     51
     52static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size)
     53{
     54    SBSA_GWDTState *s = SBSA_GWDT(opaque);
     55    uint32_t ret = 0;
     56
     57    switch (addr) {
     58    case SBSA_GWDT_WRR:
     59        /* watch refresh read has no effect and returns 0 */
     60        ret = 0;
     61        break;
     62    case SBSA_GWDT_W_IIDR:
     63        ret = s->id;
     64        break;
     65    default:
     66        qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :"
     67                        " 0x%x\n", (int)addr);
     68    }
     69    return ret;
     70}
     71
     72static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size)
     73{
     74    SBSA_GWDTState *s = SBSA_GWDT(opaque);
     75    uint32_t ret = 0;
     76
     77    switch (addr) {
     78    case SBSA_GWDT_WCS:
     79        ret = s->wcs;
     80        break;
     81    case SBSA_GWDT_WOR:
     82        ret = s->worl;
     83        break;
     84    case SBSA_GWDT_WORU:
     85         ret = s->woru;
     86         break;
     87    case SBSA_GWDT_WCV:
     88        ret = s->wcvl;
     89        break;
     90    case SBSA_GWDT_WCVU:
     91        ret = s->wcvu;
     92        break;
     93    case SBSA_GWDT_W_IIDR:
     94        ret = s->id;
     95        break;
     96    default:
     97        qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :"
     98                        " 0x%x\n", (int)addr);
     99    }
    100    return ret;
    101}
    102
    103static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype)
    104{
    105    uint64_t timeout = 0;
    106
    107    timer_del(s->timer);
    108
    109    if (s->wcs & SBSA_GWDT_WCS_EN) {
    110        /*
    111         * Extract the upper 16 bits from woru & 32 bits from worl
    112         * registers to construct the 48 bit offset value
    113         */
    114        timeout = s->woru;
    115        timeout <<= 32;
    116        timeout |= s->worl;
    117        timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ);
    118        timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    119
    120        if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) &&
    121                (!(s->wcs & SBSA_GWDT_WCS_WS0)))) {
    122            /* store the current timeout value into compare registers */
    123            s->wcvu = timeout >> 32;
    124            s->wcvl = timeout;
    125        }
    126        timer_mod(s->timer, timeout);
    127    }
    128}
    129
    130static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data,
    131                             unsigned size) {
    132    SBSA_GWDTState *s = SBSA_GWDT(opaque);
    133
    134    if (offset == SBSA_GWDT_WRR) {
    135        s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
    136
    137        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
    138    } else {
    139        qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame write :"
    140                        " 0x%x\n", (int)offset);
    141    }
    142}
    143
    144static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data,
    145                             unsigned size) {
    146    SBSA_GWDTState *s = SBSA_GWDT(opaque);
    147
    148    switch (offset) {
    149    case SBSA_GWDT_WCS:
    150        s->wcs = data & SBSA_GWDT_WCS_EN;
    151        qemu_set_irq(s->irq, 0);
    152        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
    153        break;
    154
    155    case SBSA_GWDT_WOR:
    156        s->worl = data;
    157        s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
    158        qemu_set_irq(s->irq, 0);
    159        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
    160        break;
    161
    162    case SBSA_GWDT_WORU:
    163        s->woru = data & SBSA_GWDT_WOR_MASK;
    164        s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
    165        qemu_set_irq(s->irq, 0);
    166        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
    167        break;
    168
    169    case SBSA_GWDT_WCV:
    170        s->wcvl = data;
    171        break;
    172
    173    case SBSA_GWDT_WCVU:
    174        s->wcvu = data;
    175        break;
    176
    177    default:
    178        qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :"
    179                " 0x%x\n", (int)offset);
    180    }
    181    return;
    182}
    183
    184static void wdt_sbsa_gwdt_reset(DeviceState *dev)
    185{
    186    SBSA_GWDTState *s = SBSA_GWDT(dev);
    187
    188    timer_del(s->timer);
    189
    190    s->wcs  = 0;
    191    s->wcvl = 0;
    192    s->wcvu = 0;
    193    s->worl = 0;
    194    s->woru = 0;
    195    s->id = SBSA_GWDT_ID;
    196}
    197
    198static void sbsa_gwdt_timer_sysinterrupt(void *opaque)
    199{
    200    SBSA_GWDTState *s = SBSA_GWDT(opaque);
    201
    202    if (!(s->wcs & SBSA_GWDT_WCS_WS0)) {
    203        s->wcs |= SBSA_GWDT_WCS_WS0;
    204        sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH);
    205        qemu_set_irq(s->irq, 1);
    206    } else {
    207        s->wcs |= SBSA_GWDT_WCS_WS1;
    208        qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
    209        /*
    210         * Reset the watchdog only if the guest gets notified about
    211         * expiry. watchdog_perform_action() may temporarily relinquish
    212         * the BQL; reset before triggering the action to avoid races with
    213         * sbsa_gwdt instructions.
    214         */
    215        switch (get_watchdog_action()) {
    216        case WATCHDOG_ACTION_DEBUG:
    217        case WATCHDOG_ACTION_NONE:
    218        case WATCHDOG_ACTION_PAUSE:
    219            break;
    220        default:
    221            wdt_sbsa_gwdt_reset(DEVICE(s));
    222        }
    223        watchdog_perform_action();
    224    }
    225}
    226
    227static const MemoryRegionOps sbsa_gwdt_rops = {
    228    .read = sbsa_gwdt_rread,
    229    .write = sbsa_gwdt_rwrite,
    230    .endianness = DEVICE_LITTLE_ENDIAN,
    231    .valid.min_access_size = 4,
    232    .valid.max_access_size = 4,
    233    .valid.unaligned = false,
    234};
    235
    236static const MemoryRegionOps sbsa_gwdt_ops = {
    237    .read = sbsa_gwdt_read,
    238    .write = sbsa_gwdt_write,
    239    .endianness = DEVICE_LITTLE_ENDIAN,
    240    .valid.min_access_size = 4,
    241    .valid.max_access_size = 4,
    242    .valid.unaligned = false,
    243};
    244
    245static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
    246{
    247    SBSA_GWDTState *s = SBSA_GWDT(dev);
    248    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    249
    250    memory_region_init_io(&s->rmmio, OBJECT(dev),
    251                          &sbsa_gwdt_rops, s,
    252                          "sbsa_gwdt.refresh",
    253                          SBSA_GWDT_RMMIO_SIZE);
    254
    255    memory_region_init_io(&s->cmmio, OBJECT(dev),
    256                          &sbsa_gwdt_ops, s,
    257                          "sbsa_gwdt.control",
    258                          SBSA_GWDT_CMMIO_SIZE);
    259
    260    sysbus_init_mmio(sbd, &s->rmmio);
    261    sysbus_init_mmio(sbd, &s->cmmio);
    262
    263    sysbus_init_irq(sbd, &s->irq);
    264
    265    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt,
    266                dev);
    267}
    268
    269static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data)
    270{
    271    DeviceClass *dc = DEVICE_CLASS(klass);
    272
    273    dc->realize = wdt_sbsa_gwdt_realize;
    274    dc->reset = wdt_sbsa_gwdt_reset;
    275    dc->hotpluggable = false;
    276    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
    277    dc->vmsd = &vmstate_sbsa_gwdt;
    278}
    279
    280static const TypeInfo wdt_sbsa_gwdt_info = {
    281    .class_init = wdt_sbsa_gwdt_class_init,
    282    .parent = TYPE_SYS_BUS_DEVICE,
    283    .name  = TYPE_WDT_SBSA,
    284    .instance_size  = sizeof(SBSA_GWDTState),
    285};
    286
    287static void wdt_sbsa_gwdt_register_types(void)
    288{
    289    watchdog_add_model(&model);
    290    type_register_static(&wdt_sbsa_gwdt_info);
    291}
    292
    293type_init(wdt_sbsa_gwdt_register_types)