[PATCH 6/9] target/riscv: replace env->rdtime_fn with a time source

Luc Michel posted 9 patches 1 week ago
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <dbarboza@ventanamicro.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
[PATCH 6/9] target/riscv: replace env->rdtime_fn with a time source
Posted by Luc Michel 1 week ago
Replace the env->rdtime_fn function pointer with an instance of the
RISCVCPUTimeSrcIf QOM interface.

This allows to remove the dependency on the ACLINT in the
riscv_timer_write_timecmp function:
   - This dependency was buggy because env->rdtime_fn_arg was an opaque
     pointer and was converted in riscv_timer_write_timecmp to a ACLINT
     without dynamic type check.
   - This will allow to have time sources provided by other devices than
     an ACLINT.

Signed-off-by: Luc Michel <luc.michel@amd.com>
---
 target/riscv/cpu.h         |  8 +++-----
 hw/intc/riscv_aclint.c     |  2 +-
 target/riscv/cpu_helper.c  |  7 -------
 target/riscv/csr.c         | 24 ++++++++++++------------
 target/riscv/time_helper.c | 15 +++++++++------
 5 files changed, 25 insertions(+), 31 deletions(-)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 36e7f100374..368b9e2532d 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -458,13 +458,12 @@ struct CPUArchState {
     struct CPUWatchpoint *cpu_watchpoint[RV_MAX_TRIGGERS];
     QEMUTimer *itrigger_timer[RV_MAX_TRIGGERS];
     int64_t last_icount;
     bool itrigger_enabled;
 
-    /* machine specific rdtime callback */
-    uint64_t (*rdtime_fn)(void *);
-    void *rdtime_fn_arg;
+    /* machine specific time source interface */
+    RISCVCPUTimeSrcIf *time_src;
 
     /* machine specific AIA ireg read-modify-write callback */
 #define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \
     ((((__xlen) & 0xff) << 24) | \
      (((__vgein) & 0x3f) << 20) | \
@@ -642,12 +641,11 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
 uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
                               uint64_t value);
 void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
 void riscv_cpu_interrupt(CPURISCVState *env);
 #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
-void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
-                             void *arg);
+void riscv_cpu_set_time_src(CPURISCVState *env, RISCVCPUTimeSrcIf *src);
 void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
                                    int (*rmw_fn)(void *arg,
                                                  target_ulong reg,
                                                  target_ulong *val,
                                                  target_ulong new_val,
diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
index 4a4449d9d2f..8d001a5eb20 100644
--- a/hw/intc/riscv_aclint.c
+++ b/hw/intc/riscv_aclint.c
@@ -418,11 +418,11 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size,
         if (!env) {
             g_free(cb);
             continue;
         }
         if (provide_rdtime) {
-            riscv_cpu_set_rdtime_fn(env, riscv_aclint_mtimer_get_ticks, dev);
+            riscv_cpu_set_time_src(env, RISCV_CPU_TIME_SRC_IF(dev));
         }
 
         cb->s = s;
         cb->num = i;
         s->timers[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL,
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index c4fb68b5de8..f642ec80b31 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -757,17 +757,10 @@ uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value)
     riscv_cpu_interrupt(env);
 
     return old;
 }
 
