qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

time_helper.c (3465B)


      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 
     25 static 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 
     33 static 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  */
     43 void 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 
    100 void 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 }