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

renesas_tmr.c (14238B)


      1/*
      2 * Renesas 8bit timer
      3 *
      4 * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
      5 *            (Rev.1.40 R01UH0033EJ0140)
      6 *
      7 * Copyright (c) 2019 Yoshinori Sato
      8 *
      9 * SPDX-License-Identifier: GPL-2.0-or-later
     10 *
     11 * This program is free software; you can redistribute it and/or modify it
     12 * under the terms and conditions of the GNU General Public License,
     13 * version 2 or later, as published by the Free Software Foundation.
     14 *
     15 * This program is distributed in the hope it will be useful, but WITHOUT
     16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     17 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     18 * more details.
     19 *
     20 * You should have received a copy of the GNU General Public License along with
     21 * this program.  If not, see <http://www.gnu.org/licenses/>.
     22 */
     23
     24#include "qemu/osdep.h"
     25#include "qemu/log.h"
     26#include "hw/irq.h"
     27#include "hw/registerfields.h"
     28#include "hw/qdev-properties.h"
     29#include "hw/timer/renesas_tmr.h"
     30#include "migration/vmstate.h"
     31
     32REG8(TCR, 0)
     33  FIELD(TCR, CCLR,  3, 2)
     34  FIELD(TCR, OVIE,  5, 1)
     35  FIELD(TCR, CMIEA, 6, 1)
     36  FIELD(TCR, CMIEB, 7, 1)
     37REG8(TCSR, 2)
     38  FIELD(TCSR, OSA,  0, 2)
     39  FIELD(TCSR, OSB,  2, 2)
     40  FIELD(TCSR, ADTE, 4, 2)
     41REG8(TCORA, 4)
     42REG8(TCORB, 6)
     43REG8(TCNT, 8)
     44REG8(TCCR, 10)
     45  FIELD(TCCR, CKS,   0, 3)
     46  FIELD(TCCR, CSS,   3, 2)
     47  FIELD(TCCR, TMRIS, 7, 1)
     48
     49#define CSS_EXTERNAL  0x00
     50#define CSS_INTERNAL  0x01
     51#define CSS_INVALID   0x02
     52#define CSS_CASCADING 0x03
     53#define CCLR_A    0x01
     54#define CCLR_B    0x02
     55
     56static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
     57
     58static uint8_t concat_reg(uint8_t *reg)
     59{
     60    return (reg[0] << 8) | reg[1];
     61}
     62
     63static void update_events(RTMRState *tmr, int ch)
     64{
     65    uint16_t diff[TMR_NR_EVENTS], min;
     66    int64_t next_time;
     67    int i, event;
     68
     69    if (tmr->tccr[ch] == 0) {
     70        return ;
     71    }
     72    if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
     73        /* external clock mode */
     74        /* event not happened */
     75        return ;
     76    }
     77    if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CSS_CASCADING) {
     78        /* cascading mode */
     79        if (ch == 1) {
     80            tmr->next[ch] = none;
     81            return ;
     82        }
     83        diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
     84        diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
     85        diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
     86    } else {
     87        /* separate mode */
     88        diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
     89        diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
     90        diff[ovi] = 0x100 - tmr->tcnt[ch];
     91    }
     92    /* Search for the most recently occurring event. */
     93    for (event = 0, min = diff[0], i = 1; i < none; i++) {
     94        if (min > diff[i]) {
     95            event = i;
     96            min = diff[i];
     97        }
     98    }
     99    tmr->next[ch] = event;
    100    next_time = diff[event];
    101    next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
    102    next_time *= NANOSECONDS_PER_SECOND;
    103    next_time /= tmr->input_freq;
    104    next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    105    timer_mod(&tmr->timer[ch], next_time);
    106}
    107
    108static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
    109{
    110    int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
    111    int et;
    112
    113    tmr->div_round[ch] += delta;
    114    if (divrate > 0) {
    115        et = tmr->div_round[ch] / divrate;
    116        tmr->div_round[ch] %= divrate;
    117    } else {
    118        /* disble clock. so no update */
    119        et = 0;
    120    }
    121    return et;
    122}
    123
    124static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
    125{
    126    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    127    int elapsed, ovf = 0;
    128    uint16_t tcnt[2];
    129    uint32_t ret;
    130
    131    delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
    132    if (delta > 0) {
    133        tmr->tick = now;
    134
    135        switch (FIELD_EX8(tmr->tccr[1], TCCR, CSS)) {
    136        case CSS_INTERNAL:
    137            /* timer1 count update */
    138            elapsed = elapsed_time(tmr, 1, delta);
    139            if (elapsed >= 0x100) {
    140                ovf = elapsed >> 8;
    141            }
    142            tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
    143            break;
    144        case CSS_INVALID: /* guest error to have set this */
    145        case CSS_EXTERNAL: /* QEMU doesn't implement these */
    146        case CSS_CASCADING:
    147            tcnt[1] = tmr->tcnt[1];
    148            break;
    149        default:
    150            g_assert_not_reached();
    151        }
    152        switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
    153        case CSS_INTERNAL:
    154            elapsed = elapsed_time(tmr, 0, delta);
    155            tcnt[0] = tmr->tcnt[0] + elapsed;
    156            break;
    157        case CSS_CASCADING:
    158            tcnt[0] = tmr->tcnt[0] + ovf;
    159            break;
    160        case CSS_INVALID: /* guest error to have set this */
    161        case CSS_EXTERNAL: /* QEMU doesn't implement this */
    162            tcnt[0] = tmr->tcnt[0];
    163            break;
    164        default:
    165            g_assert_not_reached();
    166        }
    167    } else {
    168        tcnt[0] = tmr->tcnt[0];
    169        tcnt[1] = tmr->tcnt[1];
    170    }
    171    if (size == 1) {
    172        return tcnt[ch];
    173    } else {
    174        ret = 0;
    175        ret = deposit32(ret, 0, 8, tcnt[1]);
    176        ret = deposit32(ret, 8, 8, tcnt[0]);
    177        return ret;
    178    }
    179}
    180
    181static uint8_t read_tccr(uint8_t r)
    182{
    183    uint8_t tccr = 0;
    184    tccr = FIELD_DP8(tccr, TCCR, TMRIS,
    185                     FIELD_EX8(r, TCCR, TMRIS));
    186    tccr = FIELD_DP8(tccr, TCCR, CSS,
    187                     FIELD_EX8(r, TCCR, CSS));
    188    tccr = FIELD_DP8(tccr, TCCR, CKS,
    189                     FIELD_EX8(r, TCCR, CKS));
    190    return tccr;
    191}
    192
    193static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
    194{
    195    RTMRState *tmr = opaque;
    196    int ch = addr & 1;
    197    uint64_t ret;
    198
    199    if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
    200        qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
    201                                       HWADDR_PRIX "\n",
    202                      addr);
    203        return UINT64_MAX;
    204    }
    205    switch (addr & 0x0e) {
    206    case A_TCR:
    207        ret = 0;
    208        ret = FIELD_DP8(ret, TCR, CCLR,
    209                        FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
    210        ret = FIELD_DP8(ret, TCR, OVIE,
    211                        FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
    212        ret = FIELD_DP8(ret, TCR, CMIEA,
    213                        FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
    214        ret = FIELD_DP8(ret, TCR, CMIEB,
    215                        FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
    216        return ret;
    217    case A_TCSR:
    218        ret = 0;
    219        ret = FIELD_DP8(ret, TCSR, OSA,
    220                        FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
    221        ret = FIELD_DP8(ret, TCSR, OSB,
    222                        FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
    223        switch (ch) {
    224        case 0:
    225            ret = FIELD_DP8(ret, TCSR, ADTE,
    226                            FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
    227            break;
    228        case 1: /* CH1 ADTE unimplement always 1 */
    229            ret = FIELD_DP8(ret, TCSR, ADTE, 1);
    230            break;
    231        }
    232        return ret;
    233    case A_TCORA:
    234        if (size == 1) {
    235            return tmr->tcora[ch];
    236        } else if (ch == 0) {
    237            return concat_reg(tmr->tcora);
    238        }
    239        /* fall through */
    240    case A_TCORB:
    241        if (size == 1) {
    242            return tmr->tcorb[ch];
    243        } else {
    244            return concat_reg(tmr->tcorb);
    245        }
    246    case A_TCNT:
    247        return read_tcnt(tmr, size, ch);
    248    case A_TCCR:
    249        if (size == 1) {
    250            return read_tccr(tmr->tccr[ch]);
    251        } else {
    252            return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
    253        }
    254    default:
    255        qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
    256                                 " not implemented\n",
    257                      addr);
    258        break;
    259    }
    260    return UINT64_MAX;
    261}
    262
    263static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
    264                            uint8_t *reg, uint64_t val)
    265{
    266    if (size == 1) {
    267        reg[ch] = val;
    268        update_events(tmr, ch);
    269    } else {
    270        reg[0] = extract32(val, 8, 8);
    271        reg[1] = extract32(val, 0, 8);
    272        update_events(tmr, 0);
    273        update_events(tmr, 1);
    274    }
    275}
    276
    277static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
    278{
    279    RTMRState *tmr = opaque;
    280    int ch = addr & 1;
    281
    282    if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
    283        qemu_log_mask(LOG_GUEST_ERROR,
    284                      "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
    285                      addr);
    286        return;
    287    }
    288    switch (addr & 0x0e) {
    289    case A_TCR:
    290        tmr->tcr[ch] = val;
    291        break;
    292    case A_TCSR:
    293        tmr->tcsr[ch] = val;
    294        break;
    295    case A_TCORA:
    296        tmr_write_count(tmr, ch, size, tmr->tcora, val);
    297        break;
    298    case A_TCORB:
    299        tmr_write_count(tmr, ch, size, tmr->tcorb, val);
    300        break;
    301    case A_TCNT:
    302        tmr_write_count(tmr, ch, size, tmr->tcnt, val);
    303        break;
    304    case A_TCCR:
    305        tmr_write_count(tmr, ch, size, tmr->tccr, val);
    306        break;
    307    default:
    308        qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
    309                                 " not implemented\n",
    310                      addr);
    311        break;
    312    }
    313}
    314
    315static const MemoryRegionOps tmr_ops = {
    316    .write = tmr_write,
    317    .read  = tmr_read,
    318    .endianness = DEVICE_LITTLE_ENDIAN,
    319    .impl = {
    320        .min_access_size = 1,
    321        .max_access_size = 2,
    322    },
    323    .valid = {
    324        .min_access_size = 1,
    325        .max_access_size = 2,
    326    },
    327};
    328
    329static void timer_events(RTMRState *tmr, int ch);
    330
    331static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
    332                        uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
    333{
    334    uint16_t ret = tcnt;
    335
    336    switch (tmr->next[ch]) {
    337    case none:
    338        break;
    339    case cmia:
    340        if (tcnt >= tcora) {
    341            if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
    342                ret = tcnt - tcora;
    343            }
    344            if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
    345                qemu_irq_pulse(tmr->cmia[ch]);
    346            }
    347            if (sz == 8 && ch == 0 &&
    348                FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CSS_CASCADING) {
    349                tmr->tcnt[1]++;
    350                timer_events(tmr, 1);
    351            }
    352        }
    353        break;
    354    case cmib:
    355        if (tcnt >= tcorb) {
    356            if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
    357                ret = tcnt - tcorb;
    358            }
    359            if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
    360                qemu_irq_pulse(tmr->cmib[ch]);
    361            }
    362        }
    363        break;
    364    case ovi:
    365        if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
    366            qemu_irq_pulse(tmr->ovi[ch]);
    367        }
    368        break;
    369    default:
    370        g_assert_not_reached();
    371    }
    372    return ret;
    373}
    374
    375static void timer_events(RTMRState *tmr, int ch)
    376{
    377    uint16_t tcnt;
    378
    379    tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
    380    if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CSS_CASCADING) {
    381        tmr->tcnt[ch] = issue_event(tmr, ch, 8,
    382                                    tmr->tcnt[ch],
    383                                    tmr->tcora[ch],
    384                                    tmr->tcorb[ch]) & 0xff;
    385    } else {
    386        if (ch == 1) {
    387            return ;
    388        }
    389        tcnt = issue_event(tmr, ch, 16,
    390                           concat_reg(tmr->tcnt),
    391                           concat_reg(tmr->tcora),
    392                           concat_reg(tmr->tcorb));
    393        tmr->tcnt[0] = (tcnt >> 8) & 0xff;
    394        tmr->tcnt[1] = tcnt & 0xff;
    395    }
    396    update_events(tmr, ch);
    397}
    398
    399static void timer_event0(void *opaque)
    400{
    401    RTMRState *tmr = opaque;
    402
    403    timer_events(tmr, 0);
    404}
    405
    406static void timer_event1(void *opaque)
    407{
    408    RTMRState *tmr = opaque;
    409
    410    timer_events(tmr, 1);
    411}
    412
    413static void rtmr_reset(DeviceState *dev)
    414{
    415    RTMRState *tmr = RTMR(dev);
    416    tmr->tcr[0]   = tmr->tcr[1]   = 0x00;
    417    tmr->tcsr[0]  = 0x00;
    418    tmr->tcsr[1]  = 0x10;
    419    tmr->tcnt[0]  = tmr->tcnt[1]  = 0x00;
    420    tmr->tcora[0] = tmr->tcora[1] = 0xff;
    421    tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
    422    tmr->tccr[0]  = tmr->tccr[1]  = 0x00;
    423    tmr->next[0]  = tmr->next[1]  = none;
    424    tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    425}
    426
    427static void rtmr_init(Object *obj)
    428{
    429    SysBusDevice *d = SYS_BUS_DEVICE(obj);
    430    RTMRState *tmr = RTMR(obj);
    431    int i;
    432
    433    memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
    434                          tmr, "renesas-tmr", 0x10);
    435    sysbus_init_mmio(d, &tmr->memory);
    436
    437    for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
    438        sysbus_init_irq(d, &tmr->cmia[i]);
    439        sysbus_init_irq(d, &tmr->cmib[i]);
    440        sysbus_init_irq(d, &tmr->ovi[i]);
    441    }
    442    timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
    443    timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
    444}
    445
    446static const VMStateDescription vmstate_rtmr = {
    447    .name = "rx-tmr",
    448    .version_id = 1,
    449    .minimum_version_id = 1,
    450    .fields = (VMStateField[]) {
    451        VMSTATE_INT64(tick, RTMRState),
    452        VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
    453        VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
    454        VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
    455        VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
    456        VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
    457        VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
    458        VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
    459        VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
    460        VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
    461        VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
    462        VMSTATE_END_OF_LIST()
    463    }
    464};
    465
    466static Property rtmr_properties[] = {
    467    DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
    468    DEFINE_PROP_END_OF_LIST(),
    469};
    470
    471static void rtmr_class_init(ObjectClass *klass, void *data)
    472{
    473    DeviceClass *dc = DEVICE_CLASS(klass);
    474
    475    dc->vmsd = &vmstate_rtmr;
    476    dc->reset = rtmr_reset;
    477    device_class_set_props(dc, rtmr_properties);
    478}
    479
    480static const TypeInfo rtmr_info = {
    481    .name = TYPE_RENESAS_TMR,
    482    .parent = TYPE_SYS_BUS_DEVICE,
    483    .instance_size = sizeof(RTMRState),
    484    .instance_init = rtmr_init,
    485    .class_init = rtmr_class_init,
    486};
    487
    488static void rtmr_register_types(void)
    489{
    490    type_register_static(&rtmr_info);
    491}
    492
    493type_init(rtmr_register_types)