]> git.proxmox.com Git - mirror_qemu.git/blob - target/riscv/time_helper.c
target/riscv: gdbstub: Do not generate CSR XML if Zicsr is disabled
[mirror_qemu.git] / target / riscv / time_helper.c
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, 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, 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 riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(1));
61 } else {
62 riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1));
63 }
64 return;
65 }
66
67 /* Clear the [VS|S]TIP bit in mip */
68 if (timer_irq == MIP_VSTIP) {
69 env->vstime_irq = 0;
70 riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(0));
71 } else {
72 riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0));
73 }
74
75 /*
76 * Sstc specification says the following about timer interrupt:
77 * "A supervisor timer interrupt becomes pending - as reflected in
78 * the STIP bit in the mip and sip registers - whenever time contains
79 * a value greater than or equal to stimecmp, treating the values
80 * as unsigned integers. Writes to stimecmp are guaranteed to be
81 * reflected in STIP eventually, but not necessarily immediately.
82 * The interrupt remains posted until stimecmp becomes greater
83 * than time - typically as a result of writing stimecmp."
84 *
85 * When timecmp = UINT64_MAX, the time CSR will eventually reach
86 * timecmp value but on next timer tick the time CSR will wrap-around
87 * and become zero which is less than UINT64_MAX. Now, the timer
88 * interrupt behaves like a level triggered interrupt so it will
89 * become 1 when time = timecmp = UINT64_MAX and next timer tick
90 * it will become 0 again because time = 0 < timecmp = UINT64_MAX.
91 *
92 * Based on above, we don't re-start the QEMU timer when timecmp
93 * equals UINT64_MAX.
94 */
95 if (timecmp == UINT64_MAX) {
96 return;
97 }
98
99 /* otherwise, set up the future timer interrupt */
100 diff = timecmp - rtc_r;
101 /* back to ns (note args switched in muldiv64) */
102 ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
103
104 /*
105 * check if ns_diff overflowed and check if the addition would potentially
106 * overflow
107 */
108 if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
109 ns_diff > INT64_MAX) {
110 next = INT64_MAX;
111 } else {
112 /*
113 * as it is very unlikely qemu_clock_get_ns will return a value
114 * greater than INT64_MAX, no additional check is needed for an
115 * unsigned integer overflow.
116 */
117 next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
118 /*
119 * if ns_diff is INT64_MAX next may still be outside the range
120 * of a signed integer.
121 */
122 next = MIN(next, INT64_MAX);
123 }
124
125 timer_mod(timer, next);
126 }
127
128 void riscv_timer_init(RISCVCPU *cpu)
129 {
130 CPURISCVState *env;
131
132 if (!cpu) {
133 return;
134 }
135
136 env = &cpu->env;
137 env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu);
138 env->stimecmp = 0;
139
140 env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu);
141 env->vstimecmp = 0;
142 }