aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2025-03-07 10:08:20 +0000
committerPeter Maydell <peter.maydell@linaro.org>2025-03-07 10:08:20 +0000
commit4aecd4b442d7abb4355896d878ffc9b028625b01 (patch)
tree9754ce97ecbe541cac4f2b906c3913819192285f
parentbdd641541fbef0a27bf9f60e7eba6f8a31d4706c (diff)
downloadqemu-4aecd4b442d7abb4355896d878ffc9b028625b01.zip
qemu-4aecd4b442d7abb4355896d878ffc9b028625b01.tar.gz
qemu-4aecd4b442d7abb4355896d878ffc9b028625b01.tar.bz2
target/arm: Always apply CNTVOFF_EL2 for CNTV_TVAL_EL02 accesses
Currently we handle CNTV_TVAL_EL02 by calling gt_tval_read() for the EL1 virt timer. This is almost correct, but the underlying CNTV_TVAL_EL0 register behaves slightly differently. CNTV_TVAL_EL02 always applies the CNTVOFF_EL2 offset; CNTV_TVAL_EL0 doesn't do so if we're at EL2 and HCR_EL2.E2H is 1. We were getting this wrong, because we ended up in gt_virt_cnt_offset() and did the E2H check. Factor out the tval read/write calculation from the selection of the offset, so that we can special case gt_virt_tval_read() and gt_virt_tval_write() to unconditionally pass CNTVOFF_EL2. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20250204125009.2281315-5-peter.maydell@linaro.org
-rw-r--r--target/arm/helper.c36
1 files changed, 27 insertions, 9 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 5b6de44..acf7779 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -2600,6 +2600,12 @@ static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
gt_recalc_timer(env_archcpu(env), timeridx);
}
+static uint64_t do_tval_read(CPUARMState *env, int timeridx, uint64_t offset)
+{
+ return (uint32_t)(env->cp15.c14_timer[timeridx].cval -
+ (gt_get_countervalue(env) - offset));
+}
+
static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri,
int timeridx)
{
@@ -2614,8 +2620,16 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri,
break;
}
- return (uint32_t)(env->cp15.c14_timer[timeridx].cval -
- (gt_get_countervalue(env) - offset));
+ return do_tval_read(env, timeridx, offset);
+}
+
+static void do_tval_write(CPUARMState *env, int timeridx, uint64_t value,
+ uint64_t offset)
+{
+ trace_arm_gt_tval_write(timeridx, value);
+ env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset +
+ sextract64(value, 0, 32);
+ gt_recalc_timer(env_archcpu(env), timeridx);
}
static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -2632,11 +2646,7 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
offset = gt_phys_cnt_offset(env);
break;
}
-
- trace_arm_gt_tval_write(timeridx, value);
- env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset +
- sextract64(value, 0, 32);
- gt_recalc_timer(env_archcpu(env), timeridx);
+ do_tval_write(env, timeridx, value, offset);
}
static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -2768,13 +2778,21 @@ static void gt_virt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
static uint64_t gt_virt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
- return gt_tval_read(env, ri, GTIMER_VIRT);
+ /*
+ * This is CNTV_TVAL_EL02; unlike the underlying CNTV_TVAL_EL0
+ * we always apply CNTVOFF_EL2. Special case that here rather
+ * than going into the generic gt_tval_read() and then having
+ * to re-detect that it's this register.
+ * Note that the accessfn/perms mean we know we're at EL2 or EL3 here.
+ */
+ return do_tval_read(env, GTIMER_VIRT, env->cp15.cntvoff_el2);
}
static void gt_virt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- gt_tval_write(env, ri, GTIMER_VIRT, value);
+ /* Similarly for writes to CNTV_TVAL_EL02 */
+ do_tval_write(env, GTIMER_VIRT, value, env->cp15.cntvoff_el2);
}
static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,