| 1 | /* |
| 2 | * Test detection of multiple writes to the same register. |
| 3 | * |
| 4 | * Ported from the system test (tests/tcg/hexagon/system/multiple_writes.c). |
| 5 | * In linux-user mode, duplicate GPR writes are detected at translate time |
| 6 | * and raise SIGILL when at least one conflicting write is unconditional. |
| 7 | * Purely predicated duplicate writes (e.g., complementary if/if-not) are |
| 8 | * legal and are not flagged statically. |
| 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 | |
| 23 | static void handle_sigill(int sig, siginfo_t *info, void *puc) |
| 24 | { |
| 25 | ucontext_t *uc = (ucontext_t *)puc; |
| 26 | |
| 27 | if (sig != SIGILL) { |
| 28 | _exit(EXIT_FAILURE); |
| 29 | } |
| 30 | |
| 31 | uc->uc_mcontext.r0 = SIGILL; |
| 32 | uc->uc_mcontext.pc = (unsigned long)resume_pc; |
| 33 | } |
| 34 | |
| 35 | /* |
| 36 | * Unconditional pair write overlapping a single write: |
| 37 | * { r1:0 = add(r3:2, r3:2); r1 = add(r0, r1) } |
| 38 | * R1 is written by both instructions. This is invalid and must raise SIGILL. |
| 39 | */ |
| 40 | static int test_static_pair_overlap(void) |
| 41 | { |
| 42 | int sig; |
| 43 | |
| 44 | asm volatile( |
| 45 | "r0 = #0\n" |
| 46 | "r1 = ##1f\n" |
| 47 | "memw(%1) = r1\n" |
| 48 | ".word 0xd30242e0\n" /* r1:0 = add(r3:2, r3:2), parse=01 */ |
| 49 | ".word 0xf300c101\n" /* r1 = add(r0, r1), parse=11 (end) */ |
| 50 | "1:\n" |
| 51 | "%0 = r0\n" |
| 52 | : "=r"(sig) |
| 53 | : "r"(&resume_pc) |
| 54 | : "r0", "r1", "memory"); |
| 55 | |
| 56 | return sig; |
| 57 | } |
| 58 | |
| 59 | /* |
| 60 | * Two predicated writes under complementary predicates: |
| 61 | * { if (p0) r0 = r2; if (!p0) r0 = r3 } |
| 62 | * This is architecturally valid: only one write executes at runtime. |
| 63 | * Must NOT raise SIGILL; the result should reflect the executed branch. |
| 64 | */ |
| 65 | static int test_legal_predicated(void) |
| 66 | { |
| 67 | int result; |
| 68 | |
| 69 | asm volatile( |
| 70 | "r0 = #0\n" |
| 71 | "r1 = ##1f\n" |
| 72 | "memw(%1) = r1\n" |
| 73 | "r2 = #7\n" |
| 74 | "r3 = #13\n" |
| 75 | "p0 = cmp.eq(r2, r2)\n" |
| 76 | "{\n" |
| 77 | " if (p0) r0 = r2\n" |
| 78 | " if (!p0) r0 = r3\n" |
| 79 | "}\n" |
| 80 | "1:\n" |
| 81 | "%0 = r0\n" |
| 82 | : "=r"(result) |
| 83 | : "r"(&resume_pc) |
| 84 | : "r0", "r1", "r2", "r3", "p0", "memory"); |
| 85 | |
| 86 | return result; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | * Mixed: unconditional + predicated writes to the same register: |
| 91 | * { if (p0) r1 = add(r0, #0); if (!p0) r1 = add(r0, #0); |
| 92 | * r1 = add(r0, #0) } |
| 93 | * The unconditional write always conflicts with the predicated writes. |
| 94 | * Must raise SIGILL. |
| 95 | */ |
| 96 | static int test_mixed_writes(void) |
| 97 | { |
| 98 | int sig; |
| 99 | |
| 100 | asm volatile( |
| 101 | "r0 = #0\n" |
| 102 | "r1 = ##1f\n" |
| 103 | "memw(%1) = r1\n" |
| 104 | "p0 = cmp.eq(r0, r0)\n" |
| 105 | ".word 0x7e204021\n" /* if (p0) r1 = add(r0, #0), parse=01 */ |
| 106 | ".word 0x7ea04021\n" /* if (!p0) r1 = add(r0, #0), parse=01 */ |
| 107 | ".word 0x7800c021\n" /* r1 = add(r0, #0), parse=11 (end) */ |
| 108 | "1:\n" |
| 109 | "%0 = r0\n" |
| 110 | : "=r"(sig) |
| 111 | : "r"(&resume_pc) |
| 112 | : "r0", "r1", "p0", "memory"); |
| 113 | |
| 114 | return sig; |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | * Zero encoding (issue #2696): |
| 119 | * The encoding 0x00000000 decodes as a duplex with parse bits |
| 120 | * [15:14] = 0b00: |
| 121 | * slot1: SL1_loadri_io R0 = memw(R0+#0x0) |
| 122 | * slot0: SL1_loadri_io R0 = memw(R0+#0x0) |
| 123 | * |
| 124 | * Both sub-instructions write R0 unconditionally, which is an invalid |
| 125 | * packet. This tests what happens when we jump to zeroed memory. |
| 126 | * Must raise SIGILL. |
| 127 | */ |
| 128 | static int test_zero(void) |
| 129 | { |
| 130 | int sig; |
| 131 | |
| 132 | asm volatile( |
| 133 | "r0 = #0\n" |
| 134 | "r1 = ##1f\n" |
| 135 | "memw(%1) = r1\n" |
| 136 | ".word 0x00000000\n" |
| 137 | "1:\n" |
| 138 | "%0 = r0\n" |
| 139 | : "=r"(sig) |
| 140 | : "r"(&resume_pc) |
| 141 | : "r0", "r1", "memory"); |
| 142 | |
| 143 | return sig; |
| 144 | } |
| 145 | |
| 146 | int main() |
| 147 | { |
| 148 | struct sigaction act; |
| 149 | |
| 150 | memset(&act, 0, sizeof(act)); |
| 151 | act.sa_sigaction = handle_sigill; |
| 152 | act.sa_flags = SA_SIGINFO; |
| 153 | assert(sigaction(SIGILL, &act, NULL) == 0); |
| 154 | |
| 155 | /* Legal: complementary predicated writes must not raise SIGILL */ |
| 156 | assert(test_legal_predicated() == 7); |
| 157 | |
| 158 | /* Illegal: unconditional pair + single overlap must raise SIGILL */ |
| 159 | assert(test_static_pair_overlap() == SIGILL); |
| 160 | |
| 161 | /* Illegal: unconditional + predicated writes to same reg must SIGILL */ |
| 162 | assert(test_mixed_writes() == SIGILL); |
| 163 | |
| 164 | /* Illegal: zero encoding = duplex with duplicate dest R0 */ |
| 165 | assert(test_zero() == SIGILL); |
| 166 | |
| 167 | puts("PASS"); |
| 168 | return EXIT_SUCCESS; |
| 169 | } |