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

wdt_imx2.c (8984B)


      1/*
      2 * Copyright (c) 2018, Impinj, Inc.
      3 *
      4 * i.MX2 Watchdog IP block
      5 *
      6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
      7 *
      8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      9 * See the COPYING file in the top-level directory.
     10 */
     11
     12#include "qemu/osdep.h"
     13#include "qemu/bitops.h"
     14#include "qemu/module.h"
     15#include "sysemu/watchdog.h"
     16#include "migration/vmstate.h"
     17#include "hw/qdev-properties.h"
     18
     19#include "hw/watchdog/wdt_imx2.h"
     20
     21static void imx2_wdt_interrupt(void *opaque)
     22{
     23    IMX2WdtState *s = IMX2_WDT(opaque);
     24
     25    s->wicr |= IMX2_WDT_WICR_WTIS;
     26    qemu_set_irq(s->irq, 1);
     27}
     28
     29static void imx2_wdt_expired(void *opaque)
     30{
     31    IMX2WdtState *s = IMX2_WDT(opaque);
     32
     33    s->wrsr = IMX2_WDT_WRSR_TOUT;
     34
     35    /* Perform watchdog action if watchdog is enabled */
     36    if (s->wcr & IMX2_WDT_WCR_WDE) {
     37        s->wrsr = IMX2_WDT_WRSR_TOUT;
     38        watchdog_perform_action();
     39    }
     40}
     41
     42static void imx2_wdt_reset(DeviceState *dev)
     43{
     44    IMX2WdtState *s = IMX2_WDT(dev);
     45
     46    ptimer_transaction_begin(s->timer);
     47    ptimer_stop(s->timer);
     48    ptimer_transaction_commit(s->timer);
     49
     50    if (s->pretimeout_support) {
     51        ptimer_transaction_begin(s->itimer);
     52        ptimer_stop(s->itimer);
     53        ptimer_transaction_commit(s->itimer);
     54    }
     55
     56    s->wicr_locked = false;
     57    s->wcr_locked = false;
     58    s->wcr_wde_locked = false;
     59
     60    s->wcr = IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS;
     61    s->wsr = 0;
     62    s->wrsr &= ~(IMX2_WDT_WRSR_TOUT | IMX2_WDT_WRSR_SFTW);
     63    s->wicr = IMX2_WDT_WICR_WICT_DEF;
     64    s->wmcr = IMX2_WDT_WMCR_PDE;
     65}
     66
     67static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size)
     68{
     69    IMX2WdtState *s = IMX2_WDT(opaque);
     70
     71    switch (addr) {
     72    case IMX2_WDT_WCR:
     73        return s->wcr;
     74    case IMX2_WDT_WSR:
     75        return s->wsr;
     76    case IMX2_WDT_WRSR:
     77        return s->wrsr;
     78    case IMX2_WDT_WICR:
     79        return s->wicr;
     80    case IMX2_WDT_WMCR:
     81        return s->wmcr;
     82    }
     83    return 0;
     84}
     85
     86static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start)
     87{
     88    bool running = (s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT);
     89    bool enabled = s->wicr & IMX2_WDT_WICR_WIE;
     90
     91    ptimer_transaction_begin(s->itimer);
     92    if (start || !enabled) {
     93        ptimer_stop(s->itimer);
     94    }
     95    if (running && enabled) {
     96        int count = ptimer_get_count(s->timer);
     97        int pretimeout = s->wicr & IMX2_WDT_WICR_WICT;
     98
     99        /*
    100         * Only (re-)start pretimeout timer if its counter value is larger
    101         * than 0. Otherwise it will fire right away and we'll get an
    102         * interrupt loop.
    103         */
    104        if (count > pretimeout) {
    105            ptimer_set_count(s->itimer, count - pretimeout);
    106            if (start) {
    107                ptimer_run(s->itimer, 1);
    108            }
    109        }
    110    }
    111    ptimer_transaction_commit(s->itimer);
    112}
    113
    114static void imx_wdt2_update_timer(IMX2WdtState *s, bool start)
    115{
    116    ptimer_transaction_begin(s->timer);
    117    if (start) {
    118        ptimer_stop(s->timer);
    119    }
    120    if ((s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT)) {
    121        int count = (s->wcr & IMX2_WDT_WCR_WT) >> 8;
    122
    123        /* A value of 0 reflects one period (0.5s). */
    124        ptimer_set_count(s->timer, count + 1);
    125        if (start) {
    126            ptimer_run(s->timer, 1);
    127        }
    128    }
    129    ptimer_transaction_commit(s->timer);
    130    if (s->pretimeout_support) {
    131        imx_wdt2_update_itimer(s, start);
    132    }
    133}
    134
    135static void imx2_wdt_write(void *opaque, hwaddr addr,
    136                           uint64_t value, unsigned int size)
    137{
    138    IMX2WdtState *s = IMX2_WDT(opaque);
    139
    140    switch (addr) {
    141    case IMX2_WDT_WCR:
    142        if (s->wcr_locked) {
    143            value &= ~IMX2_WDT_WCR_LOCK_MASK;
    144            value |= (s->wicr & IMX2_WDT_WCR_LOCK_MASK);
    145        }
    146        s->wcr_locked = true;
    147        if (s->wcr_wde_locked) {
    148            value &= ~IMX2_WDT_WCR_WDE;
    149            value |= (s->wicr & ~IMX2_WDT_WCR_WDE);
    150        } else if (value & IMX2_WDT_WCR_WDE) {
    151            s->wcr_wde_locked = true;
    152        }
    153        if (s->wcr_wdt_locked) {
    154            value &= ~IMX2_WDT_WCR_WDT;
    155            value |= (s->wicr & ~IMX2_WDT_WCR_WDT);
    156        } else if (value & IMX2_WDT_WCR_WDT) {
    157            s->wcr_wdt_locked = true;
    158        }
    159
    160        s->wcr = value;
    161        if (!(value & IMX2_WDT_WCR_SRS)) {
    162            s->wrsr = IMX2_WDT_WRSR_SFTW;
    163        }
    164        if (!(value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS)) ||
    165            (!(value & IMX2_WDT_WCR_WT) && (value & IMX2_WDT_WCR_WDE))) {
    166            watchdog_perform_action();
    167        }
    168        s->wcr |= IMX2_WDT_WCR_SRS;
    169        imx_wdt2_update_timer(s, true);
    170        break;
    171    case IMX2_WDT_WSR:
    172        if (s->wsr == IMX2_WDT_SEQ1 && value == IMX2_WDT_SEQ2) {
    173            imx_wdt2_update_timer(s, false);
    174        }
    175        s->wsr = value;
    176        break;
    177    case IMX2_WDT_WRSR:
    178        break;
    179    case IMX2_WDT_WICR:
    180        if (!s->pretimeout_support) {
    181            return;
    182        }
    183        value &= IMX2_WDT_WICR_LOCK_MASK | IMX2_WDT_WICR_WTIS;
    184        if (s->wicr_locked) {
    185            value &= IMX2_WDT_WICR_WTIS;
    186            value |= (s->wicr & IMX2_WDT_WICR_LOCK_MASK);
    187        }
    188        s->wicr = value | (s->wicr & IMX2_WDT_WICR_WTIS);
    189        if (value & IMX2_WDT_WICR_WTIS) {
    190            s->wicr &= ~IMX2_WDT_WICR_WTIS;
    191            qemu_set_irq(s->irq, 0);
    192        }
    193        imx_wdt2_update_itimer(s, true);
    194        s->wicr_locked = true;
    195        break;
    196    case IMX2_WDT_WMCR:
    197        s->wmcr = value & IMX2_WDT_WMCR_PDE;
    198        break;
    199    }
    200}
    201
    202static const MemoryRegionOps imx2_wdt_ops = {
    203    .read  = imx2_wdt_read,
    204    .write = imx2_wdt_write,
    205    .endianness = DEVICE_NATIVE_ENDIAN,
    206    .impl = {
    207        /*
    208         * Our device would not work correctly if the guest was doing
    209         * unaligned access. This might not be a limitation on the
    210         * real device but in practice there is no reason for a guest
    211         * to access this device unaligned.
    212         */
    213        .min_access_size = 2,
    214        .max_access_size = 2,
    215        .unaligned = false,
    216    },
    217};
    218
    219static const VMStateDescription vmstate_imx2_wdt = {
    220    .name = "imx2.wdt",
    221    .fields = (VMStateField[]) {
    222        VMSTATE_PTIMER(timer, IMX2WdtState),
    223        VMSTATE_PTIMER(itimer, IMX2WdtState),
    224        VMSTATE_BOOL(wicr_locked, IMX2WdtState),
    225        VMSTATE_BOOL(wcr_locked, IMX2WdtState),
    226        VMSTATE_BOOL(wcr_wde_locked, IMX2WdtState),
    227        VMSTATE_BOOL(wcr_wdt_locked, IMX2WdtState),
    228        VMSTATE_UINT16(wcr, IMX2WdtState),
    229        VMSTATE_UINT16(wsr, IMX2WdtState),
    230        VMSTATE_UINT16(wrsr, IMX2WdtState),
    231        VMSTATE_UINT16(wmcr, IMX2WdtState),
    232        VMSTATE_UINT16(wicr, IMX2WdtState),
    233        VMSTATE_END_OF_LIST()
    234    }
    235};
    236
    237static void imx2_wdt_realize(DeviceState *dev, Error **errp)
    238{
    239    IMX2WdtState *s = IMX2_WDT(dev);
    240    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    241
    242    memory_region_init_io(&s->mmio, OBJECT(dev),
    243                          &imx2_wdt_ops, s,
    244                          TYPE_IMX2_WDT,
    245                          IMX2_WDT_MMIO_SIZE);
    246    sysbus_init_mmio(sbd, &s->mmio);
    247    sysbus_init_irq(sbd, &s->irq);
    248
    249    s->timer = ptimer_init(imx2_wdt_expired, s,
    250                           PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
    251                           PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
    252                           PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
    253    ptimer_transaction_begin(s->timer);
    254    ptimer_set_freq(s->timer, 2);
    255    ptimer_set_limit(s->timer, 0xff, 1);
    256    ptimer_transaction_commit(s->timer);
    257    if (s->pretimeout_support) {
    258        s->itimer = ptimer_init(imx2_wdt_interrupt, s,
    259                                PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
    260                                PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
    261                                PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
    262        ptimer_transaction_begin(s->itimer);
    263        ptimer_set_freq(s->itimer, 2);
    264        ptimer_set_limit(s->itimer, 0xff, 1);
    265        ptimer_transaction_commit(s->itimer);
    266    }
    267}
    268
    269static Property imx2_wdt_properties[] = {
    270    DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support,
    271                     false),
    272    DEFINE_PROP_END_OF_LIST()
    273};
    274
    275static void imx2_wdt_class_init(ObjectClass *klass, void *data)
    276{
    277    DeviceClass *dc = DEVICE_CLASS(klass);
    278
    279    device_class_set_props(dc, imx2_wdt_properties);
    280    dc->realize = imx2_wdt_realize;
    281    dc->reset = imx2_wdt_reset;
    282    dc->vmsd = &vmstate_imx2_wdt;
    283    dc->desc = "i.MX watchdog timer";
    284    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
    285}
    286
    287static const TypeInfo imx2_wdt_info = {
    288    .name          = TYPE_IMX2_WDT,
    289    .parent        = TYPE_SYS_BUS_DEVICE,
    290    .instance_size = sizeof(IMX2WdtState),
    291    .class_init    = imx2_wdt_class_init,
    292};
    293
    294static WatchdogTimerModel model = {
    295    .wdt_name = "imx2-watchdog",
    296    .wdt_description = "i.MX2 Watchdog",
    297};
    298
    299static void imx2_wdt_register_type(void)
    300{
    301    watchdog_add_model(&model);
    302    type_register_static(&imx2_wdt_info);
    303}
    304type_init(imx2_wdt_register_type)