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

tco.c (7547B)


      1/*
      2 * QEMU ICH9 TCO emulation
      3 *
      4 * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
      5 *
      6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      7 * See the COPYING file in the top-level directory.
      8 */
      9
     10#include "qemu/osdep.h"
     11#include "sysemu/watchdog.h"
     12#include "hw/i386/ich9.h"
     13#include "migration/vmstate.h"
     14
     15#include "hw/acpi/tco.h"
     16#include "trace.h"
     17
     18enum {
     19    TCO_RLD_DEFAULT         = 0x0000,
     20    TCO_DAT_IN_DEFAULT      = 0x00,
     21    TCO_DAT_OUT_DEFAULT     = 0x00,
     22    TCO1_STS_DEFAULT        = 0x0000,
     23    TCO2_STS_DEFAULT        = 0x0000,
     24    TCO1_CNT_DEFAULT        = 0x0000,
     25    TCO2_CNT_DEFAULT        = 0x0008,
     26    TCO_MESSAGE1_DEFAULT    = 0x00,
     27    TCO_MESSAGE2_DEFAULT    = 0x00,
     28    TCO_WDCNT_DEFAULT       = 0x00,
     29    TCO_TMR_DEFAULT         = 0x0004,
     30    SW_IRQ_GEN_DEFAULT      = 0x03,
     31};
     32
     33static inline void tco_timer_reload(TCOIORegs *tr)
     34{
     35    int ticks = tr->tco.tmr & TCO_TMR_MASK;
     36    int64_t nsec = (int64_t)ticks * TCO_TICK_NSEC;
     37
     38    trace_tco_timer_reload(ticks, nsec / 1000000);
     39    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + nsec;
     40    timer_mod(tr->tco_timer, tr->expire_time);
     41}
     42
     43static inline void tco_timer_stop(TCOIORegs *tr)
     44{
     45    tr->expire_time = -1;
     46    timer_del(tr->tco_timer);
     47}
     48
     49static void tco_timer_expired(void *opaque)
     50{
     51    TCOIORegs *tr = opaque;
     52    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
     53    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
     54    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS);
     55
     56    trace_tco_timer_expired(tr->timeouts_no,
     57                            lpc->pin_strap.spkr_hi,
     58                            !!(gcs & ICH9_CC_GCS_NO_REBOOT));
     59    tr->tco.rld = 0;
     60    tr->tco.sts1 |= TCO_TIMEOUT;
     61    if (++tr->timeouts_no == 2) {
     62        tr->tco.sts2 |= TCO_SECOND_TO_STS;
     63        tr->tco.sts2 |= TCO_BOOT_STS;
     64        tr->timeouts_no = 0;
     65
     66        if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
     67            watchdog_perform_action();
     68            tco_timer_stop(tr);
     69            return;
     70        }
     71    }
     72
     73    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
     74        ich9_generate_smi();
     75    }
     76    tr->tco.rld = tr->tco.tmr;
     77    tco_timer_reload(tr);
     78}
     79
     80/* NOTE: values of 0 or 1 will be ignored by ICH */
     81static inline int can_start_tco_timer(TCOIORegs *tr)
     82{
     83    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
     84}
     85
     86static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
     87{
     88    uint16_t rld;
     89
     90    switch (addr) {
     91    case TCO_RLD:
     92        if (tr->expire_time != -1) {
     93            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     94            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
     95            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
     96        } else {
     97            rld = tr->tco.rld;
     98        }
     99        return rld;
    100    case TCO_DAT_IN:
    101        return tr->tco.din;
    102    case TCO_DAT_OUT:
    103        return tr->tco.dout;
    104    case TCO1_STS:
    105        return tr->tco.sts1;
    106    case TCO2_STS:
    107        return tr->tco.sts2;
    108    case TCO1_CNT:
    109        return tr->tco.cnt1;
    110    case TCO2_CNT:
    111        return tr->tco.cnt2;
    112    case TCO_MESSAGE1:
    113        return tr->tco.msg1;
    114    case TCO_MESSAGE2:
    115        return tr->tco.msg2;
    116    case TCO_WDCNT:
    117        return tr->tco.wdcnt;
    118    case TCO_TMR:
    119        return tr->tco.tmr;
    120    case SW_IRQ_GEN:
    121        return tr->sw_irq_gen;
    122    }
    123    return 0;
    124}
    125
    126static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
    127{
    128    switch (addr) {
    129    case TCO_RLD:
    130        tr->timeouts_no = 0;
    131        if (can_start_tco_timer(tr)) {
    132            tr->tco.rld = tr->tco.tmr;
    133            tco_timer_reload(tr);
    134        } else {
    135            tr->tco.rld = val;
    136        }
    137        break;
    138    case TCO_DAT_IN:
    139        tr->tco.din = val;
    140        tr->tco.sts1 |= SW_TCO_SMI;
    141        ich9_generate_smi();
    142        break;
    143    case TCO_DAT_OUT:
    144        tr->tco.dout = val;
    145        tr->tco.sts1 |= TCO_INT_STS;
    146        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
    147        break;
    148    case TCO1_STS:
    149        tr->tco.sts1 = val & TCO1_STS_MASK;
    150        break;
    151    case TCO2_STS:
    152        tr->tco.sts2 = val & TCO2_STS_MASK;
    153        break;
    154    case TCO1_CNT:
    155        val &= TCO1_CNT_MASK;
    156        /*
    157         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
    158         * is required to change this bit from 1 to 0 -- it defaults to 0.
    159         */
    160        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
    161        if (can_start_tco_timer(tr)) {
    162            tr->tco.rld = tr->tco.tmr;
    163            tco_timer_reload(tr);
    164        } else {
    165            tco_timer_stop(tr);
    166        }
    167        break;
    168    case TCO2_CNT:
    169        tr->tco.cnt2 = val;
    170        break;
    171    case TCO_MESSAGE1:
    172        tr->tco.msg1 = val;
    173        break;
    174    case TCO_MESSAGE2:
    175        tr->tco.msg2 = val;
    176        break;
    177    case TCO_WDCNT:
    178        tr->tco.wdcnt = val;
    179        break;
    180    case TCO_TMR:
    181        tr->tco.tmr = val;
    182        break;
    183    case SW_IRQ_GEN:
    184        tr->sw_irq_gen = val;
    185        break;
    186    }
    187}
    188
    189static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
    190{
    191    TCOIORegs *tr = opaque;
    192    return tco_ioport_readw(tr, addr);
    193}
    194
    195static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
    196                          unsigned width)
    197{
    198    TCOIORegs *tr = opaque;
    199    tco_ioport_writew(tr, addr, val);
    200}
    201
    202static const MemoryRegionOps tco_io_ops = {
    203    .read = tco_io_readw,
    204    .write = tco_io_writew,
    205    .valid.min_access_size = 1,
    206    .valid.max_access_size = 4,
    207    .impl.min_access_size = 1,
    208    .impl.max_access_size = 2,
    209    .endianness = DEVICE_LITTLE_ENDIAN,
    210};
    211
    212void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
    213{
    214    *tr = (TCOIORegs) {
    215        .tco = {
    216            .rld      = TCO_RLD_DEFAULT,
    217            .din      = TCO_DAT_IN_DEFAULT,
    218            .dout     = TCO_DAT_OUT_DEFAULT,
    219            .sts1     = TCO1_STS_DEFAULT,
    220            .sts2     = TCO2_STS_DEFAULT,
    221            .cnt1     = TCO1_CNT_DEFAULT,
    222            .cnt2     = TCO2_CNT_DEFAULT,
    223            .msg1     = TCO_MESSAGE1_DEFAULT,
    224            .msg2     = TCO_MESSAGE2_DEFAULT,
    225            .wdcnt    = TCO_WDCNT_DEFAULT,
    226            .tmr      = TCO_TMR_DEFAULT,
    227        },
    228        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
    229        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
    230        .expire_time   = -1,
    231        .timeouts_no   = 0,
    232    };
    233    memory_region_init_io(&tr->io, memory_region_owner(parent),
    234                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
    235    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
    236}
    237
    238const VMStateDescription vmstate_tco_io_sts = {
    239    .name = "tco io device status",
    240    .version_id = 1,
    241    .minimum_version_id = 1,
    242    .minimum_version_id_old = 1,
    243    .fields      = (VMStateField[]) {
    244        VMSTATE_UINT16(tco.rld, TCOIORegs),
    245        VMSTATE_UINT8(tco.din, TCOIORegs),
    246        VMSTATE_UINT8(tco.dout, TCOIORegs),
    247        VMSTATE_UINT16(tco.sts1, TCOIORegs),
    248        VMSTATE_UINT16(tco.sts2, TCOIORegs),
    249        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
    250        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
    251        VMSTATE_UINT8(tco.msg1, TCOIORegs),
    252        VMSTATE_UINT8(tco.msg2, TCOIORegs),
    253        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
    254        VMSTATE_UINT16(tco.tmr, TCOIORegs),
    255        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
    256        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
    257        VMSTATE_INT64(expire_time, TCOIORegs),
    258        VMSTATE_UINT8(timeouts_no, TCOIORegs),
    259        VMSTATE_END_OF_LIST()
    260    }
    261};