aboutsummaryrefslogtreecommitdiff
path: root/target/arm/tcg/op_helper.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-04-30 15:00:35 +0100
committerPeter Maydell <peter.maydell@linaro.org>2024-05-30 16:35:17 +0100
commita96edb687e76a44b554b7975d9deda522c2c4302 (patch)
treec5af340cda56025d1b408433b88dc71836566ff9 /target/arm/tcg/op_helper.c
parent408b2b3d9df631102919ada62add6d785f737f74 (diff)
target/arm: Implement FEAT WFxT and enable for '-cpu max'
FEAT_WFxT introduces new instructions WFIT and WFET, which are like the existing WFI and WFE but allow the guest to pass a timeout value in a register. The instructions will wait for an interrupt/event as usual, but will also stop waiting when the value of CNTVCT_EL0 is greater than or equal to the specified timeout value. We implement WFIT by setting up a timer to expire at the right point; when the timer expires it sets the EXITTB interrupt, which will cause the CPU to leave the halted state. If we come out of halt for some other reason, we unset the pending timer. We implement WFET as a nop, which is architecturally permitted and matches the way we currently make WFE a nop. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240430140035.3889879-3-peter.maydell@linaro.org
Diffstat (limited to 'target/arm/tcg/op_helper.c')
-rw-r--r--target/arm/tcg/op_helper.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index c199b69fbf..c083e5cfb8 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -409,6 +409,60 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
#endif
}
+void HELPER(wfit)(CPUARMState *env, uint64_t timeout)
+{
+#ifdef CONFIG_USER_ONLY
+ /*
+ * WFI in the user-mode emulator is technically permitted but not
+ * something any real-world code would do. AArch64 Linux kernels
+ * trap it via SCTRL_EL1.nTWI and make it an (expensive) NOP;
+ * AArch32 kernels don't trap it so it will delay a bit.
+ * For QEMU, make it NOP here, because trying to raise EXCP_HLT
+ * would trigger an abort.
+ */
+ return;
+#else
+ ARMCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+ int target_el = check_wfx_trap(env, false);
+ /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */
+ uint64_t cntval = gt_get_countervalue(env);
+ uint64_t offset = gt_virt_cnt_offset(env);
+ uint64_t cntvct = cntval - offset;
+ uint64_t nexttick;
+
+ if (cpu_has_work(cs) || cntvct >= timeout) {
+ /*
+ * Don't bother to go into our "low power state" if
+ * we would just wake up immediately.
+ */
+ return;
+ }
+
+ if (target_el) {
+ env->pc -= 4;
+ raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, false),
+ target_el);
+ }
+
+ if (uadd64_overflow(timeout, offset, &nexttick)) {
+ nexttick = UINT64_MAX;
+ }
+ if (nexttick > INT64_MAX / gt_cntfrq_period_ns(cpu)) {
+ /*
+ * If the timeout is too long for the signed 64-bit range
+ * of a QEMUTimer, let it expire early.
+ */
+ timer_mod_ns(cpu->wfxt_timer, INT64_MAX);
+ } else {
+ timer_mod(cpu->wfxt_timer, nexttick);
+ }
+ cs->exception_index = EXCP_HLT;
+ cs->halted = 1;
+ cpu_loop_exit(cs);
+#endif
+}
+
void HELPER(wfe)(CPUARMState *env)
{
/* This is a hint instruction that is semantically different