On 8/18/2026 6:31 PM, Brian Cain wrote:
> In system mode, route accesses to the qtimer-backed global
> TIMERLO/TIMERHI so guest reads see a live, monotonically increasing
> timer.
> 
> In user mode we derive it from QEMU_CLOCK_VIRTUAL.
> 
> Signed-off-by: Brian Cain <[email protected]>
> ---
>  target/hexagon/helper.h     |  4 ++++
>  target/hexagon/genptr.c     | 29 +++++++++++++++++++++++++++++
>  target/hexagon/op_helper.c  | 21 +++++++++++++++++++++
>  tests/tcg/hexagon/reg_mut.c | 11 ++++++++---
>  4 files changed, 62 insertions(+), 3 deletions(-)
> 
> diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
> index 78dc28ca9e5..e39afd623b5 100644
> --- a/target/hexagon/helper.h
> +++ b/target/hexagon/helper.h
> @@ -113,6 +113,10 @@ DEF_HELPER_FLAGS_4(gvec_sabsdiff_w, TCG_CALL_NO_RWG, 
> void, ptr, ptr, ptr, i32)
>  DEF_HELPER_FLAGS_4(gvec_uabsdiff_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, 
> i32)
>  DEF_HELPER_FLAGS_4(gvec_uabsdiff_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, 
> i32)
>  
> +#if defined(CONFIG_USER_ONLY)
> +DEF_HELPER_FLAGS_0(utimer, TCG_CALL_NO_RWG, i64)
> +#endif
> +
>  #if !defined(CONFIG_USER_ONLY)
>  DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32)
>  DEF_HELPER_2(swi, void, env, i32)
> diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
> index 2a98b13b714..282a697d190 100644
> --- a/target/hexagon/genptr.c
> +++ b/target/hexagon/genptr.c
> @@ -411,6 +411,23 @@ static inline void gen_read_ctrl_reg(DisasContext *ctx, 
> const int reg_num,
>      } else if (reg_num == HEX_REG_QEMU_HVX_CNT) {
>          tcg_gen_addi_tl(dest, hex_gpr[HEX_REG_QEMU_HVX_CNT],
>                          ctx->num_hvx_insns);
> +#ifndef CONFIG_USER_ONLY
> +    } else if (reg_num == HEX_REG_UTIMERLO) {
> +        gen_helper_sreg_read(dest, tcg_env,
> +                             tcg_constant_i32(HEX_SREG_TIMERLO));
> +    } else if (reg_num == HEX_REG_UTIMERHI) {
> +        gen_helper_sreg_read(dest, tcg_env,
> +                             tcg_constant_i32(HEX_SREG_TIMERHI));
> +#else
> +    } else if (reg_num == HEX_REG_UTIMERLO) {
> +        TCGv_i64 utimer = tcg_temp_new_i64();
> +        gen_helper_utimer(utimer);
> +        tcg_gen_extrl_i64_i32(dest, utimer);
> +    } else if (reg_num == HEX_REG_UTIMERHI) {
> +        TCGv_i64 utimer = tcg_temp_new_i64();
> +        gen_helper_utimer(utimer);
> +        tcg_gen_extrh_i64_i32(dest, utimer);
> +#endif
>      } else {
>          tcg_gen_mov_tl(dest, hex_gpr[reg_num]);
>      }
> @@ -439,6 +456,18 @@ static inline void gen_read_ctrl_reg_pair(DisasContext 
> *ctx, const int reg_num,
>          tcg_gen_addi_tl(hvx_cnt, hex_gpr[HEX_REG_QEMU_HVX_CNT],
>                          ctx->num_hvx_insns);
>          tcg_gen_concat_i32_i64(dest, hvx_cnt, hex_gpr[reg_num + 1]);
> +#ifndef CONFIG_USER_ONLY
> +    } else if (reg_num == HEX_REG_UTIMERLO) {
> +        TCGv lo = tcg_temp_new();
> +        TCGv hi = tcg_temp_new();
> +        gen_helper_sreg_read(lo, tcg_env, 
> tcg_constant_i32(HEX_SREG_TIMERLO));
> +        gen_helper_sreg_read(hi, tcg_env, 
> tcg_constant_i32(HEX_SREG_TIMERHI));
> +        tcg_gen_concat_i32_i64(dest, lo, hi);
> +#else
> +    } else if (reg_num == HEX_REG_UTIMERLO) {
> +        /* One helper call, so the pair is a coherent 64-bit snapshot. */
> +        gen_helper_utimer(dest);
> +#endif
>      } else {
>          tcg_gen_concat_i32_i64(dest,
>              hex_gpr[reg_num],

Out of the scope of this patch, but seems like we could use a switch
instead of if/else ladder.

> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
> index 2cea1927263..df31ed2488a 100644
> --- a/target/hexagon/op_helper.c
> +++ b/target/hexagon/op_helper.c
> @@ -37,6 +37,9 @@
>  #include "cpu_helper.h"
>  #include "tcg/tcg-gvec-desc.h"
>  #include "translate.h"
> +#ifdef CONFIG_USER_ONLY
> +#include "qemu/timer.h"
> +#endif
>  #ifndef CONFIG_USER_ONLY
>  #include "hw/hexagon/hexagon_globalreg.h"
>  #include "hex_mmu.h"
> @@ -46,6 +49,24 @@
>  #include "hexswi.h"
>  #endif
>  
> +#ifdef CONFIG_USER_ONLY
> +/*
> + * User mode has no qtimer device backing TIMERLO/TIMERHI, so derive the
> + * user timer directly from the virtual clock -- the same clock the qtimer
> + * counts -- at the qtimer's default 19.2MHz tick rate, masked to the
> + * qtimer's counter width.
> + */
> +#define HEX_UTIMER_FREQ_HZ  19200000ULL
> +#define HEX_UTIMER_CNT_MASK 0x00ffffffffffffffULL
> +
> +uint64_t HELPER(utimer)(void)
> +{
> +    return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
> +                    HEX_UTIMER_FREQ_HZ, NANOSECONDS_PER_SECOND) &
> +           HEX_UTIMER_CNT_MASK;
> +}
> +#endif
> +
>  #define SF_BIAS        127
>  #define SF_MANTBITS    23
>  
> diff --git a/tests/tcg/hexagon/reg_mut.c b/tests/tcg/hexagon/reg_mut.c
> index c5a39e55100..9ce18f3ebe5 100644
> --- a/tests/tcg/hexagon/reg_mut.c
> +++ b/tests/tcg/hexagon/reg_mut.c
> @@ -77,10 +77,10 @@ static inline void write_control_registers(void)
>      check32(result, 0x00000000);
>  
>      WRITE_REG_NOCLOBBER(result, "utimerlo", 0xffffffff);
> -    check32(result, 0x00000000);
> +    check32_ne(result, 0xffffffff);
>  
>      WRITE_REG_NOCLOBBER(result, "utimerhi", 0xffffffff);
> -    check32(result, 0x00000000);
> +    check32_ne(result, 0xffffffff);
>  
>      /*
>       * PC is special.  Setting it to these values
> @@ -106,8 +106,13 @@ static inline void write_control_register_pairs(void)
>      WRITE_REG_NOCLOBBER(result, "c15:14", 0xffffffffffffffff);
>      check64(result, 0x0000000000000000);
>  
> +    /*
> +     * c31:30 is UTIMERHI:UTIMERLO, a read-only free-running counter.  The
> +     * write must be discarded; the read-back is whatever the timer says,
> +     * so only check that the written value did not stick.
> +     */
>      WRITE_REG_NOCLOBBER(result, "c31:30", 0xffffffffffffffff);
> -    check64(result, 0x0000000000000000);
> +    check64_ne(result, 0xffffffffffffffff);
>  
>      WRITE_REG_PAIR_ENCODED(result, "c9:8", (uint64_t) 0x0000000000000000,
>                             C9_8_EQ_R1_0);

Reviewed-by: Pierrick Bouvier <[email protected]>


Reply via email to