* [PATCH v9 1/7] hw/arm: use cortex-a9 mpcore base for CBAR on npcm7xx machines
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-06-24 10:30 ` [PATCH v9 2/7] tests/functional: update anacapa-bmc image Alex Bennée
` (6 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée, qemu-stable,
Arnd Bergmann, Philippe Mathieu-Daudé
CBAR is an IMPDEF register and according to the A9 TRM [1]:
In Cortex-A9 MPCore implementations, the base address is reset to
PERIPHBASE[31:13] so that software can determine the location of the
private memory region [2].
If it doesn't we will confuse the Linux kernel as it probes the system
SCU registers [3] and erroneously assumes the system is a buggy Aegis SOC
and nerf the emission of SEV instructions, deadlocking any WFE's in
the kernel (or QEMU smpboot code).
[1] https://developer.arm.com/documentation/ddi0388/i/system-control/register-descriptions/configuration-base-address-register
[2] https://developer.arm.com/documentation/ddi0407/g/Introduction/Private-Memory-Region
[3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/kernel/head.S?h=v7.1#n550
Fixes: 2d8f048c25ab ("hw/arm: Add NPCM730 and NPCM750 SoC models")
Cc: qemu-stable@nongnu.org
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Suggested-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
v2
- rewrote commit message for clarity, added links
- used arnd's arndb.de address
---
hw/arm/npcm7xx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index c2bbcd89dbc..c27f149c04a 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -492,7 +492,7 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
/* CPUs */
for (i = 0; i < nc->num_cpus; i++) {
object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar",
- NPCM7XX_GIC_CPU_IF_ADDR, &error_abort);
+ NPCM7XX_CPUP_BA, &error_abort);
object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
&error_abort);
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 2/7] tests/functional: update anacapa-bmc image
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
2026-06-24 10:30 ` [PATCH v9 1/7] hw/arm: use cortex-a9 mpcore base for CBAR on npcm7xx machines Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-06-24 10:30 ` [PATCH v9 3/7] target/arm: do not clear halting reason in has_work helper Alex Bennée
` (5 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée, Cédric Le Goater
The initial version had the wrong DTB which caused issues with image
corruption [1]. Update to the latest version.
[1] https://github.com/legoater/qemu-aspeed-boot/pull/7
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Suggested-by: Cédric Le Goater <clg@redhat.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
tests/functional/arm/test_aspeed_anacapa.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/functional/arm/test_aspeed_anacapa.py b/tests/functional/arm/test_aspeed_anacapa.py
index 27f8bd8b56a..b16c6035c95 100644
--- a/tests/functional/arm/test_aspeed_anacapa.py
+++ b/tests/functional/arm/test_aspeed_anacapa.py
@@ -11,8 +11,8 @@
class AnacapaMachine(FacebookAspeedTest):
ASSET_ANACAPA_FLASH = Asset(
- 'https://github.com/legoater/qemu-aspeed-boot/raw/3fa3212827b04be4034d43b5adeef57c27d6ab18/images/anacapa-bmc/openbmc-20260512025228/obmc-phosphor-image-anacapa-20260512025228.static.mtd.xz',
- '2232e241abcfb6d4f6b82cb6c378ce5ce05e364aac6d118785c2b6cc33fe43f3')
+ 'https://github.com/legoater/qemu-aspeed-boot/raw/refs/heads/master/images/anacapa-bmc/openbmc-20260616025349/obmc-phosphor-image-anacapa-20260616025349.static.mtd.xz',
+ 'de3841fb6ed3085aec6424358ee6efc4b8ee85688361e5aa1987fd1acb7d3fb4')
def test_arm_ast2600_anacapa_openbmc(self):
image_path = self.uncompress(self.ASSET_ANACAPA_FLASH)
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 3/7] target/arm: do not clear halting reason in has_work helper
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
2026-06-24 10:30 ` [PATCH v9 1/7] hw/arm: use cortex-a9 mpcore base for CBAR on npcm7xx machines Alex Bennée
2026-06-24 10:30 ` [PATCH v9 2/7] tests/functional: update anacapa-bmc image Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-06-25 15:01 ` Richard Henderson
2026-06-24 10:30 ` [PATCH v9 4/7] target/arm: ensure we create the wxft_timer for all modes Alex Bennée
` (4 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée
The helper will be called multiple times as we exit a loop and until
we actually restart (via arm_cpu_exec_halt) we should leave the
condition the same.
Fixes: 6fd2fcdc61b (target/arm: teach arm_cpu_has_work about halting reasons)
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
include/hw/core/sysemu-cpu-ops.h | 3 +++
target/arm/cpu.c | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h
index 8be6a84bd54..9a45596169a 100644
--- a/include/hw/core/sysemu-cpu-ops.h
+++ b/include/hw/core/sysemu-cpu-ops.h
@@ -18,6 +18,9 @@
typedef struct SysemuCPUOps {
/**
* @has_work: Callback for checking if there is work to do.
+ *
+ * This function should be idempotent (i.e. not change state) as
+ * it will likely be queried multiple times before a CPU resumes.
*/
bool (*has_work)(CPUState *cpu); /* MANDATORY NON-NULL */
/**
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 86aae36ae55..597e0626e7f 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -158,7 +158,6 @@ static bool arm_cpu_has_work(CPUState *cs)
* A wake-up event should only wake us if we are halted on a WFE
*/
if (cpu->env.halt_reason == HALT_WFE && cpu->env.event_register) {
- cpu->env.halt_reason = NOT_HALTED;
return true;
}
@@ -170,7 +169,6 @@ static bool arm_cpu_has_work(CPUState *cs)
| CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI
| CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR
| CPU_INTERRUPT_EXITTB)) {
- cpu->env.halt_reason = NOT_HALTED;
return true;
}
@@ -878,6 +876,8 @@ bool arm_cpu_exec_halt(CPUState *cs)
if (cpu->wfxt_timer) {
timer_del(cpu->wfxt_timer);
}
+ /* clear the halt reason */
+ cpu->env.halt_reason = NOT_HALTED;
}
return leave_halt;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v9 3/7] target/arm: do not clear halting reason in has_work helper
2026-06-24 10:30 ` [PATCH v9 3/7] target/arm: do not clear halting reason in has_work helper Alex Bennée
@ 2026-06-25 15:01 ` Richard Henderson
0 siblings, 0 replies; 14+ messages in thread
From: Richard Henderson @ 2026-06-25 15:01 UTC (permalink / raw)
To: qemu-devel
On 6/24/26 03:30, Alex Bennée wrote:
> The helper will be called multiple times as we exit a loop and until
> we actually restart (via arm_cpu_exec_halt) we should leave the
> condition the same.
>
> Fixes: 6fd2fcdc61b (target/arm: teach arm_cpu_has_work about halting reasons)
> Reviewed-by: Peter Maydell<peter.maydell@linaro.org>
> Signed-off-by: Alex Bennée<alex.bennee@linaro.org>
> ---
> include/hw/core/sysemu-cpu-ops.h | 3 +++
> target/arm/cpu.c | 4 ++--
> 2 files changed, 5 insertions(+), 2 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v9 4/7] target/arm: ensure we create the wxft_timer for all modes
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
` (2 preceding siblings ...)
2026-06-24 10:30 ` [PATCH v9 3/7] target/arm: do not clear halting reason in has_work helper Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-06-25 15:02 ` Richard Henderson
2026-06-24 10:30 ` [PATCH v9 5/7] target/arm: implements SEV/SEVL " Alex Bennée
` (3 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée
We don't want to just use it for timeouts as we will calculate which
will comes first. Remove the wxft feature test in favour of the
broader architecture checks.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
v9
- we only need to check ARM_FEATURE_V6K as V7 and V8 imply it thanks
to arm_cpu_propagate_feature_implications
v8
- new commit
---
target/arm/cpu.h | 2 +-
target/arm/cpu.c | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 31a5567c951..8aa7437a397 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -966,7 +966,7 @@ struct ArchCPU {
* pmu_op_finish() - it does not need other handling during migration
*/
QEMUTimer *pmu_timer;
- /* Timer used for WFxT timeouts */
+ /* Timer used for WFxT timeouts OR event stream events */
QEMUTimer *wfxt_timer;
/* GPIO outputs for generic timer */
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 597e0626e7f..4b49f8c43ec 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2269,7 +2269,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
}
#ifndef CONFIG_USER_ONLY
- if (tcg_enabled() && cpu_isar_feature(aa64_wfxt, cpu)) {
+ /*
+ * We use the wfxt_timer for timeouts and event stream so we
+ * enable from V6K up. There is no event stream on M-profile.
+ */
+ if (tcg_enabled() && arm_feature(env, ARM_FEATURE_V6K)) {
cpu->wfxt_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
arm_wfxt_timer_cb, cpu);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 5/7] target/arm: implements SEV/SEVL for all modes
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
` (3 preceding siblings ...)
2026-06-24 10:30 ` [PATCH v9 4/7] target/arm: ensure we create the wxft_timer for all modes Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-06-24 10:30 ` [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile Alex Bennée
` (2 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée, Richard Henderson
Remove the restrictions that make this a M-profile only operation and
enable the instructions for all Arm profiles.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
v3
- restrict SEV to M profile plus
- restrict SEVL to V8
v2
- fix alignment in a32.decode
- set bool directly, defend with QEMU_BUILD_BUG_ON
- s/instructions/profiles/
- share get_event_reg between translate/translate-a64
---
target/arm/tcg/translate.h | 18 ++++++++++++++++++
target/arm/tcg/a32.decode | 5 ++---
target/arm/tcg/a64.decode | 5 ++---
target/arm/tcg/t16.decode | 4 +---
target/arm/tcg/t32.decode | 4 +---
target/arm/tcg/op_helper.c | 4 +---
target/arm/tcg/translate-a64.c | 17 +++++++++++++++++
target/arm/tcg/translate.c | 20 +++++++++++++++++---
8 files changed, 59 insertions(+), 18 deletions(-)
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index 462d4c1c74f..83b413ee368 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -860,6 +860,24 @@ static inline void gen_restore_rmode(TCGv_i32 old, TCGv_ptr fpst)
gen_helper_set_rmode(old, old, fpst);
}
+/*
+ * Event Register signalling.
+ *
+ * A bunch of activities trigger events, we just need to latch on to
+ * true. The event eventually gets consumed by WFE/WFET.
+ *
+ * user-mode treats these as NOPs.
+ */
+
+static inline void gen_event_reg(void)
+{
+#ifndef CONFIG_USER_ONLY
+ TCGv_i32 set_event = tcg_constant_i32(1);
+ QEMU_BUILD_BUG_ON(sizeof_field(CPUARMState, event_register) != 1);
+ tcg_gen_st8_i32(set_event, tcg_env, offsetof(CPUARMState, event_register));
+#endif
+}
+
/*
* Helpers for implementing sets of trans_* functions.
* Defer the implementation of NAME to FUNC, with optional extra arguments.
diff --git a/target/arm/tcg/a32.decode b/target/arm/tcg/a32.decode
index f2ca4809495..547aa2b1490 100644
--- a/target/arm/tcg/a32.decode
+++ b/target/arm/tcg/a32.decode
@@ -192,9 +192,8 @@ SMULTT .... 0001 0110 .... 0000 .... 1110 .... @rd0mn
WFE ---- 0011 0010 0000 1111 ---- 0000 0010
WFI ---- 0011 0010 0000 1111 ---- 0000 0011
- # TODO: Implement SEV, SEVL; may help SMP performance.
- # SEV ---- 0011 0010 0000 1111 ---- 0000 0100
- # SEVL ---- 0011 0010 0000 1111 ---- 0000 0101
+ SEV ---- 0011 0010 0000 1111 ---- 0000 0100
+ SEVL ---- 0011 0010 0000 1111 ---- 0000 0101
ESB ---- 0011 0010 0000 1111 ---- 0001 0000
]
diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode
index 28cd1faf61a..81070dac0a6 100644
--- a/target/arm/tcg/a64.decode
+++ b/target/arm/tcg/a64.decode
@@ -250,9 +250,8 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB
YIELD 1101 0101 0000 0011 0010 0000 001 11111
WFE 1101 0101 0000 0011 0010 0000 010 11111
WFI 1101 0101 0000 0011 0010 0000 011 11111
- # We implement WFE to never block, so our SEV/SEVL are NOPs
- # SEV 1101 0101 0000 0011 0010 0000 100 11111
- # SEVL 1101 0101 0000 0011 0010 0000 101 11111
+ SEV 1101 0101 0000 0011 0010 0000 100 11111
+ SEVL 1101 0101 0000 0011 0010 0000 101 11111
# Our DGL is a NOP because we don't merge memory accesses anyway.
# DGL 1101 0101 0000 0011 0010 0000 110 11111
XPACLRI 1101 0101 0000 0011 0010 0000 111 11111
diff --git a/target/arm/tcg/t16.decode b/target/arm/tcg/t16.decode
index 778fbf16275..9a8f89538ac 100644
--- a/target/arm/tcg/t16.decode
+++ b/target/arm/tcg/t16.decode
@@ -228,10 +228,8 @@ REVSH 1011 1010 11 ... ... @rdm
WFE 1011 1111 0010 0000
WFI 1011 1111 0011 0000
- # M-profile SEV is implemented.
- # TODO: Implement SEV for other profiles, and SEVL for all profiles; may help SMP performance.
SEV 1011 1111 0100 0000
- # SEVL 1011 1111 0101 0000
+ SEVL 1011 1111 0101 0000
# The canonical nop has the second nibble as 0000, but the whole of the
# rest of the space is a reserved hint, behaves as nop.
diff --git a/target/arm/tcg/t32.decode b/target/arm/tcg/t32.decode
index 49b8d0037ec..8ae277fe112 100644
--- a/target/arm/tcg/t32.decode
+++ b/target/arm/tcg/t32.decode
@@ -369,10 +369,8 @@ CLZ 1111 1010 1011 ---- 1111 .... 1000 .... @rdm
WFE 1111 0011 1010 1111 1000 0000 0000 0010
WFI 1111 0011 1010 1111 1000 0000 0000 0011
- # M-profile SEV is implemented.
- # TODO: Implement SEV for other profiles, and SEVL for all profiles; may help SMP performance.
SEV 1111 0011 1010 1111 1000 0000 0000 0100
- # SEVL 1111 0011 1010 1111 1000 0000 0000 0101
+ SEVL 1111 0011 1010 1111 1000 0000 0000 0101
ESB 1111 0011 1010 1111 1000 0000 0001 0000
]
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index 9f9ea39be5a..d15062e155f 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -477,9 +477,7 @@ void HELPER(sev)(CPUARMState *env)
CPUState *cs = env_cpu(env);
CPU_FOREACH(cs) {
ARMCPU *target_cpu = ARM_CPU(cs);
- if (arm_feature(&target_cpu->env, ARM_FEATURE_M)) {
- target_cpu->env.event_register = true;
- }
+ target_cpu->env.event_register = true;
if (!qemu_cpu_is_self(cs)) {
qemu_cpu_kick(cs);
}
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 227719ef25b..df5bac22acd 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -2142,6 +2142,23 @@ static bool trans_WFI(DisasContext *s, arg_WFI *a)
return true;
}
+static bool trans_SEV(DisasContext *s, arg_SEV *a)
+{
+ /*
+ * SEV is a NOP for user-mode emulation.
+ */
+#ifndef CONFIG_USER_ONLY
+ gen_helper_sev(tcg_env);
+#endif
+ return true;
+}
+
+static bool trans_SEVL(DisasContext *s, arg_SEV *a)
+{
+ gen_event_reg();
+ return true;
+}
+
static bool trans_WFE(DisasContext *s, arg_WFI *a)
{
/*
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index c744b163453..9079458a297 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -3246,17 +3246,31 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a)
static bool trans_SEV(DisasContext *s, arg_SEV *a)
{
/*
- * Currently SEV is a NOP for non-M-profile and in user-mode emulation.
- * For system-mode M-profile, it sets the event register.
+ * SEV is a NOP for user-mode emulation. For v6T2 and earlier
+ * non-M-profile cores this encoding is a NOP hint.
*/
#ifndef CONFIG_USER_ONLY
- if (arm_dc_feature(s, ARM_FEATURE_M)) {
+ if (arm_dc_feature(s, ARM_FEATURE_M) ||
+ arm_dc_feature(s, ARM_FEATURE_V7)) {
gen_helper_sev(tcg_env);
}
#endif
return true;
}
+static bool trans_SEVL(DisasContext *s, arg_SEV *a)
+{
+ /*
+ * SEVL only exists for v8A; for M-profile and v7A and earlier
+ * this encoding is an unallocated must-NOP hint.
+ */
+ if (!arm_dc_feature(s, ARM_FEATURE_M) &&
+ arm_dc_feature(s, ARM_FEATURE_V8)) {
+ gen_event_reg();
+ }
+ return true;
+}
+
static bool trans_WFE(DisasContext *s, arg_WFE *a)
{
/*
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
` (4 preceding siblings ...)
2026-06-24 10:30 ` [PATCH v9 5/7] target/arm: implements SEV/SEVL " Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-10-26 15:53 ` Thomas Huth
2026-06-24 10:30 ` [PATCH v9 7/7] target/arm: implement WFET Alex Bennée
2026-07-03 9:59 ` [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Peter Maydell
7 siblings, 1 reply; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée, Richard Henderson
To enable full architectural behaviour for A-profile we need to do a
number of things:
- add support for the event stream to wake things up
- add support for potential trap on sleep
- handle the global monitor's interactions with WFE
- remove the M-profile specific gates
Event stream
------------
Two generic timers (K and H) are capable of generating timer event
stream events. Provide a helper to calculate when the nearest one will
happen.
Now we can calculate when the next event stream event is we can re-use
the wfxt_timer and configure it to fire as we enter a WFE that is
going to sleep. Reverse the M-profile logic so we can enter a sleep
state in both profiles.
We also take care to use atomics for accessing env->event_register as
we now have potential access outside the vCPU context.
Traps
-----
A-profile can trap WFE's *if* the instruction would otherwise sleep.
To do this we need to pass the instruction size so we can deal with
the is_16bit syndrome encoding.
Global Monitor
--------------
To avoid issues with QEMU's incomplete ldst exclusive handling causing
potential deadlocks in common WFE enabled locking patterns we take
advantage of the architectures flexibility and treat being in the
exclusive region as a reason to exit.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
v2
- merged target/arm: add gt_calc_next_event_stream
- update to use halt_reason
- made arm_wfxt_timer_cb atomically consume halt_reason
v4
- skip sleep if in the exclusive region
- update commit message
- remove the CF_PARALLEL guards so we work in smp
v5
- use env_archcpu for ARMCPU rather then expensive QOM cast
- rely on cpu->wfxt_timer to guard event stream leg
v6
- use atomic_xchg to consume event_register
- remove extraneous target_el calculation
- defer calculating target_el until after the early return
v7
- merged with trap patch
- rewrite commit message
---
target/arm/tcg/helper-defs.h | 2 +-
target/arm/cpu.c | 13 +++
target/arm/tcg/op_helper.c | 156 ++++++++++++++++++++++++++++-----
target/arm/tcg/translate-a64.c | 12 +--
target/arm/tcg/translate.c | 18 +---
5 files changed, 156 insertions(+), 45 deletions(-)
diff --git a/target/arm/tcg/helper-defs.h b/target/arm/tcg/helper-defs.h
index 8ec6c163195..99ebd754942 100644
--- a/target/arm/tcg/helper-defs.h
+++ b/target/arm/tcg/helper-defs.h
@@ -54,7 +54,7 @@ DEF_HELPER_2(exception_swstep, noreturn, env, i32)
DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr)
DEF_HELPER_1(setend, void, env)
DEF_HELPER_2(wfi, void, env, i32)
-DEF_HELPER_1(wfe, void, env)
+DEF_HELPER_2(wfe, void, env, i32)
DEF_HELPER_2(wfit, void, env, i32)
DEF_HELPER_1(yield, void, env)
DEF_HELPER_1(pre_hvc, void, env)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 4b49f8c43ec..b3de399f40c 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -883,10 +883,23 @@ bool arm_cpu_exec_halt(CPUState *cs)
}
#endif
+/*
+ * Unlike almost everything else that messes with the halt_reason and
+ * event_register details the timer callbacks are not in the vCPU
+ * context.
+ *
+ * To prevent races we atomically consume a HALT_WFE and set the event
+ * register. Either way we trigger the an exit event.
+ */
static void arm_wfxt_timer_cb(void *opaque)
{
ARMCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+
+ if (qatomic_cmpxchg(&env->halt_reason, HALT_WFE, NOT_HALTED)) {
+ qatomic_set(&env->event_register, true);
+ }
/*
* We expect the CPU to be halted; this will cause arm_cpu_is_work()
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index d15062e155f..3321e29898d 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -484,7 +484,98 @@ void HELPER(sev)(CPUARMState *env)
}
}
-void HELPER(wfe)(CPUARMState *env)
+#ifndef CONFIG_USER_ONLY
+/*
+ * Event Stream events don't do anything apart from wake up sleeping
+ * cores. These helpers calculate the next event stream event time so
+ * the WFE helper can decide when its next wake up tick will be.
+ */
+static int64_t gt_recalc_one_evt(CPUARMState *env, uint32_t control, uint64_t offset)
+{
+ ARMCPU *cpu = env_archcpu(env);
+ bool evnten = FIELD_EX32(control, CNTxCTL, EVNTEN);
+
+ if (evnten) {
+ int evnti = FIELD_EX32(control, CNTxCTL, EVNTI);
+ bool evntis = FIELD_EX32(control, CNTxCTL, EVNTIS);
+ bool evntdir = FIELD_EX32(control, CNTxCTL, EVNTDIR);
+ /*
+ * To figure out when the next event timer should fire we need
+ * to calculate which bit of the counter we want to flip and
+ * which transition counts.
+ *
+ * So we calculate 1 << bit - current lower bits and then add
+ * 1 << bit if the bit needs to flip twice to meet evntdir
+ */
+ int bit = evntis ? evnti + 8 : evnti;
+ uint64_t count = gt_get_countervalue(env) - offset;
+ uint64_t target_bit = BIT_ULL(bit);
+ uint64_t lower_bits = MAKE_64BIT_MASK(0, bit - 1);
+ uint64_t next_tick = target_bit - (count & lower_bits);
+ uint64_t abstick;
+
+ /* do we need to bit flip twice? */
+ if (((count & target_bit) != 0) ^ evntdir) {
+ next_tick += target_bit;
+ }
+
+ /*
+ * Note that the desired next expiry time might be beyond the
+ * signed-64-bit range of a QEMUTimer -- in this case we just
+ * set the timer for as far in the future as possible. When the
+ * timer expires we will reset the timer for any remaining period.
+ */
+ if (uadd64_overflow(next_tick, offset, &abstick)) {
+ abstick = UINT64_MAX;
+ }
+ if (abstick > INT64_MAX / gt_cntfrq_period_ns(cpu)) {
+ return INT64_MAX;
+ } else {
+ return abstick;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Calculate the next event stream time and return it. Returns -1 if
+ * no event streams are enabled. It is up to the WFE helpers to decide
+ * on the next time.
+ */
+static int64_t gt_calc_next_event_stream(CPUARMState *env)
+{
+ ARMCPU *cpu = env_archcpu(env);
+ uint64_t hcr = arm_hcr_el2_eff(env);
+ int64_t next_time = -1;
+ uint64_t offset;
+
+ /* Unless we are missing EL2 this can generate events */
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ offset = gt_direct_access_timer_offset(env, GTIMER_PHYS);
+ next_time = gt_recalc_one_evt(env, env->cp15.cnthctl_el2, offset);
+ }
+
+ /* Event stream events from virtual counter enabled? */
+ if (!cpu_isar_feature(aa64_vh, cpu) ||
+ !((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE))) {
+ int64_t next_virt_time;
+ offset = gt_direct_access_timer_offset(env, GTIMER_VIRT);
+ next_virt_time = gt_recalc_one_evt(env, env->cp15.c14_cntkctl, offset);
+
+ /* is this earlier than the next physical event? */
+ if (next_virt_time > 0) {
+ if (next_time < 0 || next_virt_time < next_time) {
+ next_time = next_virt_time;
+ }
+ }
+ }
+
+ return next_time;
+}
+#endif
+
+void HELPER(wfe)(CPUARMState *env, uint32_t insn_len)
{
#ifdef CONFIG_USER_ONLY
/*
@@ -496,32 +587,57 @@ void HELPER(wfe)(CPUARMState *env)
#else
/*
* WFE (Wait For Event) is a hint instruction.
- * For Cortex-M (M-profile), we implement the strict architectural behavior:
+ *
* 1. Check the Event Register (set by SEV or SEVONPEND).
* 2. If set, clear it and continue (consume the event).
*/
- if (arm_feature(env, ARM_FEATURE_M)) {
- CPUState *cs = env_cpu(env);
+ CPUState *cs = env_cpu(env);
+ ARMCPU *cpu = env_archcpu(env);
+ uint32_t excp;
+ int target_el;
- if (env->event_register) {
- env->event_register = false;
- return;
+ if (qatomic_xchg(&env->event_register, false)) {
+ return;
+ }
+
+ /* We might sleep, so now we check to see if we should trap */
+ target_el = check_wfx_trap(env, true, &excp);
+ if (target_el) {
+ if (env->aarch64) {
+ env->pc -= insn_len;
+ } else {
+ env->regs[15] -= insn_len;
}
+ raise_exception(env, excp, syn_wfx(1, 0xe, 0, false, WFE, insn_len == 2),
+ target_el);
+ }
- env->halt_reason = HALT_WFE;
- cs->exception_index = EXCP_HLT;
- cs->halted = 1;
- cpu_loop_exit(cs);
- } else {
- /*
- * For A-profile and others, we rely on the existing "yield" behavior.
- * Don't actually halt the CPU, just yield back to top
- * level loop. This is not going into a "low power state"
- * (ie halting until some event occurs), so we never take
- * a configurable trap to a different exception level
- */
- HELPER(yield)(env);
+ /*
+ * If the CPU has entered the exclusive region we could sleep
+ * until the global monitor moves from Exclusive to Open Access.
+ * However it would be expensive for QEMU to fully model the
+ * global monitor and not doing so would potentially trigger
+ * deadlocks in WFE enabled locking code. However as WFE is a hint
+ * instruction the architecture allows for the PE to leave
+ * low-power state for any reason. QEMU chooses to treat being in
+ * an exclusive region as such and return directly.
+ */
+ if (env->exclusive_addr != -1) {
+ return;
}
+
+ /* For A-profile we also can be woken by the event stream */
+ if (cpu->wfxt_timer) {
+ int64_t next_event = gt_calc_next_event_stream(env);
+ if (next_event > 0) {
+ timer_mod(cpu->wfxt_timer, next_event);
+ }
+ }
+
+ env->halt_reason = HALT_WFE;
+ cs->exception_index = EXCP_HLT;
+ cs->halted = 1;
+ cpu_loop_exit(cs);
#endif
}
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index df5bac22acd..b45aac6d269 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -2161,15 +2161,7 @@ static bool trans_SEVL(DisasContext *s, arg_SEV *a)
static bool trans_WFE(DisasContext *s, arg_WFI *a)
{
- /*
- * When running in MTTCG we don't generate jumps to the yield and
- * WFE helpers as it won't affect the scheduling of other vCPUs.
- * If we wanted to more completely model WFE/SEV so we don't busy
- * spin unnecessarily we would need to do something more involved.
- */
- if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
- s->base.is_jmp = DISAS_WFE;
- }
+ s->base.is_jmp = DISAS_WFE;
return true;
}
@@ -11232,7 +11224,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
*/
case DISAS_WFE:
gen_a64_update_pc(dc, 4);
- gen_helper_wfe(tcg_env);
+ gen_helper_wfe(tcg_env, tcg_constant_i32(4));
tcg_gen_exit_tb(NULL, 0);
break;
case DISAS_WFI:
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 9079458a297..a1fc0506188 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -3273,19 +3273,9 @@ static bool trans_SEVL(DisasContext *s, arg_SEV *a)
static bool trans_WFE(DisasContext *s, arg_WFE *a)
{
- /*
- * When running single-threaded TCG code, use the helper to ensure that
- * the next round-robin scheduled vCPU gets a crack.
- *
- * For Cortex-M, we implement the architectural WFE behavior (sleeping
- * until an event occurs or the Event Register is set).
- * For other profiles, we currently treat this as a NOP or yield,
- * to preserve existing performance characteristics.
- */
- if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
- gen_update_pc(s, curr_insn_len(s));
- s->base.is_jmp = DISAS_WFE;
- }
+ /* For WFE, halt the vCPU until an event. */
+ gen_update_pc(s, curr_insn_len(s));
+ s->base.is_jmp = DISAS_WFE;
return true;
}
@@ -6857,7 +6847,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
tcg_gen_exit_tb(NULL, 0);
break;
case DISAS_WFE:
- gen_helper_wfe(tcg_env);
+ gen_helper_wfe(tcg_env, tcg_constant_i32(curr_insn_len(dc)));
/*
* The helper can return if the event register is set, so we
* must go back to the main loop to check for events.
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile
2026-06-24 10:30 ` [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile Alex Bennée
@ 2026-10-26 15:53 ` Thomas Huth
2026-10-26 16:12 ` Alex Bennée
0 siblings, 1 reply; 14+ messages in thread
From: Thomas Huth @ 2026-10-26 15:53 UTC (permalink / raw)
To: Alex Bennée, qemu-devel, Peter Maydell
Cc: kvm, Troy Lee, Hao Wu, Kane Chen, Mohamed Mediouni, Steven Lee,
Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Richard Henderson
On 24/06/2026 12.30, Alex Bennée wrote:
> To enable full architectural behaviour for A-profile we need to do a
> number of things:
>
> - add support for the event stream to wake things up
> - add support for potential trap on sleep
> - handle the global monitor's interactions with WFE
> - remove the M-profile specific gates
Hi Alex,
this patch broke the test_arm_quanta_gsj test in
tests/functional/arm/test_quanta_gsj.py. The test is not run by default, so
nobody noticed. But if you run it like this:
QEMU_TEST_ALLOW_SLOW=1 QEMU_TEST_QEMU_BINARY=./qemu-system-arm ./run \
~/devel/qemu/tests/functional/arm/test_quanta_gsj.py
you'll notice that the test never finishes on the current master branch.
When switching back to the commit before the "enable WFE" patch, the test
works fine again and finishes within a minute or two here.
Could you please have a look when you've got some spare time?
Thanks,
Thomas
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile
2026-10-26 15:53 ` Thomas Huth
@ 2026-10-26 16:12 ` Alex Bennée
2026-10-26 16:54 ` Thomas Huth
0 siblings, 1 reply; 14+ messages in thread
From: Alex Bennée @ 2026-10-26 16:12 UTC (permalink / raw)
To: Thomas Huth
Cc: qemu-devel, Peter Maydell, kvm, Troy Lee, Hao Wu, Kane Chen,
Mohamed Mediouni, Steven Lee, Joel Stanley, Jamin Lin,
Pedro Barbuda, Paolo Bonzini, Tyrone Ting, Andrew Jeffery,
qemu-arm, Alexander Graf, Cédric Le Goater,
Richard Henderson
Thomas Huth <thuth@redhat.com> writes:
> On 24/06/2026 12.30, Alex Bennée wrote:
>> To enable full architectural behaviour for A-profile we need to do a
>> number of things:
>> - add support for the event stream to wake things up
>> - add support for potential trap on sleep
>> - handle the global monitor's interactions with WFE
>> - remove the M-profile specific gates
> Hi Alex,
>
> this patch broke the test_arm_quanta_gsj test in
> tests/functional/arm/test_quanta_gsj.py. The test is not run by
> default, so nobody noticed. But if you run it like this:
>
> QEMU_TEST_ALLOW_SLOW=1 QEMU_TEST_QEMU_BINARY=./qemu-system-arm ./run \
> ~/devel/qemu/tests/functional/arm/test_quanta_gsj.py
>
> you'll notice that the test never finishes on the current master
> branch. When switching back to the commit before the "enable WFE"
> patch, the test works fine again and finishes within a minute or two
> here.
>
> Could you please have a look when you've got some spare time?
It works on my current dev tree, probably because I have:
Message-ID: <20261012134840.1051969-1-alex.bennee@linaro.org>
Date: Mon, 12 Oct 2026 14:48:40 +0100
Subject: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org>
which needs a light re-spin when I have some spare time (sorry it's
review season so I'm trying to get them all done by EoTM).
>
> Thanks,
> Thomas
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile
2026-10-26 16:12 ` Alex Bennée
@ 2026-10-26 16:54 ` Thomas Huth
0 siblings, 0 replies; 14+ messages in thread
From: Thomas Huth @ 2026-10-26 16:54 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, Peter Maydell, kvm, Troy Lee, Hao Wu, Kane Chen,
Mohamed Mediouni, Steven Lee, Joel Stanley, Jamin Lin,
Pedro Barbuda, Paolo Bonzini, Tyrone Ting, Andrew Jeffery,
qemu-arm, Alexander Graf, Cédric Le Goater,
Richard Henderson
On 26/10/2026 17.12, Alex Bennée wrote:
> Thomas Huth <thuth@redhat.com> writes:
>
>> On 24/06/2026 12.30, Alex Bennée wrote:
>>> To enable full architectural behaviour for A-profile we need to do a
>>> number of things:
>>> - add support for the event stream to wake things up
>>> - add support for potential trap on sleep
>>> - handle the global monitor's interactions with WFE
>>> - remove the M-profile specific gates
>> Hi Alex,
>>
>> this patch broke the test_arm_quanta_gsj test in
>> tests/functional/arm/test_quanta_gsj.py. The test is not run by
>> default, so nobody noticed. But if you run it like this:
>>
>> QEMU_TEST_ALLOW_SLOW=1 QEMU_TEST_QEMU_BINARY=./qemu-system-arm ./run \
>> ~/devel/qemu/tests/functional/arm/test_quanta_gsj.py
>>
>> you'll notice that the test never finishes on the current master
>> branch. When switching back to the commit before the "enable WFE"
>> patch, the test works fine again and finishes within a minute or two
>> here.
>>
>> Could you please have a look when you've got some spare time?
>
> It works on my current dev tree, probably because I have:
>
> Message-ID: <20261012134840.1051969-1-alex.bennee@linaro.org>
> Date: Mon, 12 Oct 2026 14:48:40 +0100
> Subject: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
> From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org>
I gave it a quick try, and this patch seems to fix the problem, indeed.
Thanks,
Thomas
PS: I think we should remove @@skipSlowTest decorator from the quanta test
once your patch has been merged - the test does not seem to run that slow
that the decorator is really justified here.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v9 7/7] target/arm: implement WFET
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
` (5 preceding siblings ...)
2026-06-24 10:30 ` [PATCH v9 6/7] target/arm: enable WFE sleeping for A-profile Alex Bennée
@ 2026-06-24 10:30 ` Alex Bennée
2026-07-03 9:59 ` [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Peter Maydell
7 siblings, 0 replies; 14+ messages in thread
From: Alex Bennée @ 2026-06-24 10:30 UTC (permalink / raw)
To: qemu-devel
Cc: kvm, Troy Lee, Peter Maydell, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater, Alex Bennée, Richard Henderson
Now we have the event stream and SEV/SEVL implemented we can finally
enable WFET for Aarch64.
To avoid issues with QEMU's incomplete ldst exclusive handling causing
potential deadlocks in common WFE enabled locking patterns we take
advantage of the architectures flexibility and treat being in the
exclusive region as a reason to exit.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
v2
- fix exception syndrome by using enum value
- use env->halt_reason
v3
- fix check_wfx_trap(s/false/true/) as it is a WFE
v4
- defer expensive calculations until needed
- treat cs->exclusive_addr as a IMPDEF WFE exit
- update commit message
v5
- use atomic_xchg to consume event_register
---
target/arm/tcg/helper-defs.h | 1 +
target/arm/tcg/op_helper.c | 94 ++++++++++++++++++++++++++++++++++
target/arm/tcg/translate-a64.c | 15 +++---
3 files changed, 103 insertions(+), 7 deletions(-)
diff --git a/target/arm/tcg/helper-defs.h b/target/arm/tcg/helper-defs.h
index 99ebd754942..0077aeb4e22 100644
--- a/target/arm/tcg/helper-defs.h
+++ b/target/arm/tcg/helper-defs.h
@@ -56,6 +56,7 @@ DEF_HELPER_1(setend, void, env)
DEF_HELPER_2(wfi, void, env, i32)
DEF_HELPER_2(wfe, void, env, i32)
DEF_HELPER_2(wfit, void, env, i32)
+DEF_HELPER_2(wfet, void, env, i32)
DEF_HELPER_1(yield, void, env)
DEF_HELPER_1(pre_hvc, void, env)
DEF_HELPER_2(pre_smc, void, env, i32)
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index 3321e29898d..c4433be2ed0 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -641,6 +641,100 @@ void HELPER(wfe)(CPUARMState *env, uint32_t insn_len)
#endif
}
+void HELPER(wfet)(CPUARMState *env, uint32_t rd)
+{
+#ifdef CONFIG_USER_ONLY
+ /*
+ * As for WFIT make it NOP here, because trying to raise EXCP_HLT
+ * would trigger an abort.
+ */
+ return;
+#else
+ CPUState *cs = env_cpu(env);
+ uint32_t excp;
+ int target_el;
+ ARMCPU *cpu;
+ uint64_t cntval, timeout, offset, cntvct, nexttick;
+ int64_t next_event;
+
+ /*
+ * As for WFE if the event register is already set we can consume
+ * the event and return immediately.
+ */
+ if (qatomic_xchg(&env->event_register, false)) {
+ return;
+ }
+
+ /*
+ * Don't bother to go into our "low power state" if
+ * we would just wake up immediately.
+ *
+ * We want the value that we would get if we read CNTVCT_EL0 from
+ * the current exception level, so the direct_access offset, not
+ * the indirect_access one. Compare the pseudocode LocalTimeoutEvent(),
+ * which calls VirtualCounterTimer().
+ */
+ cntval = gt_get_countervalue(env);
+ offset = gt_direct_access_timer_offset(env, GTIMER_VIRT);
+ cntvct = cntval - offset;
+ timeout = env->xregs[rd];
+ if (cpu_has_work(cs) || cntvct >= timeout) {
+ return;
+ }
+
+ /* We might sleep, so now we check to see if we should trap */
+ target_el = check_wfx_trap(env, true, &excp);
+ if (target_el) {
+ env->pc -= 4;
+ raise_exception(env, excp, syn_wfx(1, 0xe, rd, true, WFET, false), target_el);
+ }
+
+ /*
+ * If the CPU has entered the exclusive region we could sleep
+ * until the global monitor moves from Exclusive to Open Access.
+ * However it would be expensive for QEMU to fully model the
+ * global monitor and not doing so would potentially trigger
+ * deadlocks in WFE enabled locking code. However as WFE is a hint
+ * instruction the architecture allows for the PE to leave
+ * low-power state for any reason. QEMU chooses to treat being in
+ * an exclusive region as such and return directly.
+ */
+ if (env->exclusive_addr != -1) {
+ return;
+ }
+
+ /*
+ * Finally work out if the timeout or event stream will kick in
+ * earlier.
+ *
+ * The WFET should time out when CNTVCT_EL0 >= the specified value.
+ */
+ cpu = env_archcpu(env);
+ if (uadd64_overflow(timeout, offset, &nexttick)) {
+ nexttick = UINT64_MAX;
+ }
+ if (nexttick > INT64_MAX / gt_cntfrq_period_ns(cpu)) {
+ nexttick = INT64_MAX;
+ }
+
+ next_event = gt_calc_next_event_stream(env);
+ if (next_event > 0 && next_event < nexttick) {
+ timer_mod(cpu->wfxt_timer, next_event);
+ } else {
+ if (nexttick == INT64_MAX) {
+ timer_mod_ns(cpu->wfxt_timer, INT64_MAX);
+ } else {
+ timer_mod(cpu->wfxt_timer, nexttick);
+ }
+ }
+
+ env->halt_reason = HALT_WFE;
+ cs->exception_index = EXCP_HLT;
+ cs->halted = 1;
+ cpu_loop_exit(cs);
+#endif
+}
+
void HELPER(yield)(CPUARMState *env)
{
CPUState *cs = env_cpu(env);
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index b45aac6d269..4a24e0a7fa0 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -2195,14 +2195,15 @@ static bool trans_WFET(DisasContext *s, arg_WFET *a)
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;
+ if (s->ss_active) {
+ /* Act like a NOP under architectural singlestep */
+ return true;
}
+
+ gen_a64_update_pc(s, 4);
+ gen_helper_wfet(tcg_env, tcg_constant_i32(a->rd));
+ /* Go back to the main loop to check for interrupts */
+ s->base.is_jmp = DISAS_EXIT;
return true;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile
2026-06-24 10:30 [PATCH v9 0/7] target/arm: fully model WFxT instructions for A-profile Alex Bennée
` (6 preceding siblings ...)
2026-06-24 10:30 ` [PATCH v9 7/7] target/arm: implement WFET Alex Bennée
@ 2026-07-03 9:59 ` Peter Maydell
7 siblings, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2026-07-03 9:59 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, kvm, Troy Lee, Hao Wu, Kane Chen, Mohamed Mediouni,
Steven Lee, Joel Stanley, Jamin Lin, Pedro Barbuda, Paolo Bonzini,
Tyrone Ting, Andrew Jeffery, qemu-arm, Alexander Graf,
Cédric Le Goater
On Wed, 24 Jun 2026 at 11:30, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> This series fully models the behaviour of WFxT instructions. We
> already had support for WFE for M-profile but we left off A-profile as
> it has more potential sources of wake-ups. The main one is the event
> stream which includes events from significant bits of the timer
> ticking over.
>
> The refactoring from the previous iterations of the patch have now
> been merged.
>
> I've dropped trying to fully model the global monitor in favour of
> taking advantage of the architectural flexibility to have an IMPDEF
> event wake up for any reason. We treat the setting of exclusive_addr
> as such a reason because it indicates the current vCPU is in a ldstx
> exclusive region and we don't want the guest to deadlock. This does mean
> the system won't sleep on WFE enabled locks but people shouldn't be
> relying on QEMU to model real world sleep patterns anyway given the
> efficiency of emulation compared to real HW.
>
Applied to target-arm.next, thanks.
-- PMM
^ permalink raw reply [flat|nested] 14+ messages in thread