tests/tcg/hexagon: Add test for revision-gated instruction decoding
Add check_rev_gating, a linux-user test that verifies the decoder rejects instructions from a newer CPU revision than the one selected by the ELF binary's e_flags. Co-authored-by: Taylor Simpson <ltaylorsimpson@gmail.com> Co-authored-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com> Reviewed-by: Marco Liebel <marco.liebel@oss.qualcomm.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Brian Cain committed
Feb 17, 2026 at 14:22 UTC
2030954cf620ab4feb16fb10429244fcc62152b4
2 files changed
+147
tests/tcg/hexagon/Makefile.target
+6
@@ -81,6 +81,7 @@ HEX_TESTS += test_vminh
81
HEX_TESTS += test_vpmpyh
82
HEX_TESTS += test_vspliceb
83
84
+HEX_TESTS += check_rev_gating
85
HEX_TESTS += test_pnew_jump_loads
86
87
HEX_TESTS += v68_scalar
@@ -109,6 +110,11 @@ reg_mut: reg_mut.c hex_test.h
110
test_pnew_jump_loads: test_pnew_jump_loads.c hex_test.h
111
unaligned_pc: unaligned_pc.c
112
113
+# Compile for v66 so that the ELF selects a v66 CPU; the test then
114
+# exercises revision gating by executing a v68 .word instruction.
115
+check_rev_gating: check_rev_gating.c
116
+ $(CC) $(CFLAGS) -mv66 -O2 $< -o $@ $(LDFLAGS)
117
+
118
# This test has to be compiled for the -mv67t target
119
usr: usr.c hex_test.h
120
$(CC) $(CFLAGS) -mv67t -O2 -Wno-inline-asm -Wno-expansion-to-defined $< -o $@ $(LDFLAGS)
tests/tcg/hexagon/check_rev_gating.c
new
+141
@@ -0,0 +1,141 @@
1
+/*
2
+ * Test that instructions from a newer revision than the running CPU
3
+ * are rejected with SIGILL.
4
+ *
5
+ * Compiled with -mv66 so that e_flags selects CPU v66. The test embeds
6
+ * a v68 instruction (L2_loadw_aq: "r0 = memw_aq(r0)") via .word
7
+ * encoding. The revision-gated decoder must reject it, and linux-user
8
+ * must deliver SIGILL.
9
+ *
10
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
11
+ * SPDX-License-Identifier: GPL-2.0-or-later
12
+ */
13
+
14
+#include <assert.h>
15
+#include <signal.h>
16
+#include <stdio.h>
17
+#include <stdlib.h>
18
+#include <string.h>
19
+#include <unistd.h>
20
+
21
+static void *resume_pc;
22
+static int signals_handled;
23
+static int expected_signals;
24
+
25
+static void handle_sigill(int sig, siginfo_t *info, void *puc)
26
+{
27
+ ucontext_t *uc = (ucontext_t *)puc;
28
+
29
+ if (sig != SIGILL) {
30
+ _exit(EXIT_FAILURE);
31
+ }
32
+
33
+ uc->uc_mcontext.r0 = SIGILL;
34
+ uc->uc_mcontext.pc = (unsigned long)resume_pc;
35
+ signals_handled++;
36
+}
37
+
38
+/*
39
+ * Try to execute an instruction introduced after v66
40
+ * On a v66 CPU this must raise SIGILL.
41
+ *
42
+ * Since we are building for v66, the assembler will reject
43
+ * the instructions, so introduce them with .word.
44
+ */
45
+#define TRY_FUNC(NAME, WORD) \
46
+static int try_##NAME(void) \
47
+{ \
48
+ int sig; \
49
+ expected_signals++; \
50
+ asm volatile( \
51
+ "r0 = #0\n" \
52
+ "r1 = ##1f\n" \
53
+ "memw(%1) = r1\n" \
54
+ WORD \
55
+ "1:\n" \
56
+ "%0 = r0\n" \
57
+ : "=r"(sig) \
58
+ : "r"(&resume_pc) \
59
+ : "r0", "r1", "memory"); \
60
+ return sig; \
61
+}
62
+
63
+TRY_FUNC(v68_loadw_aq,
64
+ ".word 0x9200c800 /* { r0 = memw_aq(r0) } */\n")
65
+TRY_FUNC(v68_loadd_aq,
66
+ ".word 0x9201d800 /* r1:0 = memd_aq(r1) */\n")
67
+TRY_FUNC(v68_release_at,
68
+ ".word 0xa0e0c00c /* release(r0):at */\n")
69
+TRY_FUNC(v68_release_st,
70
+ ".word 0xa0e0c02c /* release(r0):st */\n")
71
+TRY_FUNC(v68_storew_rl_at,
72
+ ".word 0xa0a0c108 /* memw_rl(r0):at = r1 */\n")
73
+TRY_FUNC(v68_stored_rl_at,
74
+ ".word 0xa0e2c008 /* memd_rl(r2):at = r1:0 */\n")
75
+TRY_FUNC(v68_storew_rl_st,
76
+ ".word 0xa0a0c128 /* memw_rl(r0):st = r1 */\n")
77
+TRY_FUNC(v68_stored_rl_st,
78
+ ".word 0xa0e2c028 /* memd_rl(r2):st = r1:0 */\n")
79
+
80
+TRY_FUNC(v68hvx_v6mpy,
81
+ ".word 0x1f42e424 /* v5:4.w = v6mpy(v5:4.ub, v3:2.b, #1):v */\n")
82
+
83
+TRY_FUNC(v69hvx_vasrvuhubrndsat,
84
+ ".word 0x1d06c465 /* v5.ub = vasr(v5:4.uh, v6.ub):rnd:sat */\n")
85
+TRY_FUNC(v69hvx_vasrvuhubsat,
86
+ ".word 0x1d06c445 /* v5.ub = vasr(v5:4.uh, v6.ub):sat */\n")
87
+TRY_FUNC(v69hvx_vasrvwuhrndsat,
88
+ ".word 0x1d06c425 /* v5.uh = vasr(v5:4.w, v6.uh):rnd:sat */\n")
89
+TRY_FUNC(v69hvx_vasrvwuhsat,
90
+ ".word 0x1d06c405 /* v5.uh = vasr(v5:4.w, v6.uh):sat */\n")
91
+TRY_FUNC(v69hvx_vassign_tmp,
92
+ ".word 0x1e014dcc /* { v12.tmp = v13 */\n"
93
+ ".word 0x1c43cc04 /* v4.w = vadd(v12.w, v3.w) } */\n")
94
+TRY_FUNC(v69hvx_vcombine_tmp,
95
+ ".word 0x1eae4fec /* { v13:12.tmp = vcombine(v15, v14) */\n"
96
+ ".word 0x1c434c04 /* v4.w = vadd(v12.w, v3.w) */\n"
97
+ ".word 0x1e03edf0 /* v16 = v13 } */\n")
98
+TRY_FUNC(v69hvx_vmpyuhvs,
99
+ ".word 0x1fc5e4e4 /* v4.uh = vmpy(V4.uh, v5.uh):>>16 */\n")
100
+
101
+TRY_FUNC(v73_callrh,
102
+ ".word 0x50c5c000 /* callrh r5 */\n")
103
+TRY_FUNC(v73_jumprh,
104
+ ".word 0x52c0c000 /* jumprh r0 */\n")
105
+
106
+int main(void)
107
+{
108
+ struct sigaction act;
109
+
110
+ memset(&act, 0, sizeof(act));
111
+ act.sa_sigaction = handle_sigill;
112
+ act.sa_flags = SA_SIGINFO;
113
+ assert(sigaction(SIGILL, &act, NULL) == 0);
114
+
115
+ assert(try_v68_loadw_aq() == SIGILL);
116
+ assert(try_v68_loadd_aq() == SIGILL);
117
+ assert(try_v68_release_at() == SIGILL);
118
+ assert(try_v68_release_st() == SIGILL);
119
+ assert(try_v68_storew_rl_at() == SIGILL);
120
+ assert(try_v68_stored_rl_at() == SIGILL);
121
+ assert(try_v68_storew_rl_st() == SIGILL);
122
+ assert(try_v68_stored_rl_st() == SIGILL);
123
+
124
+ assert(try_v68hvx_v6mpy() == SIGILL);
125
+
126
+ assert(try_v69hvx_vasrvuhubrndsat() == SIGILL);
127
+ assert(try_v69hvx_vasrvuhubsat() == SIGILL);
128
+ assert(try_v69hvx_vasrvwuhrndsat() == SIGILL);
129
+ assert(try_v69hvx_vasrvwuhsat() == SIGILL);
130
+ assert(try_v69hvx_vassign_tmp() == SIGILL);
131
+ assert(try_v69hvx_vcombine_tmp() == SIGILL);
132
+ assert(try_v69hvx_vmpyuhvs() == SIGILL);
133
+
134
+ assert(try_v73_callrh() == SIGILL);
135
+ assert(try_v73_jumprh() == SIGILL);
136
+
137
+ assert(signals_handled == expected_signals);
138
+
139
+ puts("PASS");
140
+ return EXIT_SUCCESS;
141
+}