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
This commit is contained in:
Peter Maydell 2024-04-30 15:00:35 +01:00
parent 408b2b3d9d
commit a96edb687e
12 changed files with 180 additions and 2 deletions

View file

@ -1132,6 +1132,35 @@ static bool arm_cpu_virtio_is_big_endian(CPUState *cs)
return arm_cpu_data_is_big_endian(env);
}
#ifdef CONFIG_TCG
static bool arm_cpu_exec_halt(CPUState *cs)
{
bool leave_halt = cpu_has_work(cs);
if (leave_halt) {
/* We're about to come out of WFI/WFE: disable the WFxT timer */
ARMCPU *cpu = ARM_CPU(cs);
if (cpu->wfxt_timer) {
timer_del(cpu->wfxt_timer);
}
}
return leave_halt;
}
#endif
static void arm_wfxt_timer_cb(void *opaque)
{
ARMCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
/*
* We expect the CPU to be halted; this will cause arm_cpu_is_work()
* to return true (so we will come out of halt even with no other
* pending interrupt), and the TCG accelerator's cpu_exec_interrupt()
* function auto-clears the CPU_INTERRUPT_EXITTB flag for us.
*/
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
}
#endif
static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
@ -1877,6 +1906,9 @@ static void arm_cpu_finalizefn(Object *obj)
if (cpu->pmu_timer) {
timer_free(cpu->pmu_timer);
}
if (cpu->wfxt_timer) {
timer_free(cpu->wfxt_timer);
}
#endif
}
@ -2369,6 +2401,13 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
#endif
}
#ifndef CONFIG_USER_ONLY
if (tcg_enabled() && cpu_isar_feature(aa64_wfxt, cpu)) {
cpu->wfxt_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
arm_wfxt_timer_cb, cpu);
}
#endif
if (tcg_enabled()) {
/*
* Don't report some architectural features in the ID registers
@ -2625,6 +2664,7 @@ static const TCGCPUOps arm_tcg_ops = {
#else
.tlb_fill = arm_cpu_tlb_fill,
.cpu_exec_interrupt = arm_cpu_exec_interrupt,
.cpu_exec_halt = arm_cpu_exec_halt,
.do_interrupt = arm_cpu_do_interrupt,
.do_transaction_failed = arm_cpu_do_transaction_failed,
.do_unaligned_access = arm_cpu_do_unaligned_access,