qemu/target/riscv/time_helper.c
<<
>>
Prefs
   1/*
   2 * RISC-V timer helper implementation.
   3 *
   4 * Copyright (c) 2022 Rivos Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2 or later, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License along with
  16 * this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include "qemu/osdep.h"
  20#include "qemu/log.h"
  21#include "cpu_bits.h"
  22#include "time_helper.h"
  23#include "hw/intc/riscv_aclint.h"
  24
  25static void riscv_vstimer_cb(void *opaque)
  26{
  27    RISCVCPU *cpu = opaque;
  28    CPURISCVState *env = &cpu->env;
  29    env->vstime_irq = 1;
  30    riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1));
  31}
  32
  33static void riscv_stimer_cb(void *opaque)
  34{
  35    RISCVCPU *cpu = opaque;
  36    riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1));
  37}
  38
  39/*
  40 * Called when timecmp is written to update the QEMU timer or immediately
  41 * trigger timer interrupt if mtimecmp <= current timer value.
  42 */
  43void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer,
  44                               uint64_t timecmp, uint64_t delta,
  45                               uint32_t timer_irq)
  46{
  47    uint64_t diff, ns_diff, next;
  48    CPURISCVState *env = &cpu->env;
  49    RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg;
  50    uint32_t timebase_freq = mtimer->timebase_freq;
  51    uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta;
  52
  53    if (timecmp <= rtc_r) {
  54        /*
  55         * If we're setting an stimecmp value in the "past",
  56         * immediately raise the timer interrupt
  57         */
  58        if (timer_irq == MIP_VSTIP) {
  59            env->vstime_irq = 1;
  60        }
  61        riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1));
  62        return;
  63    }
  64
  65    if (timer_irq == MIP_VSTIP) {
  66        env->vstime_irq = 0;
  67    }
  68    /* Clear the [V]STIP bit in mip */
  69    riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0));
  70
  71    /* otherwise, set up the future timer interrupt */
  72    diff = timecmp - rtc_r;
  73    /* back to ns (note args switched in muldiv64) */
  74    ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
  75
  76    /*
  77     * check if ns_diff overflowed and check if the addition would potentially
  78     * overflow
  79     */
  80    if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
  81        ns_diff > INT64_MAX) {
  82        next = INT64_MAX;
  83    } else {
  84        /*
  85         * as it is very unlikely qemu_clock_get_ns will return a value
  86         * greater than INT64_MAX, no additional check is needed for an
  87         * unsigned integer overflow.
  88         */
  89        next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
  90        /*
  91         * if ns_diff is INT64_MAX next may still be outside the range
  92         * of a signed integer.
  93         */
  94        next = MIN(next, INT64_MAX);
  95    }
  96
  97    timer_mod(timer, next);
  98}
  99
 100void riscv_timer_init(RISCVCPU *cpu)
 101{
 102    CPURISCVState *env;
 103
 104    if (!cpu) {
 105        return;
 106    }
 107
 108    env = &cpu->env;
 109    env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu);
 110    env->stimecmp = 0;
 111
 112    env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu);
 113    env->vstimecmp = 0;
 114}
 115