blob: 8686317fdcce360ccd863661b827cc6a8326fb66 [file] [log] [blame]
/*
* Test detection of multiple writes to the same register.
*
* Ported from the system test (tests/tcg/hexagon/system/multiple_writes.c).
* In linux-user mode, duplicate GPR writes are detected at translate time
* and raise SIGILL when at least one conflicting write is unconditional.
* Purely predicated duplicate writes (e.g., complementary if/if-not) are
* legal and are not flagged statically.
*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void *resume_pc;
static void handle_sigill(int sig, siginfo_t *info, void *puc)
{
ucontext_t *uc = (ucontext_t *)puc;
if (sig != SIGILL) {
_exit(EXIT_FAILURE);
}
uc->uc_mcontext.r0 = SIGILL;
uc->uc_mcontext.pc = (unsigned long)resume_pc;
}
/*
* Unconditional pair write overlapping a single write:
* { r1:0 = add(r3:2, r3:2); r1 = add(r0, r1) }
* R1 is written by both instructions. This is invalid and must raise SIGILL.
*/
static int test_static_pair_overlap(void)
{
int sig;
asm volatile(
"r0 = #0\n"
"r1 = ##1f\n"
"memw(%1) = r1\n"
".word 0xd30242e0\n" /* r1:0 = add(r3:2, r3:2), parse=01 */
".word 0xf300c101\n" /* r1 = add(r0, r1), parse=11 (end) */
"1:\n"
"%0 = r0\n"
: "=r"(sig)
: "r"(&resume_pc)
: "r0", "r1", "memory");
return sig;
}
/*
* Two predicated writes under complementary predicates:
* { if (p0) r0 = r2; if (!p0) r0 = r3 }
* This is architecturally valid: only one write executes at runtime.
* Must NOT raise SIGILL; the result should reflect the executed branch.
*/
static int test_legal_predicated(void)
{
int result;
asm volatile(
"r0 = #0\n"
"r1 = ##1f\n"
"memw(%1) = r1\n"
"r2 = #7\n"
"r3 = #13\n"
"p0 = cmp.eq(r2, r2)\n"
"{\n"
" if (p0) r0 = r2\n"
" if (!p0) r0 = r3\n"
"}\n"
"1:\n"
"%0 = r0\n"
: "=r"(result)
: "r"(&resume_pc)
: "r0", "r1", "r2", "r3", "p0", "memory");
return result;
}
/*
* Mixed: unconditional + predicated writes to the same register:
* { if (p0) r1 = add(r0, #0); if (!p0) r1 = add(r0, #0);
* r1 = add(r0, #0) }
* The unconditional write always conflicts with the predicated writes.
* Must raise SIGILL.
*/
static int test_mixed_writes(void)
{
int sig;
asm volatile(
"r0 = #0\n"
"r1 = ##1f\n"
"memw(%1) = r1\n"
"p0 = cmp.eq(r0, r0)\n"
".word 0x7e204021\n" /* if (p0) r1 = add(r0, #0), parse=01 */
".word 0x7ea04021\n" /* if (!p0) r1 = add(r0, #0), parse=01 */
".word 0x7800c021\n" /* r1 = add(r0, #0), parse=11 (end) */
"1:\n"
"%0 = r0\n"
: "=r"(sig)
: "r"(&resume_pc)
: "r0", "r1", "p0", "memory");
return sig;
}
/*
* Zero encoding (issue #2696):
* The encoding 0x00000000 decodes as a duplex with parse bits
* [15:14] = 0b00:
* slot1: SL1_loadri_io R0 = memw(R0+#0x0)
* slot0: SL1_loadri_io R0 = memw(R0+#0x0)
*
* Both sub-instructions write R0 unconditionally, which is an invalid
* packet. This tests what happens when we jump to zeroed memory.
* Must raise SIGILL.
*/
static int test_zero(void)
{
int sig;
asm volatile(
"r0 = #0\n"
"r1 = ##1f\n"
"memw(%1) = r1\n"
".word 0x00000000\n"
"1:\n"
"%0 = r0\n"
: "=r"(sig)
: "r"(&resume_pc)
: "r0", "r1", "memory");
return sig;
}
int main()
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = handle_sigill;
act.sa_flags = SA_SIGINFO;
assert(sigaction(SIGILL, &act, NULL) == 0);
/* Legal: complementary predicated writes must not raise SIGILL */
assert(test_legal_predicated() == 7);
/* Illegal: unconditional pair + single overlap must raise SIGILL */
assert(test_static_pair_overlap() == SIGILL);
/* Illegal: unconditional + predicated writes to same reg must SIGILL */
assert(test_mixed_writes() == SIGILL);
/* Illegal: zero encoding = duplex with duplicate dest R0 */
assert(test_zero() == SIGILL);
puts("PASS");
return EXIT_SUCCESS;
}