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

sifive_pwm.c (14851B)


      1/*
      2 * SiFive PWM
      3 *
      4 * Copyright (c) 2020 Western Digital
      5 *
      6 * Author:  Alistair Francis <alistair.francis@wdc.com>
      7 *
      8 * Permission is hereby granted, free of charge, to any person obtaining a copy
      9 * of this software and associated documentation files (the "Software"), to deal
     10 * in the Software without restriction, including without limitation the rights
     11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     12 * copies of the Software, and to permit persons to whom the Software is
     13 * furnished to do so, subject to the following conditions:
     14 *
     15 * The above copyright notice and this permission notice shall be included in
     16 * all copies or substantial portions of the Software.
     17 *
     18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     24 * THE SOFTWARE.
     25 */
     26
     27#include "qemu/osdep.h"
     28#include "trace.h"
     29#include "hw/irq.h"
     30#include "hw/timer/sifive_pwm.h"
     31#include "hw/qdev-properties.h"
     32#include "hw/registerfields.h"
     33#include "migration/vmstate.h"
     34#include "qemu/log.h"
     35#include "qemu/module.h"
     36
     37#define HAS_PWM_EN_BITS(cfg) ((cfg & R_CONFIG_ENONESHOT_MASK) || \
     38                              (cfg & R_CONFIG_ENALWAYS_MASK))
     39
     40#define PWMCMP_MASK 0xFFFF
     41#define PWMCOUNT_MASK 0x7FFFFFFF
     42
     43REG32(CONFIG,                   0x00)
     44    FIELD(CONFIG, SCALE,            0, 4)
     45    FIELD(CONFIG, STICKY,           8, 1)
     46    FIELD(CONFIG, ZEROCMP,          9, 1)
     47    FIELD(CONFIG, DEGLITCH,         10, 1)
     48    FIELD(CONFIG, ENALWAYS,         12, 1)
     49    FIELD(CONFIG, ENONESHOT,        13, 1)
     50    FIELD(CONFIG, CMP0CENTER,       16, 1)
     51    FIELD(CONFIG, CMP1CENTER,       17, 1)
     52    FIELD(CONFIG, CMP2CENTER,       18, 1)
     53    FIELD(CONFIG, CMP3CENTER,       19, 1)
     54    FIELD(CONFIG, CMP0GANG,         24, 1)
     55    FIELD(CONFIG, CMP1GANG,         25, 1)
     56    FIELD(CONFIG, CMP2GANG,         26, 1)
     57    FIELD(CONFIG, CMP3GANG,         27, 1)
     58    FIELD(CONFIG, CMP0IP,           28, 1)
     59    FIELD(CONFIG, CMP1IP,           29, 1)
     60    FIELD(CONFIG, CMP2IP,           30, 1)
     61    FIELD(CONFIG, CMP3IP,           31, 1)
     62REG32(COUNT,                    0x08)
     63REG32(PWMS,                     0x10)
     64REG32(PWMCMP0,                  0x20)
     65REG32(PWMCMP1,                  0x24)
     66REG32(PWMCMP2,                  0x28)
     67REG32(PWMCMP3,                  0x2C)
     68
     69static inline uint64_t sifive_pwm_ns_to_ticks(SiFivePwmState *s,
     70                                                uint64_t time)
     71{
     72    return muldiv64(time, s->freq_hz, NANOSECONDS_PER_SECOND);
     73}
     74
     75static inline uint64_t sifive_pwm_ticks_to_ns(SiFivePwmState *s,
     76                                                uint64_t ticks)
     77{
     78    return muldiv64(ticks, NANOSECONDS_PER_SECOND, s->freq_hz);
     79}
     80
     81static inline uint64_t sifive_pwm_compute_scale(SiFivePwmState *s)
     82{
     83    return s->pwmcfg & R_CONFIG_SCALE_MASK;
     84}
     85
     86static void sifive_pwm_set_alarms(SiFivePwmState *s)
     87{
     88    uint64_t now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     89
     90    if (HAS_PWM_EN_BITS(s->pwmcfg)) {
     91        /*
     92         * Subtract ticks from number of ticks when the timer was zero
     93         * and mask to the register width.
     94         */
     95        uint64_t pwmcount = (sifive_pwm_ns_to_ticks(s, now_ns) -
     96                             s->tick_offset) & PWMCOUNT_MASK;
     97        uint64_t scale = sifive_pwm_compute_scale(s);
     98        /* PWMs only contains PWMCMP_MASK bits starting at scale */
     99        uint64_t pwms = (pwmcount & (PWMCMP_MASK << scale)) >> scale;
    100
    101        for (int i = 0; i < SIFIVE_PWM_CHANS; i++) {
    102            uint64_t pwmcmp = s->pwmcmp[i] & PWMCMP_MASK;
    103            uint64_t pwmcmp_ticks = pwmcmp << scale;
    104
    105            /*
    106             * Per circuit diagram and spec, both cases raises corresponding
    107             * IP bit one clock cycle after time expires.
    108             */
    109            if (pwmcmp > pwms) {
    110                uint64_t offset = pwmcmp_ticks - pwmcount + 1;
    111                uint64_t when_to_fire = now_ns +
    112                                          sifive_pwm_ticks_to_ns(s, offset);
    113
    114                trace_sifive_pwm_set_alarm(when_to_fire, now_ns);
    115                timer_mod(&s->timer[i], when_to_fire);
    116            } else {
    117                /* Schedule interrupt for next cycle */
    118                trace_sifive_pwm_set_alarm(now_ns + 1, now_ns);
    119                timer_mod(&s->timer[i], now_ns + 1);
    120            }
    121
    122        }
    123    } else {
    124        /*
    125         * If timer incrementing disabled, just do pwms > pwmcmp check since
    126         * a write may have happened to PWMs.
    127         */
    128        uint64_t pwmcount = (s->tick_offset) & PWMCOUNT_MASK;
    129        uint64_t scale = sifive_pwm_compute_scale(s);
    130        uint64_t pwms = (pwmcount & (PWMCMP_MASK << scale)) >> scale;
    131
    132        for (int i = 0; i < SIFIVE_PWM_CHANS; i++) {
    133            uint64_t pwmcmp = s->pwmcmp[i] & PWMCMP_MASK;
    134
    135            if (pwms >= pwmcmp) {
    136                trace_sifive_pwm_set_alarm(now_ns + 1, now_ns);
    137                timer_mod(&s->timer[i], now_ns + 1);
    138            } else {
    139                /* Effectively disable timer by scheduling far in future. */
    140                trace_sifive_pwm_set_alarm(0xFFFFFFFFFFFFFF, now_ns);
    141                timer_mod(&s->timer[i], 0xFFFFFFFFFFFFFF);
    142            }
    143        }
    144    }
    145}
    146
    147static void sifive_pwm_interrupt(SiFivePwmState *s, int num)
    148{
    149    uint64_t now = sifive_pwm_ns_to_ticks(s,
    150                                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
    151    bool was_incrementing = HAS_PWM_EN_BITS(s->pwmcfg);
    152
    153    trace_sifive_pwm_interrupt(num);
    154
    155    s->pwmcfg |= R_CONFIG_CMP0IP_MASK << num;
    156    qemu_irq_raise(s->irqs[num]);
    157
    158    /*
    159     * If the zerocmp is set and pwmcmp0 raised the interrupt
    160     * reset the zero ticks.
    161     */
    162    if ((s->pwmcfg & R_CONFIG_ZEROCMP_MASK) && (num == 0)) {
    163        /* If reset signal conditions, disable ENONESHOT. */
    164        s->pwmcfg &= ~R_CONFIG_ENONESHOT_MASK;
    165
    166        if (was_incrementing) {
    167            /* If incrementing, time in ticks is when pwmcount is zero */
    168            s->tick_offset = now;
    169        } else {
    170            /* If not incrementing, pwmcount = 0 */
    171            s->tick_offset = 0;
    172        }
    173    }
    174
    175    /*
    176     * If carryout bit set, which we discern via looking for overflow,
    177     * also reset ENONESHOT.
    178     */
    179    if (was_incrementing &&
    180        ((now & PWMCOUNT_MASK) < (s->tick_offset & PWMCOUNT_MASK))) {
    181        s->pwmcfg &= ~R_CONFIG_ENONESHOT_MASK;
    182    }
    183
    184    /* Schedule or disable interrupts */
    185    sifive_pwm_set_alarms(s);
    186
    187    /* If was enabled, and now not enabled, switch tick rep */
    188    if (was_incrementing && !HAS_PWM_EN_BITS(s->pwmcfg)) {
    189        s->tick_offset = (now - s->tick_offset) & PWMCOUNT_MASK;
    190    }
    191}
    192
    193static void sifive_pwm_interrupt_0(void *opaque)
    194{
    195    SiFivePwmState *s = opaque;
    196
    197    sifive_pwm_interrupt(s, 0);
    198}
    199
    200static void sifive_pwm_interrupt_1(void *opaque)
    201{
    202    SiFivePwmState *s = opaque;
    203
    204    sifive_pwm_interrupt(s, 1);
    205}
    206
    207static void sifive_pwm_interrupt_2(void *opaque)
    208{
    209    SiFivePwmState *s = opaque;
    210
    211    sifive_pwm_interrupt(s, 2);
    212}
    213
    214static void sifive_pwm_interrupt_3(void *opaque)
    215{
    216    SiFivePwmState *s = opaque;
    217
    218    sifive_pwm_interrupt(s, 3);
    219}
    220
    221static uint64_t sifive_pwm_read(void *opaque, hwaddr addr,
    222                                  unsigned int size)
    223{
    224    SiFivePwmState *s = opaque;
    225    uint64_t cur_time, scale;
    226    uint64_t now = sifive_pwm_ns_to_ticks(s,
    227                                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
    228
    229    trace_sifive_pwm_read(addr);
    230
    231    switch (addr) {
    232    case A_CONFIG:
    233        return s->pwmcfg;
    234    case A_COUNT:
    235        cur_time = s->tick_offset;
    236
    237        if (HAS_PWM_EN_BITS(s->pwmcfg)) {
    238            cur_time = now - cur_time;
    239        }
    240
    241        /*
    242         * Return the value in the counter with bit 31 always 0
    243         * This is allowed to wrap around so we don't need to check that.
    244         */
    245        return cur_time & PWMCOUNT_MASK;
    246    case A_PWMS:
    247        cur_time = s->tick_offset;
    248        scale = sifive_pwm_compute_scale(s);
    249
    250        if (HAS_PWM_EN_BITS(s->pwmcfg)) {
    251            cur_time = now - cur_time;
    252        }
    253
    254        return ((cur_time & PWMCOUNT_MASK) >> scale) & PWMCMP_MASK;
    255    case A_PWMCMP0:
    256        return s->pwmcmp[0] & PWMCMP_MASK;
    257    case A_PWMCMP1:
    258        return s->pwmcmp[1] & PWMCMP_MASK;
    259    case A_PWMCMP2:
    260        return s->pwmcmp[2] & PWMCMP_MASK;
    261    case A_PWMCMP3:
    262        return s->pwmcmp[3] & PWMCMP_MASK;
    263    default:
    264        qemu_log_mask(LOG_GUEST_ERROR,
    265                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
    266        return 0;
    267    }
    268
    269    return 0;
    270}
    271
    272static void sifive_pwm_write(void *opaque, hwaddr addr,
    273                               uint64_t val64, unsigned int size)
    274{
    275    SiFivePwmState *s = opaque;
    276    uint32_t value = val64;
    277    uint64_t new_offset, scale;
    278    uint64_t now = sifive_pwm_ns_to_ticks(s,
    279                                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
    280
    281    trace_sifive_pwm_write(value, addr);
    282
    283    switch (addr) {
    284    case A_CONFIG:
    285        if (value & (R_CONFIG_CMP0CENTER_MASK | R_CONFIG_CMP1CENTER_MASK |
    286                     R_CONFIG_CMP2CENTER_MASK | R_CONFIG_CMP3CENTER_MASK)) {
    287            qemu_log_mask(LOG_UNIMP, "%s: CMPxCENTER is not supported\n",
    288                          __func__);
    289        }
    290
    291        if (value & (R_CONFIG_CMP0GANG_MASK | R_CONFIG_CMP1GANG_MASK |
    292                     R_CONFIG_CMP2GANG_MASK | R_CONFIG_CMP3GANG_MASK)) {
    293            qemu_log_mask(LOG_UNIMP, "%s: CMPxGANG is not supported\n",
    294                          __func__);
    295        }
    296
    297        if (value & (R_CONFIG_CMP0IP_MASK | R_CONFIG_CMP1IP_MASK |
    298                     R_CONFIG_CMP2IP_MASK | R_CONFIG_CMP3IP_MASK)) {
    299            qemu_log_mask(LOG_UNIMP, "%s: CMPxIP is not supported\n",
    300                          __func__);
    301        }
    302
    303        if (!(value & R_CONFIG_CMP0IP_MASK)) {
    304            qemu_irq_lower(s->irqs[0]);
    305        }
    306
    307        if (!(value & R_CONFIG_CMP1IP_MASK)) {
    308            qemu_irq_lower(s->irqs[1]);
    309        }
    310
    311        if (!(value & R_CONFIG_CMP2IP_MASK)) {
    312            qemu_irq_lower(s->irqs[2]);
    313        }
    314
    315        if (!(value & R_CONFIG_CMP3IP_MASK)) {
    316            qemu_irq_lower(s->irqs[3]);
    317        }
    318
    319        /*
    320         * If this write enables the timer increment
    321         * set the time when pwmcount was zero to be cur_time - pwmcount.
    322         * If this write disables the timer increment
    323         * convert back from pwmcount to the time in ticks
    324         * when pwmcount was zero.
    325         */
    326        if ((!HAS_PWM_EN_BITS(s->pwmcfg) && HAS_PWM_EN_BITS(value)) ||
    327            (HAS_PWM_EN_BITS(s->pwmcfg) && !HAS_PWM_EN_BITS(value))) {
    328            s->tick_offset = (now - s->tick_offset) & PWMCOUNT_MASK;
    329        }
    330
    331        s->pwmcfg = value;
    332        break;
    333    case A_COUNT:
    334        /* The guest changed the counter, updated the offset value. */
    335        new_offset = value;
    336
    337        if (HAS_PWM_EN_BITS(s->pwmcfg)) {
    338            new_offset = now - new_offset;
    339        }
    340
    341        s->tick_offset = new_offset;
    342        break;
    343    case A_PWMS:
    344        scale = sifive_pwm_compute_scale(s);
    345        new_offset = (((value & PWMCMP_MASK) << scale) & PWMCOUNT_MASK);
    346
    347        if (HAS_PWM_EN_BITS(s->pwmcfg)) {
    348            new_offset = now - new_offset;
    349        }
    350
    351        s->tick_offset = new_offset;
    352        break;
    353    case A_PWMCMP0:
    354        s->pwmcmp[0] = value & PWMCMP_MASK;
    355        break;
    356    case A_PWMCMP1:
    357        s->pwmcmp[1] = value & PWMCMP_MASK;
    358        break;
    359    case A_PWMCMP2:
    360        s->pwmcmp[2] = value & PWMCMP_MASK;
    361        break;
    362    case A_PWMCMP3:
    363        s->pwmcmp[3] = value & PWMCMP_MASK;
    364        break;
    365    default:
    366        qemu_log_mask(LOG_GUEST_ERROR,
    367                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
    368    }
    369
    370    /* Update the alarms to reflect possible updated values */
    371    sifive_pwm_set_alarms(s);
    372}
    373
    374static void sifive_pwm_reset(DeviceState *dev)
    375{
    376    SiFivePwmState *s = SIFIVE_PWM(dev);
    377    uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    378
    379    s->pwmcfg = 0x00000000;
    380    s->pwmcmp[0] = 0x00000000;
    381    s->pwmcmp[1] = 0x00000000;
    382    s->pwmcmp[2] = 0x00000000;
    383    s->pwmcmp[3] = 0x00000000;
    384
    385    s->tick_offset = sifive_pwm_ns_to_ticks(s, now);
    386}
    387
    388static const MemoryRegionOps sifive_pwm_ops = {
    389    .read = sifive_pwm_read,
    390    .write = sifive_pwm_write,
    391    .endianness = DEVICE_NATIVE_ENDIAN,
    392};
    393
    394static const VMStateDescription vmstate_sifive_pwm = {
    395    .name = TYPE_SIFIVE_PWM,
    396    .version_id = 1,
    397    .minimum_version_id = 1,
    398    .fields = (VMStateField[]) {
    399        VMSTATE_TIMER_ARRAY(timer, SiFivePwmState, 4),
    400        VMSTATE_UINT64(tick_offset, SiFivePwmState),
    401        VMSTATE_UINT32(pwmcfg, SiFivePwmState),
    402        VMSTATE_UINT32_ARRAY(pwmcmp, SiFivePwmState, 4),
    403        VMSTATE_END_OF_LIST()
    404    }
    405};
    406
    407static Property sifive_pwm_properties[] = {
    408    /* 0.5Ghz per spec after FSBL */
    409    DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState,
    410                       freq_hz, 500000000ULL),
    411    DEFINE_PROP_END_OF_LIST(),
    412};
    413
    414static void sifive_pwm_init(Object *obj)
    415{
    416    SiFivePwmState *s = SIFIVE_PWM(obj);
    417    int i;
    418
    419    for (i = 0; i < SIFIVE_PWM_IRQS; i++) {
    420        sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irqs[i]);
    421    }
    422
    423    memory_region_init_io(&s->mmio, obj, &sifive_pwm_ops, s,
    424                          TYPE_SIFIVE_PWM, 0x100);
    425    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
    426}
    427
    428static void sifive_pwm_realize(DeviceState *dev, Error **errp)
    429{
    430    SiFivePwmState *s = SIFIVE_PWM(dev);
    431
    432    timer_init_ns(&s->timer[0], QEMU_CLOCK_VIRTUAL,
    433                  sifive_pwm_interrupt_0, s);
    434
    435    timer_init_ns(&s->timer[1], QEMU_CLOCK_VIRTUAL,
    436                  sifive_pwm_interrupt_1, s);
    437
    438    timer_init_ns(&s->timer[2], QEMU_CLOCK_VIRTUAL,
    439                  sifive_pwm_interrupt_2, s);
    440
    441    timer_init_ns(&s->timer[3], QEMU_CLOCK_VIRTUAL,
    442                  sifive_pwm_interrupt_3, s);
    443}
    444
    445static void sifive_pwm_class_init(ObjectClass *klass, void *data)
    446{
    447    DeviceClass *dc = DEVICE_CLASS(klass);
    448
    449    dc->reset = sifive_pwm_reset;
    450    device_class_set_props(dc, sifive_pwm_properties);
    451    dc->vmsd = &vmstate_sifive_pwm;
    452    dc->realize = sifive_pwm_realize;
    453}
    454
    455static const TypeInfo sifive_pwm_info = {
    456    .name          = TYPE_SIFIVE_PWM,
    457    .parent        = TYPE_SYS_BUS_DEVICE,
    458    .instance_size = sizeof(SiFivePwmState),
    459    .instance_init = sifive_pwm_init,
    460    .class_init    = sifive_pwm_class_init,
    461};
    462
    463static void sifive_pwm_register_types(void)
    464{
    465    type_register_static(&sifive_pwm_info);
    466}
    467
    468type_init(sifive_pwm_register_types)