Commit 7ba06c8553 for qemu.org

commit 7ba06c85534da97a36f8d9e6a485a41d540d0397
Author: Brian Cain <brian.cain@oss.qualcomm.com>
Date:   Tue Sep 8 21:01:30 2026 -0700

    target/hexagon: add the v73 J2_unpause scalar instruction

    v73 already added J2_callrh and J2_jumprh but unintentionally
    omitted `unpause`.  Its effect is to cause other threads' pause
    instructions to cease pausing, but we treat `pause` as a nop or
    a yield in RR, so unpause can safely be a nop.

    While here, spell the other two v73 entries' version as HEX_VER_V73
    instead of the literal 0x73, matching every other entry in the table.

    Reviewed-by: Marco Liebel <marco.liebel@oss.qualcomm.com>
    Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def
index d9e8ea2aeb..1fce318b2b 100644
--- a/target/hexagon/imported/encode_pp.def
+++ b/target/hexagon/imported/encode_pp.def
@@ -585,6 +585,7 @@ DEF_FIELDROW_DESC32(ICLASS_J" 0100 -------- PP------ --------","[#4] (#u8) ")
 DEF_ENC32(J2_trap0,     ICLASS_J" 0100  00------  PP-iiiii  ---iii--")
 DEF_ENC32(J2_trap1,     ICLASS_J" 0100  10-xxxxx  PP-iiiii  ---iii--")
 DEF_ENC32(J2_pause,     ICLASS_J" 0100  01----ii  PP-iiiii  ---iii--")
+DEF_ENC32(J2_unpause,"01010111111-----PP01----000-----")

 DEF_FIELDROW_DESC32(ICLASS_J" 0101 -------- PP------ --------","[#5] Rd=(Rs) ")
 DEF_ENC32(Y2_icdatar,   ICLASS_J" 0101  101sssss  PP------  ---ddddd")
diff --git a/target/hexagon/imported/system.idef b/target/hexagon/imported/system.idef
index 02aee8ce8e..b44e87565c 100644
--- a/target/hexagon/imported/system.idef
+++ b/target/hexagon/imported/system.idef
@@ -51,6 +51,9 @@ Q6INSN(J2_trap1,"trap1(Rx32,#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPAC
 Q6INSN(J2_pause,"pause(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),
 "Enter low-power state for #u8 cycles",{fPAUSE(uiV);})

+Q6INSN(J2_unpause,"unpause",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),
+"Resume paused threads",{fUNPAUSE();})
+
 Q6INSN(J2_rte,  "rte", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NO_TIMING_LOG),
 "Return from Exception",
 {
diff --git a/target/hexagon/tag_rev_info.c.inc b/target/hexagon/tag_rev_info.c.inc
index a91b91a23e..44513f4680 100644
--- a/target/hexagon/tag_rev_info.c.inc
+++ b/target/hexagon/tag_rev_info.c.inc
@@ -571,8 +571,9 @@ static const struct tag_rev_info tag_rev_info[XX_LAST_OPCODE] = {
     [F2_dfmpyhh] = { .introduced = HEX_VER_V67, .removed = HEX_VER_NONE },
     [F2_dfmpylh] = { .introduced = HEX_VER_V67, .removed = HEX_VER_NONE },
     [F2_dfmpyll] = { .introduced = HEX_VER_V67, .removed = HEX_VER_NONE },
-    [J2_callrh] = { .introduced = 0x73, .removed = HEX_VER_NONE },
-    [J2_jumprh] = { .introduced = 0x73, .removed = HEX_VER_NONE },
+    [J2_callrh] = { .introduced = HEX_VER_V73, .removed = HEX_VER_NONE },
+    [J2_jumprh] = { .introduced = HEX_VER_V73, .removed = HEX_VER_NONE },
+    [J2_unpause] = { .introduced = HEX_VER_V73, .removed = HEX_VER_NONE },
     [L2_loadw_aq] = { .introduced = HEX_VER_V68, .removed = HEX_VER_NONE },
     [L4_loadd_aq] = { .introduced = HEX_VER_V68, .removed = HEX_VER_NONE },
     [Y6_dmcfgrd] = { .introduced = HEX_VER_V68, .removed = HEX_VER_NONE },
diff --git a/tests/tcg/hexagon/check_rev_gating.c b/tests/tcg/hexagon/check_rev_gating.c
index 26b66f5455..d5295bf24a 100644
--- a/tests/tcg/hexagon/check_rev_gating.c
+++ b/tests/tcg/hexagon/check_rev_gating.c
@@ -3,9 +3,12 @@
  * are rejected with SIGILL.
  *
  * Compiled with -mv66 so that e_flags selects CPU v66. The test embeds
- * a v68 instruction (L2_loadw_aq: "r0 = memw_aq(r0)") via .word
- * encoding. The revision-gated decoder must reject it, and linux-user
- * must deliver SIGILL.
+ * instructions from v68 through v73 via .word encoding: the assembler
+ * enforces the selected CPU's own minimum version, so none of these --
+ * including ones it otherwise knows how to assemble at their own
+ * target, such as callrh or unpause -- can be written as themselves in
+ * a file built for v66. The revision-gated decoder must reject every
+ * one of them, and linux-user must deliver SIGILL.
  *
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  * SPDX-License-Identifier: GPL-2.0-or-later
@@ -102,6 +105,8 @@ TRY_FUNC(v73_callrh,
          ".word 0x50c5c000    /* callrh r5 */\n")
 TRY_FUNC(v73_jumprh,
          ".word 0x52c0c000    /* jumprh r0 */\n")
+TRY_FUNC(v73_unpause,
+         ".word 0x57e0d000    /* unpause */\n")

 int main(void)
 {
@@ -133,6 +138,7 @@ int main(void)

     assert(try_v73_callrh() == SIGILL);
     assert(try_v73_jumprh() == SIGILL);
+    assert(try_v73_unpause() == SIGILL);

     assert(signals_handled == expected_signals);

diff --git a/tests/tcg/hexagon/v73_scalar.c b/tests/tcg/hexagon/v73_scalar.c
index fee67fc531..2b5f3aa214 100644
--- a/tests/tcg/hexagon/v73_scalar.c
+++ b/tests/tcg/hexagon/v73_scalar.c
@@ -86,10 +86,17 @@ static void test_jumprh(void)
     check32(res, 1);
 }

+static void test_unpause(void)
+{
+    /* Nothing is paused, so this just needs to execute without faulting. */
+    asm volatile("unpause\n\t");
+}
+
 int main()
 {
     test_callrh();
     test_jumprh();
+    test_unpause();

     puts(err ? "FAIL" : "PASS");
     return err ? 1 : 0;