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
riscv_vstimer_cb(void * opaque)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
riscv_stimer_cb(void * opaque)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 */
riscv_timer_write_timecmp(CPURISCVState * env,QEMUTimer * timer,uint64_t timecmp,uint64_t delta,uint32_t timer_irq)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 */
riscv_timer_disable_timecmp(CPURISCVState * env,QEMUTimer * timer,uint32_t timer_irq)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 */
riscv_timer_stce_changed(CPURISCVState * env,bool is_m_mode,bool enable)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
riscv_timer_init(RISCVCPU * cpu)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