@@ -146,6 +146,7 @@ the following architecture extensions:
- FEAT_UAO (Unprivileged Access Override control)
- FEAT_VHE (Virtualization Host Extensions)
- FEAT_VMID16 (16-bit VMID)
+- FEAT_WFxT (WFE and WFI instructions with timeout)
- FEAT_XNX (Translation table stage 2 Unprivileged Execute-never)
For information on the specifics of these extensions, please refer
@@ -571,6 +571,11 @@ static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id)
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0;
}
+static inline bool isar_feature_aa64_wfxt(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, WFXT) >= 2;
+}
+
static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0;
@@ -866,6 +866,9 @@ struct ArchCPU {
* pmu_op_finish() - it does not need other handling during migration
*/
QEMUTimer *pmu_timer;
+ /* Timer used for WFxT timeouts */
+ QEMUTimer *wfxt_timer;
+
/* GPIO outputs for generic timer */
qemu_irq gt_timer_outputs[NUM_GTIMERS];
/* GPIO output for GICv3 maintenance interrupt signal */
@@ -53,6 +53,7 @@ DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl)
DEF_HELPER_1(setend, void, env)
DEF_HELPER_2(wfi, void, env, i32)
DEF_HELPER_1(wfe, void, env)
+DEF_HELPER_2(wfit, void, env, i64)
DEF_HELPER_1(yield, void, env)
DEF_HELPER_1(pre_hvc, void, env)
DEF_HELPER_2(pre_smc, void, env, i32)
@@ -1770,4 +1770,12 @@ bool check_watchpoint_in_range(int i, target_ulong addr);
CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr);
int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type);
int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type);
+
+/* Return the current value of the system counter in ticks */
+uint64_t gt_get_countervalue(CPUARMState *env);
+/*
+ * Return the currently applicable offset between the system counter
+ * and CNTVCT_EL0 (this will be either 0 or the value of CNTVOFF_EL2).
+ */
+uint64_t gt_virt_cnt_offset(CPUARMState *env);
#endif
@@ -230,6 +230,10 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB
NOP 1101 0101 0000 0011 0010 ---- --- 11111
}
+# System instructions with register argument
+WFET 1101 0101 0000 0011 0001 0000 000 rd:5
+WFIT 1101 0101 0000 0011 0001 0000 001 rd:5
+
# Barriers
CLREX 1101 0101 0000 0011 0011 ---- 010 11111
@@ -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,
@@ -2665,7 +2665,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env,
}
}
-static uint64_t gt_get_countervalue(CPUARMState *env)
+uint64_t gt_get_countervalue(CPUARMState *env)
{
ARMCPU *cpu = env_archcpu(env);
@@ -2800,7 +2800,7 @@ static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
return gt_get_countervalue(env) - gt_phys_cnt_offset(env);
}
-static uint64_t gt_virt_cnt_offset(CPUARMState *env)
+uint64_t gt_virt_cnt_offset(CPUARMState *env)
{
uint64_t hcr;
@@ -242,6 +242,25 @@ static const VMStateDescription vmstate_irq_line_state = {
}
};
+static bool wfxt_timer_needed(void *opaque)
+{
+ ARMCPU *cpu = opaque;
+
+ /* We'll only have the timer object if FEAT_WFxT is implemented */
+ return cpu->wfxt_timer;
+}
+
+static const VMStateDescription vmstate_wfxt_timer = {
+ .name = "cpu/wfxt-timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = wfxt_timer_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_TIMER_PTR(wfxt_timer, ARMCPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static bool m_needed(void *opaque)
{
ARMCPU *cpu = opaque;
@@ -957,6 +976,7 @@ const VMStateDescription vmstate_arm_cpu = {
#endif
&vmstate_serror,
&vmstate_irq_line_state,
+ &vmstate_wfxt_timer,
NULL
}
};
@@ -1168,6 +1168,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = cpu->isar.id_aa64isar2;
t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */
t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */
+ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */
cpu->isar.id_aa64isar2 = t;
t = cpu->isar.id_aa64pfr0;
@@ -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
@@ -1745,6 +1745,47 @@ static bool trans_WFE(DisasContext *s, arg_WFI *a)
return true;
}
+static bool trans_WFIT(DisasContext *s, arg_WFIT *a)
+{
+ if (!dc_isar_feature(aa64_wfxt, s)) {
+ return false;
+ }
+
+ /*
+ * Because we need to pass the register value to the helper,
+ * it's easier to emit the code now, unlike trans_WFI which
+ * defers it to aarch64_tr_tb_stop(). That means we need to
+ * check ss_active so that single-stepping a WFIT doesn't halt.
+ */
+ if (s->ss_active) {
+ /* Act like a NOP under architectural singlestep */
+ return true;
+ }
+
+ gen_a64_update_pc(s, 4);
+ gen_helper_wfit(tcg_env, cpu_reg(s, a->rd));
+ /* Go back to the main loop to check for interrupts */
+ s->base.is_jmp = DISAS_EXIT;
+ return true;
+}
+
+static bool trans_WFET(DisasContext *s, arg_WFET *a)
+{
+ if (!dc_isar_feature(aa64_wfxt, s)) {
+ return false;
+ }
+
+ /*
+ * We rely here on our WFE implementation being a NOP, so we
+ * don't need to do anything different to handle the WFET timeout
+ * from what trans_WFE does.
+ */
+ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
+ s->base.is_jmp = DISAS_WFE;
+ }
+ return true;
+}
+
static bool trans_XPACLRI(DisasContext *s, arg_XPACLRI *a)
{
if (s->pauth_active) {