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(env, 0, 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->env, 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(CPURISCVState *env, QEMUTimer *timer, 44 uint64_t timecmp, uint64_t delta, 45 uint32_t timer_irq) 46 { 47 uint64_t diff, ns_diff, next; 48 RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; 49 uint32_t timebase_freq; 50 uint64_t rtc_r; 51 52 if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn || 53 !env->rdtime_fn_arg || !get_field(env->menvcfg, MENVCFG_STCE)) { 54 /* S/VS Timer IRQ depends on sstc extension, rdtime_fn(), and STCE. */ 55 return; 56 } 57 58 if (timer_irq == MIP_VSTIP && 59 (!riscv_has_ext(env, RVH) || !get_field(env->henvcfg, HENVCFG_STCE))) { 60 /* VS Timer IRQ also depends on RVH and henvcfg.STCE. */ 61 return; 62 } 63 64 timebase_freq = mtimer->timebase_freq; 65 rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; 66 67 if (timecmp <= rtc_r) { 68 /* 69 * If we're setting an stimecmp value in the "past", 70 * immediately raise the timer interrupt 71 */ 72 if (timer_irq == MIP_VSTIP) { 73 env->vstime_irq = 1; 74 riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1)); 75 } else { 76 riscv_cpu_update_mip(env, MIP_STIP, BOOL_TO_MASK(1)); 77 } 78 return; 79 } 80 81 /* Clear the [VS|S]TIP bit in mip */ 82 if (timer_irq == MIP_VSTIP) { 83 env->vstime_irq = 0; 84 riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0)); 85 } else { 86 riscv_cpu_update_mip(env, timer_irq, BOOL_TO_MASK(0)); 87 } 88 89 /* 90 * Sstc specification says the following about timer interrupt: 91 * "A supervisor timer interrupt becomes pending - as reflected in 92 * the STIP bit in the mip and sip registers - whenever time contains 93 * a value greater than or equal to stimecmp, treating the values 94 * as unsigned integers. Writes to stimecmp are guaranteed to be 95 * reflected in STIP eventually, but not necessarily immediately. 96 * The interrupt remains posted until stimecmp becomes greater 97 * than time - typically as a result of writing stimecmp." 98 * 99 * When timecmp = UINT64_MAX, the time CSR will eventually reach 100 * timecmp value but on next timer tick the time CSR will wrap-around 101 * and become zero which is less than UINT64_MAX. Now, the timer 102 * interrupt behaves like a level triggered interrupt so it will 103 * become 1 when time = timecmp = UINT64_MAX and next timer tick 104 * it will become 0 again because time = 0 < timecmp = UINT64_MAX. 105 * 106 * Based on above, we don't re-start the QEMU timer when timecmp 107 * equals UINT64_MAX. 108 */ 109 if (timecmp == UINT64_MAX) { 110 timer_del(timer); 111 return; 112 } 113 114 /* otherwise, set up the future timer interrupt */ 115 diff = timecmp - rtc_r; 116 /* back to ns (note args switched in muldiv64) */ 117 ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); 118 119 /* 120 * check if ns_diff overflowed and check if the addition would potentially 121 * overflow 122 */ 123 if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || 124 ns_diff > INT64_MAX) { 125 next = INT64_MAX; 126 } else { 127 /* 128 * as it is very unlikely qemu_clock_get_ns will return a value 129 * greater than INT64_MAX, no additional check is needed for an 130 * unsigned integer overflow. 131 */ 132 next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; 133 /* 134 * if ns_diff is INT64_MAX next may still be outside the range 135 * of a signed integer. 136 */ 137 next = MIN(next, INT64_MAX); 138 } 139 140 timer_mod(timer, next); 141 } 142 143 /* 144 * When disabling xenvcfg.STCE, the S/VS Timer may be disabled at the same time. 145 * It is safe to call this function regardless of whether the timer has been 146 * deleted or not. timer_del() will do nothing if the timer has already 147 * been deleted. 148 */ 149 static void riscv_timer_disable_timecmp(CPURISCVState *env, QEMUTimer *timer, 150 uint32_t timer_irq) 151 { 152 /* Disable S-mode Timer IRQ and HW-based STIP */ 153 if ((timer_irq == MIP_STIP) && !get_field(env->menvcfg, MENVCFG_STCE)) { 154 riscv_cpu_update_mip(env, timer_irq, BOOL_TO_MASK(0)); 155 timer_del(timer); 156 return; 157 } 158 159 /* Disable VS-mode Timer IRQ and HW-based VSTIP */ 160 if ((timer_irq == MIP_VSTIP) && 161 (!get_field(env->menvcfg, MENVCFG_STCE) || 162 !get_field(env->henvcfg, HENVCFG_STCE))) { 163 env->vstime_irq = 0; 164 riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0)); 165 timer_del(timer); 166 return; 167 } 168 } 169 170 /* Enable or disable S/VS-mode Timer when xenvcfg.STCE is changed */ 171 void riscv_timer_stce_changed(CPURISCVState *env, bool is_m_mode, bool enable) 172 { 173 if (enable) { 174 riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, 175 env->htimedelta, MIP_VSTIP); 176 } else { 177 riscv_timer_disable_timecmp(env, env->vstimer, MIP_VSTIP); 178 } 179 180 if (is_m_mode) { 181 if (enable) { 182 riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); 183 } else { 184 riscv_timer_disable_timecmp(env, env->stimer, MIP_STIP); 185 } 186 } 187 } 188 189 void riscv_timer_init(RISCVCPU *cpu) 190 { 191 CPURISCVState *env; 192 193 if (!cpu) { 194 return; 195 } 196 197 env = &cpu->env; 198 env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); 199 env->stimecmp = 0; 200 201 env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu); 202 env->vstimecmp = 0; 203 } 204