-void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
-                             void *arg)
-{
-    env->rdtime_fn = fn;
-    env->rdtime_fn_arg = arg;
-}
-
 void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
                                    int (*rmw_fn)(void *arg,
                                                  target_ulong reg,
                                                  target_ulong *val,
                                                  target_ulong new_val,
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 5c91658c3dc..583f646460c 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -572,11 +572,11 @@ static RISCVException sstateen(CPURISCVState *env, int csrno)
 
 static RISCVException sstc(CPURISCVState *env, int csrno)
 {
     bool hmode_check = false;
 
-    if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn) {
+    if (!riscv_cpu_cfg(env)->ext_sstc || !env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
     if ((csrno == CSR_VSTIMECMP) || (csrno == CSR_VSTIMECMPH)) {
         hmode_check = true;
@@ -1634,28 +1634,28 @@ static RISCVException read_scountovf(CPURISCVState *env, int csrno,
 static RISCVException read_time(CPURISCVState *env, int csrno,
                                 target_ulong *val)
 {
     uint64_t delta = env->virt_enabled ? env->htimedelta : 0;
 
-    if (!env->rdtime_fn) {
+    if (!env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
-    *val = env->rdtime_fn(env->rdtime_fn_arg) + delta;
+    *val = riscv_cpu_time_src_get_ticks(env->time_src) + delta;
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException read_timeh(CPURISCVState *env, int csrno,
                                  target_ulong *val)
 {
     uint64_t delta = env->virt_enabled ? env->htimedelta : 0;
 
-    if (!env->rdtime_fn) {
+    if (!env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
-    *val = (env->rdtime_fn(env->rdtime_fn_arg) + delta) >> 32;
+    *val = (riscv_cpu_time_src_get_ticks(env->time_src) + delta) >> 32;
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException read_vstimecmp(CPURISCVState *env, int csrno,
                                      target_ulong *val)
@@ -4965,60 +4965,60 @@ static RISCVException write_hgatp(CPURISCVState *env, int csrno,
 }
 
 static RISCVException read_htimedelta(CPURISCVState *env, int csrno,
                                       target_ulong *val)
 {
-    if (!env->rdtime_fn) {
+    if (!env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
     *val = env->htimedelta;
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException write_htimedelta(CPURISCVState *env, int csrno,
                                        target_ulong val, uintptr_t ra)
 {
-    if (!env->rdtime_fn) {
+    if (!env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
     if (riscv_cpu_mxl(env) == MXL_RV32) {
         env->htimedelta = deposit64(env->htimedelta, 0, 32, (uint64_t)val);
     } else {
         env->htimedelta = val;
     }
 
-    if (riscv_cpu_cfg(env)->ext_sstc && env->rdtime_fn) {
+    if (riscv_cpu_cfg(env)->ext_sstc) {
         riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp,
                                   env->htimedelta, MIP_VSTIP);
     }
 
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException read_htimedeltah(CPURISCVState *env, int csrno,
                                        target_ulong *val)
 {
-    if (!env->rdtime_fn) {
+    if (!env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
     *val = env->htimedelta >> 32;
     return RISCV_EXCP_NONE;
 }
 
 static RISCVException write_htimedeltah(CPURISCVState *env, int csrno,
                                         target_ulong val, uintptr_t ra)
 {
-    if (!env->rdtime_fn) {
+    if (!env->time_src) {
         return RISCV_EXCP_ILLEGAL_INST;
     }
 
     env->htimedelta = deposit64(env->htimedelta, 32, 32, (uint64_t)val);
 
-    if (riscv_cpu_cfg(env)->ext_sstc && env->rdtime_fn) {
+    if (riscv_cpu_cfg(env)->ext_sstc) {
         riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp,
                                   env->htimedelta, MIP_VSTIP);
     }
 
     return RISCV_EXCP_NONE;
@@ -5825,11 +5825,11 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_CYCLEH]   = { "cycleh",   ctr32,  read_hpmcounterh },
     [CSR_INSTRETH] = { "instreth", ctr32,  read_hpmcounterh },
 
     /*
      * In privileged mode, the monitor will have to emulate TIME CSRs only if
-     * rdtime callback is not provided by machine/platform emulation.
+     * CPU time source interface is not provided by machine/platform emulation.
      */
     [CSR_TIME]  = { "time",  ctr,   read_time  },
     [CSR_TIMEH] = { "timeh", ctr32, read_timeh },
 
     /* Crypto Extension */
diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c
index dc0777607ab..7b493b7a233 100644
--- a/target/riscv/time_helper.c
+++ b/target/riscv/time_helper.c
@@ -17,11 +17,10 @@
  */
 
 #include "qemu/osdep.h"
 #include "cpu_bits.h"
 #include "time_helper.h"
-#include "hw/intc/riscv_aclint.h"
 
 static void riscv_vstimer_cb(void *opaque)
 {
     RISCVCPU *cpu = opaque;
     CPURISCVState *env = &cpu->env;
@@ -42,28 +41,27 @@ static void riscv_stimer_cb(void *opaque)
 void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer,
                                uint64_t timecmp, uint64_t delta,
                                uint32_t timer_irq)
 {
     uint64_t diff, ns_diff, next;
-    RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg;
     uint32_t timebase_freq;
     uint64_t rtc_r;
 
-    if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn ||
-        !env->rdtime_fn_arg || !get_field(env->menvcfg, MENVCFG_STCE)) {
+    if (!riscv_cpu_cfg(env)->ext_sstc || !env->time_src ||
+        !get_field(env->menvcfg, MENVCFG_STCE)) {
         /* S/VS Timer IRQ depends on sstc extension, rdtime_fn(), and STCE. */
         return;
     }
 
     if (timer_irq == MIP_VSTIP &&
         (!riscv_has_ext(env, RVH) || !get_field(env->henvcfg, HENVCFG_STCE))) {
         /* VS Timer IRQ also depends on RVH and henvcfg.STCE. */
         return;
     }
 
-    timebase_freq = mtimer->timebase_freq;
-    rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta;
+    timebase_freq = riscv_cpu_time_src_get_tick_freq(env->time_src);
+    rtc_r = riscv_cpu_time_src_get_ticks(env->time_src) + delta;
 
     if (timecmp <= rtc_r) {
         /*
          * If we're setting an stimecmp value in the "past",
          * immediately raise the timer interrupt
@@ -199,10 +197,15 @@ void riscv_timer_init(RISCVCPU *cpu)
 
     env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu);
     env->vstimecmp = 0;
 }
 
+void riscv_cpu_set_time_src(CPURISCVState *env, RISCVCPUTimeSrcIf *src)
+{
+    env->time_src = src;
+}
+
 static const TypeInfo riscv_cpu_time_src_if_info = {
     .name = TYPE_RISCV_CPU_TIME_SRC_IF,
     .parent = TYPE_INTERFACE,
     .class_size = sizeof(RISCVCPUTimeSrcIfClass),
 };
-- 
2.51.0
Re: [PATCH 6/9] target/riscv: replace env->rdtime_fn with a time source
Posted by Philippe Mathieu-Daudé 5 days, 1 hour ago
On 7/11/25 11:23, Luc Michel wrote:
> Replace the env->rdtime_fn function pointer with an instance of the
> RISCVCPUTimeSrcIf QOM interface.
> 
> This allows to remove the dependency on the ACLINT in the
> riscv_timer_write_timecmp function:
>     - This dependency was buggy because env->rdtime_fn_arg was an opaque
>       pointer and was converted in riscv_timer_write_timecmp to a ACLINT
>       without dynamic type check.
>     - This will allow to have time sources provided by other devices than
>       an ACLINT.
> 
> Signed-off-by: Luc Michel <luc.michel@amd.com>
> ---
>   target/riscv/cpu.h         |  8 +++-----
>   hw/intc/riscv_aclint.c     |  2 +-
>   target/riscv/cpu_helper.c  |  7 -------
>   target/riscv/csr.c         | 24 ++++++++++++------------
>   target/riscv/time_helper.c | 15 +++++++++------
>   5 files changed, 25 insertions(+), 31 deletions(-)


> +void riscv_cpu_set_time_src(CPURISCVState *env, RISCVCPUTimeSrcIf *src)
> +{
> +    env->time_src = src;

Worth asserting time_src is NULL? Regardless, good cleanup:

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>

> +}