Commit d88781a4e8 for qemu.org

commit d88781a4e8881bae032802f2b5440274ceece15a
Author: Peter Maydell <peter.maydell@linaro.org>
Date:   Fri Jul 31 11:24:27 2026 +0100

    target/arm: Make YIELD, WFI and WFE be NOPs on pre-v6K

    The YIELD, WFI and WFE instructions are in the NOP hint space, and
    were only defined to actual non-NOP instructions starting in the v6K
    architecture; they are also present for all M-profile architecture
    versions.

    We never did check the architecture version before making these
    instructions have their special behaviour.  Mostly this has not been
    a problem because a guest won't execute one of these insns unless it
    is prepared for it to have its usual effect, and because we
    implemented SEV and WFE as NOPs anyway.

    Now we have implemented SEV and WFE to be more than just NOPs, it's
    important that we have the same condition on the SEV as the WFE, so
    that we either NOP both or else implement both.  A guest probably
    won't try to use SEV/WFE on CPUs that don't implement them, but it is
    valid for it to do that and rely on them both being NOPs (and so a
    WFE-loop falls back to a pure busy-wait loop).

    Add the "only if M profile or v6K or better" check to YIELD, WFE and
    WFI.  This means that all the insns in the NOP-hint space for A32,
    T32 and T16 have a correct feature check.

    Fixes: 60e7ee5bb7cd ("target/arm: implements SEV/SEVL for all modes")
    Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
    Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
    Message-id: 20260728111629.1705308-3-peter.maydell@linaro.org

diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 6e52ad082e..055f3c5b40 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -3235,10 +3235,14 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a)
      * the next round-robin scheduled vCPU gets a crack.  When running in
      * MTTCG we don't generate jumps to the helper as it won't affect the
      * scheduling of other vCPUs.
+     * This is a NOP hint on older architectures.
      */
-    if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
-        gen_update_pc(s, curr_insn_len(s));
-        s->base.is_jmp = DISAS_YIELD;
+    if (arm_dc_feature(s, ARM_FEATURE_M) ||
+        arm_dc_feature(s, ARM_FEATURE_V6K)) {
+        if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
+            gen_update_pc(s, curr_insn_len(s));
+            s->base.is_jmp = DISAS_YIELD;
+        }
     }
     return true;
 }
@@ -3280,17 +3284,30 @@ static bool trans_SEVL(DisasContext *s, arg_SEV *a)

 static bool trans_WFE(DisasContext *s, arg_WFE *a)
 {
-    /* For WFE, halt the vCPU until an event. */
-    gen_update_pc(s, curr_insn_len(s));
-    s->base.is_jmp = DISAS_WFE;
+    /*
+     * For WFE, halt the vCPU until an event. This is a NOP
+     * hint on older architectures, with the same conditions
+     * as SEV.
+     */
+    if (arm_dc_feature(s, ARM_FEATURE_M) ||
+        arm_dc_feature(s, ARM_FEATURE_V6K)) {
+        gen_update_pc(s, curr_insn_len(s));
+        s->base.is_jmp = DISAS_WFE;
+    }
     return true;
 }

 static bool trans_WFI(DisasContext *s, arg_WFI *a)
 {
-    /* For WFI, halt the vCPU until an IRQ. */
-    gen_update_pc(s, curr_insn_len(s));
-    s->base.is_jmp = DISAS_WFI;
+    /*
+     * For WFI, halt the vCPU until an IRQ. This is a NOP
+     * hint on older architectures.
+     */
+    if (arm_dc_feature(s, ARM_FEATURE_M) ||
+        arm_dc_feature(s, ARM_FEATURE_V6K)) {
+        gen_update_pc(s, curr_insn_len(s));
+        s->base.is_jmp = DISAS_WFI;
+    }
     return true;
 }