Commit e41bf7cb3e for qemu.org
commit e41bf7cb3e2f54896cb1308fc5a40d8642e22840
Author: Brian Cain <brian.cain@oss.qualcomm.com>
Date: Tue Sep 1 10:33:49 2026 -0700
target/hexagon: read UTIMERLO/UTIMERHI from the global timer
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 the host monotonic clock.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index 9330732c99..1f109d44de 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],
diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
index 78dc28ca9e..e39afd623b 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/op_helper.c b/target/hexagon/op_helper.c
index 2cea192726..f9e66acf84 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,22 @@
#include "hexswi.h"
#endif
+#ifdef CONFIG_USER_ONLY
+/*
+ * User mode has no qtimer device backing TIMERLO/TIMERHI, so derive the
+ * user timer from the host monotonic clock 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(get_clock(), 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 c5a39e5510..9ce18f3ebe 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);