tests/tcg/hexagon: add v85 scalar tests

Add tests for: vallocframe, atomics, trap2, CFI/restricted branches, and
scalar fp instructions.

New instructions not yet supported by the toolchain are manually encoded
with .word directives.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Reviewed-by: Marco Liebel <marco.liebel@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
This commit is contained in:
Brian Cain committed 2026-10-27 07:23:27 -07:00
1 parent 37f482c0f8
commit 21a446d057
11 files changed
+793 -2

No files matched your search

+111
View File
@@ -109,6 +109,43 @@ static inline int64_t atomic_dec64(int64_t *x)
return old;
}
static uint32_t v85_atomic_inc(uint32_t *counter)
{
uint32_t old_val;
asm volatile("r0 = %0\n"
"r1 = %1\n"
"r2 = #1\n"
".word 0x9001e220\n" /* r0 = atomw_add(r1,r2):aq */
"%0 = r0\n"
: "=r"(old_val)
: "r"(counter)
: "r0", "r1", "r2");
return old_val;
}
static uint64_t v85_atomic_swap64(uint64_t *addr, uint64_t val)
{
asm volatile("r1:0 = %0\n"
"r2 = %1\n"
".word 0x90c2e0a0\n" /* r1:0 = atomd_swap(r2,r1:0):aq */
"%0 = r1:0\n"
: "+r"(val)
: "r"(addr)
: "r0", "r1", "r2");
return val;
}
static void v85_dcfetch_multi(const void *addr)
{
asm volatile("r0 = %0\n"
"r1 = #0\n"
".word 0x9400c840\n" /* dcfetch_multi(r0,r1) */
:
: "r"(addr)
: "r0", "r1", "memory");
}
#define LOOP_CNT 1000
volatile int32_t tick32 = 1; /* Using volatile because we are testing atomics */
volatile int64_t tick64 = 1; /* Using volatile because we are testing atomics */
@@ -159,10 +196,84 @@ void test_pthread(void)
check64(tick64, 1);
}
static void test_v85_swap64(void)
{
#define INITIAL_V1 0xffffffffffffffff
#define INITIAL_V2 0x1111111111111111
uint64_t v1 = INITIAL_V1;
uint64_t v2 = INITIAL_V2;
v2 = v85_atomic_swap64(&v1, v2);
check64(v1, INITIAL_V2);
check64(v2, INITIAL_V1);
v2 = v85_atomic_swap64(&v1, v2);
check64(v1, INITIAL_V1);
check64(v2, INITIAL_V2);
#undef INITIAL_V1
#undef INITIAL_V2
}
#define V85_ROUNDS 1000
#define V85_THREADS 10
static uint32_t v85_global_counter = 1;
static uint32_t v85_started_threads;
static void *v85_counter_thread(void *arg)
{
uint32_t previous;
v85_atomic_inc(&v85_started_threads);
while (v85_started_threads != V85_THREADS) {
asm volatile("pause(#0)");
}
previous = 0;
for (int i = 0; i < V85_ROUNDS; i++) {
uint32_t current = v85_atomic_inc(&v85_global_counter);
if (current <= previous) {
printf("ERROR: counter is not strictly growing. "
"Before: %" PRIu32 ", Now: %" PRIu32 "\n",
previous, current);
err = 1;
return arg;
}
previous = current;
}
return arg;
}
static void test_v85_race(void)
{
pthread_t tids[V85_THREADS];
for (int i = 0; i < V85_THREADS; i++) {
pthread_create(&tids[i], NULL, v85_counter_thread, NULL);
}
for (int i = 0; i < V85_THREADS; i++) {
pthread_join(tids[i], NULL);
}
check32(v85_global_counter, 1 + V85_THREADS * V85_ROUNDS);
}
static void test_v85_inc32(void)
{
uint32_t counter = UINT32_MAX;
check32(v85_atomic_inc(&counter), UINT32_MAX);
check32(counter, 0);
}
int main(int argc, char **argv)
{
test_reservations();
test_pthread();
v85_dcfetch_multi(&v85_global_counter);
test_v85_swap64();
test_v85_race();
test_v85_inc32();
puts(err ? "FAIL" : "PASS");
return err;
}
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
.text
valid_tgt:
.word 0x7f80c000 /* nop_rb */
jumpr r2
invalid_tgt:
nop
jumpr r2
valid_call_tgt:
.word 0x7f80c000 /* nop_rb */
jumpr r31
invalid_call_tgt:
nop
jumpr r31
.macro set_pred reg, id
r4 = \id
r3 = xor(\reg, r4)
p0 = !cmp.eq(r3, #0)
.endm
.macro set_pred_in_packet reg, id
r4 = \id
r3 = xor(\reg, r4)
.word 0x75034010 /* { p0 = !cmp.eq(r3, #0) */
.endm
/*
* r0: nonzero for a valid target, zero for an invalid target
* r1: test case number
*/
.global run_test_case
run_test_case:
r5 = r31
p1 = cmp.eq(r0, #1)
if (p1) r0 = #valid_tgt
if (!p1) r0 = #invalid_tgt
r2 = #return
set_pred r1, #0
.word 0x53e0c000 /* if (!p0) jumpr_rb:nt r0 */
set_pred r1, #1
.word 0x53e0d000 /* if (!p0) jumpr_rb:t r0 */
set_pred_in_packet r1, #2
.word 0x53e0c800 /* if (!p0.new) jumpr_rb:nt r0 } */
set_pred_in_packet r1, #3
.word 0x53e0d800 /* if (!p0.new) jumpr_rb:t r0 } */
p0 = cmp.eq(r1, #4)
.word 0x53c0c000 /* if (p0) jumpr_rb:nt r0 */
p0 = cmp.eq(r1, #5)
.word 0x53c0d000 /* if (p0) jumpr_rb:t r0 */
.word 0x750140c0 /* { p0 = cmp.eq(r1, #6) */
.word 0x53c0c800 /* if (p0.new) jumpr_rb:nt r0 } */
.word 0x750140e0 /* { p0 = cmp.eq(r1, #7) */
.word 0x53c0d800 /* if (p0.new) jumpr_rb:t r0 } */
p0 = cmp.eq(r1, #9)
if (p0) jump callr_case
p0 = cmp.eq(r1, #10)
if (p0) jump callrh_case
p0 = cmp.eq(r1, #11)
if (p0) jump callrt_case
p0 = cmp.eq(r1, #12)
if (p0) jump callrf_case
.word 0x5200c000 /* jumpr_rb r0 */
callr_case:
if (p1) r0 = #valid_call_tgt
if (!p1) r0 = #invalid_call_tgt
.word 0x5020c000 /* callr_rb r0 */
jump return
callrh_case:
if (p1) r0 = #valid_call_tgt
if (!p1) r0 = #invalid_call_tgt
.word 0x5040c000 /* callrh_rb r0 */
jump return
callrt_case:
if (p1) r0 = #valid_call_tgt
if (!p1) r0 = #invalid_call_tgt
p0 = cmp.eq(r0, r0)
.word 0x5180c000 /* if (p0) callr_rb r0 */
jump return
callrf_case:
if (p1) r0 = #valid_call_tgt
if (!p1) r0 = #invalid_call_tgt
p0 = cmp.eq(r0, #0)
.word 0x51a0c000 /* if (!p0) callr_rb r0 */
jump return
return:
jumpr r5
/* jumpr_rb r31 is a return, not a restricted branch. */
.global run_jumpr_rb_lr_test
run_jumpr_rb_lr_test:
.word 0x521fc000 /* jumpr_rb r31 */
/* A target marker and restricted branch in the same packet. */
.global run_jumpr_rb_nop_rb_test
run_jumpr_rb_nop_rb_test:
r0 = #1f
.word 0x7f804000 /* { nop_rb */
.word 0x5200c000 /* jumpr_rb r0 } */
1:
nop
/* A trap0(#0xdb) landing pad: the syscall must not leave CFI armed. */
.global run_trap0_target_test
run_trap0_target_test:
r5 = r31
r6 = #172 /* __NR_getpid */
r0 = #1f
.word 0x5200c000 /* jumpr_rb r0 */
1:
trap0(#0xdb)
nop
jumpr r5
/*
* callr_rb r31 remains restricted: it must branch to a landing pad even
* though it overwrites r31 with its return address.
*/
.global run_callr_rb_lr_test
run_callr_rb_lr_test:
r2 = r31
p0 = cmp.eq(r0, #1)
if (p0) r31 = #valid_callr_rb_lr_tgt
if (!p0) r31 = #invalid_callr_rb_lr_tgt
.word 0x503fc000 /* callr_rb r31 */
jumpr r2
valid_callr_rb_lr_tgt:
.word 0x7f80c000 /* nop_rb */
jumpr r31
invalid_callr_rb_lr_tgt:
nop
jumpr r31
/*
* Enter each restricted allocframe instruction with CFI active. The
* following ordinary instructions show that the restricted target reset it.
*/
.global run_allocframe_test
run_allocframe_test:
r0 = #allocframe_rb_reg
r2 = #allocframe_rb_duplex
.word 0x5200c000 /* jumpr_rb r0 */
allocframe_rb_reg:
.word 0xa09de000 /* allocframe_rb(r29, #0):raw */
deallocframe
nop
jumpr r2
allocframe_rb_duplex:
.word 0x70003e00 /* r0 = r0; allocframe_rb(#0) */
deallocframe
nop
jumpr r31
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef CFI_TGTS_H
#define CFI_TGTS_H
#define CFI_TEST_CASES 13
void run_test_case(int valid, int test_case);
void run_callr_rb_lr_test(int valid);
void run_jumpr_rb_lr_test(void);
void run_jumpr_rb_nop_rb_test(void);
void run_trap0_target_test(void);
void run_allocframe_test(void);
#endif
+99
View File
@@ -0,0 +1,99 @@
/*
* Test a restricted branch interrupted by a data TLB miss.
*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#define _GNU_SOURCE
#include <assert.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
static sigjmp_buf cfi_jmp;
static volatile sig_atomic_t sigsegv_count;
static volatile sig_atomic_t sigill_count;
static void *target;
static void *landing_pad(int valid_target)
{
void *pad;
asm volatile(
"p0 = cmp.eq(%[valid_target], #1)\n\t"
"if (p0) %[pad] = ##valid_cfi_tlb_target\n\t"
"if (!p0) %[pad] = ##invalid_cfi_tlb_target\n\t"
: [pad] "=r"(pad) : [valid_target] "r"(valid_target) : "p0");
return pad;
}
static __attribute__((noinline)) void run_cfi_tlb_test(int valid_target)
{
asm volatile(
"p0 = cmp.eq(%[valid_target], #1)\n\t"
"if (p0) r0 = ##valid_cfi_tlb_target\n\t"
"if (!p0) r0 = ##invalid_cfi_tlb_target\n\t"
"r1 = #0\n\t"
"r2 = #0\n\t"
".word 0x52004000\n\t" /* { jumpr_rb r0 */
".word 0xa181c200\n\t" /* memw(r1 + #0) = r2 } */
"valid_cfi_tlb_target:\n\t"
".word 0x7f80c000\n\t" /* nop_rb */
"jump 1f\n\t"
"invalid_cfi_tlb_target:\n\t"
"nop\n\t"
"1:\n\t"
: : [valid_target] "r"(valid_target)
: "r0", "r1", "r2", "p0", "memory");
}
static void handle_sigsegv(int sig, siginfo_t *info, void *context)
{
ucontext_t *uc = context;
assert(sig == SIGSEGV);
sigsegv_count++;
uc->uc_mcontext.pc = (unsigned long)target;
}
static void handle_sigill(int sig, siginfo_t *info, void *context)
{
assert(sig == SIGILL);
sigill_count++;
siglongjmp(cfi_jmp, 1);
}
int main(void)
{
struct sigaction action;
action.sa_sigaction = handle_sigsegv;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
assert(sigaction(SIGSEGV, &action, NULL) == 0);
action.sa_sigaction = handle_sigill;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
assert(sigaction(SIGILL, &action, NULL) == 0);
target = landing_pad(1);
run_cfi_tlb_test(1);
assert(sigsegv_count == 1);
assert(sigill_count == 0);
target = landing_pad(0);
if (sigsetjmp(cfi_jmp, 1) == 0) {
run_cfi_tlb_test(0);
abort();
}
assert(sigsegv_count == 2);
assert(sigill_count == 1);
puts("PASS");
return EXIT_SUCCESS;
}
+73
View File
@@ -0,0 +1,73 @@
/*
* Test v83 restricted indirect control-flow forms and restricted targets.
*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <assert.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include "cfi_tgts.h"
static sigjmp_buf cfi_jmp;
static volatile sig_atomic_t cfi_signal;
static void handle_sigill(int sig, siginfo_t *info, void *context)
{
assert(sig == SIGILL);
cfi_signal = sig;
siglongjmp(cfi_jmp, 1);
}
int main(void)
{
struct sigaction action;
int i;
action.sa_sigaction = handle_sigill;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
assert(sigaction(SIGILL, &action, NULL) == 0);
for (i = 0; i < CFI_TEST_CASES; i++) {
run_test_case(1, i);
}
for (i = 0; i < CFI_TEST_CASES; i++) {
cfi_signal = 0;
if (sigsetjmp(cfi_jmp, 1) == 0) {
run_test_case(0, i);
abort();
}
assert(cfi_signal == SIGILL);
}
run_jumpr_rb_lr_test();
cfi_signal = 0;
if (sigsetjmp(cfi_jmp, 1) == 0) {
run_jumpr_rb_nop_rb_test();
abort();
}
assert(cfi_signal == SIGILL);
run_trap0_target_test();
run_callr_rb_lr_test(1);
cfi_signal = 0;
if (sigsetjmp(cfi_jmp, 1) == 0) {
run_callr_rb_lr_test(0);
abort();
}
assert(cfi_signal == SIGILL);
run_allocframe_test();
puts("PASS");
return EXIT_SUCCESS;
}
+10
View File
@@ -122,6 +122,13 @@ TRY_FUNC(v79_dczeroa_nt,
TRY_FUNC(v79_dcfetchbo_nt,
".word 0x9402e000 /* dcfetch(r2+#0):nt */\n")
TRY_FUNC(v83_callr_rb,
".word 0x5020c000 /* callr_rb r0 */\n")
TRY_FUNC(v83_nop_rb,
".word 0x7f80c000 /* nop_rb */\n")
TRY_FUNC(v83_vsfadd,
".word 0xeb02c440 /* r1:0 = vsfadd(r3:2, r5:4) */\n")
TRY_FUNC(v79hvx_vmerge_qf,
".word 0x1f01e0e2 /* v2 = vmerge(v0.x, v1.w) */\n")
@@ -165,6 +172,9 @@ int main(void)
assert(try_v79_pstorerif_pi_nt() == SIGILL);
assert(try_v79_dczeroa_nt() == SIGILL);
assert(try_v79_dcfetchbo_nt() == SIGILL);
assert(try_v83_callr_rb() == SIGILL);
assert(try_v83_nop_rb() == SIGILL);
assert(try_v83_vsfadd() == SIGILL);
assert(try_v79hvx_vmerge_qf() == SIGILL);
+20
View File
@@ -89,6 +89,25 @@ static int test_invalid_nonduplex(void)
return sig;
}
/* L4_loadw_phys bit 7 was reserved beginning with v83. */
static int test_v83_loadw_phys_reserved_bit(void)
{
int sig;
asm volatile(
"r0 = #0\n"
"r1 = ##1f\n"
"memw(%1) = r1\n"
".word 0x9200e080\n" /* r0 = memw_phys(r0, r0), bit 7 = 1 */
"1:\n"
"%0 = r0\n"
: "=r"(sig)
: "r"(&resume_pc)
: "r0", "r1", "memory");
return sig;
}
int main()
{
struct sigaction act;
@@ -100,6 +119,7 @@ int main()
assert(test_invalid_duplex() == SIGILL);
assert(test_invalid_nonduplex() == SIGILL);
assert(test_v83_loadw_phys_reserved_bit() == SIGILL);
puts("PASS");
return EXIT_SUCCESS;
+22 -2
View File
@@ -25,8 +25,20 @@ foreach t: tcg_tests['multiarch-linux-user']['tests']
endforeach
tests += {
'atomics.c': {'cflags': cflags},
# The cross assembler does not know v85; v85 instructions in atomics.c
# are encoded with .word directives.
'atomics.c': {'cflags': cflags, 'qemu_args': ['-cpu', 'v85']},
'brev.c': {'cflags': cflags},
# The cross assembler does not know v83; cfi_tlb.c uses raw encodings.
'cfi_tlb.c': {
'cflags': cflags,
'qemu_args': ['-cpu', 'v85'],
},
# The cross assembler does not know v83; cfi_tgts.S uses raw encodings.
'cfi_user.c': {
'cflags': [cflags, files('cfi_tgts.S')],
'qemu_args': ['-cpu', 'v85'],
},
# Compile for v66 so that the ELF selects a v66 CPU; the test then
# exercises revision gating by executing a v68 .word instruction.
'check_rev_gating.c': {
@@ -57,7 +69,7 @@ tests += {
'cflags': [cflags, '-mhvx', '-Wno-gnu-folding-constant',
files('hvx_histogram_row.S')]
},
'invalid-encoding.c': {'cflags': cflags},
'invalid-encoding.c': {'cflags': cflags, 'qemu_args': ['-cpu', 'v85']},
'invalid-slots.c': {'cflags': cflags},
'icinva.c': {'cflags': cflags},
'load_align.c': {'cflags': cflags},
@@ -107,6 +119,8 @@ tests += {
'test_vpmpyh.S': {'cflags': asmflags},
'test_vspliceb.S': {'cflags': asmflags},
'test_vwhist.c': {'cflags': [cflags, '-mhvx']},
# The cross assembler does not know trap2; the test uses its raw encoding.
'trap2.c': {'cflags': cflags, 'qemu_args': ['-cpu', 'v85']},
'unaligned_pc.c': {'cflags': cflags},
'unaligned_data.c': {'cflags': cflags},
'usr.c': {
@@ -118,6 +132,12 @@ tests += {
'v69_hvx.c': {'cflags': [cflags, '-mhvx', '-Wno-unused-function', '-mv69']},
'v73_scalar.c': {'cflags': [cflags, '-Wno-unused-function', '-mv73']},
'v79_scalar.c': {'cflags': [cflags, '-Wno-unused-function', '-mv79']},
'v83_scalar_fp.c': {'cflags': [cflags, '-mv67'],
'qemu_args': ['-cpu', 'v85']},
# The cross assembler does not know v83; the vallocframe instructions in
# this test are encoded with .word directives.
'vallocframe.c': {'cflags': [cflags, '-mv67', '-O2'],
'qemu_args': ['-cpu', 'v85']},
'valid-slots.c': {'cflags': cflags},
'vector_add_int.c': {'cflags': [cflags, '-mhvx', '-fvectorize']},
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <stdint.h>
#include <stdio.h>
uint32_t err;
#include "hex_test.h"
static int trap2_getpid(void)
{
int pid;
asm volatile("r6 = #172\n" /* __NR_getpid */
".word 0x54c0c004\n" /* trap2(#1) */
"%0 = r0\n"
: "=r"(pid)
:
: "r0", "r6");
return pid;
}
int main(void)
{
if (trap2_getpid() <= 0) {
err++;
}
puts(err ? "FAIL" : "PASS");
return err;
}
+98
View File
@@ -0,0 +1,98 @@
/*
* Test the v83 scalar two-lane floating-point instructions.
*
* The assembler does not yet know these instructions, so use their encodings.
*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <stdint.h>
#include <stdio.h>
uint32_t err;
#include "hex_test.h"
static uint64_t vsfadd(uint64_t a, uint64_t b)
{
uint64_t result;
asm volatile("r3:2 = %1\n"
"r5:4 = %2\n"
".word 0xeb02c440\n" /* r1:0 = vsfadd(r3:2, r5:4) */
"%0 = r1:0\n"
: "=r"(result) : "r"(a), "r"(b) : "r0", "r1", "r2",
"r3", "r4", "r5");
return result;
}
static uint64_t vsfsub(uint64_t a, uint64_t b)
{
uint64_t result;
asm volatile("r3:2 = %1\n"
"r5:4 = %2\n"
".word 0xeb02c460\n" /* r1:0 = vsfsub(r3:2, r5:4) */
"%0 = r1:0\n"
: "=r"(result) : "r"(a), "r"(b) : "r0", "r1", "r2",
"r3", "r4", "r5");
return result;
}
static uint64_t vsfmpy(uint64_t a, uint64_t b)
{
uint64_t result;
asm volatile("r3:2 = %1\n"
"r5:4 = %2\n"
".word 0xeb42c420\n" /* r1:0 = vsfmpy(r3:2, r5:4) */
"%0 = r1:0\n"
: "=r"(result) : "r"(a), "r"(b) : "r0", "r1", "r2",
"r3", "r4", "r5");
return result;
}
static uint64_t vsffma(uint64_t a, uint64_t b, uint64_t acc)
{
uint64_t result;
asm volatile("r1:0 = %3\n"
"r3:2 = %1\n"
"r5:4 = %2\n"
".word 0xeb42c440\n" /* r1:0 += vsfmpy(r3:2, r5:4) */
"%0 = r1:0\n"
: "=r"(result) : "r"(a), "r"(b), "r"(acc) : "r0", "r1",
"r2", "r3", "r4", "r5");
return result;
}
static uint64_t vsffms(uint64_t a, uint64_t b, uint64_t acc)
{
uint64_t result;
asm volatile("r1:0 = %3\n"
"r3:2 = %1\n"
"r5:4 = %2\n"
".word 0xeb42c460\n" /* r1:0 -= vsfmpy(r3:2, r5:4) */
"%0 = r1:0\n"
: "=r"(result) : "r"(a), "r"(b), "r"(acc) : "r0", "r1",
"r2", "r3", "r4", "r5");
return result;
}
int main(void)
{
uint64_t a = 0x400000003fc00000ULL;
uint64_t b = 0x4080000040000000ULL;
uint64_t acc = 0x40a000003f800000ULL;
check64(vsfadd(a, b), 0x40c0000040600000ULL);
check64(vsfsub(a, b), 0xc0000000bf000000ULL);
check64(vsfmpy(a, b), 0x4100000040400000ULL);
check64(vsffma(a, b, acc), 0x4150000040800000ULL);
check64(vsffms(a, b, acc), 0xc0400000c0000000ULL);
puts(err ? "FAIL" : "PASS");
return err;
}
+130
View File
@@ -0,0 +1,130 @@
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <stdint.h>
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
uint32_t err;
#include "hex_test.h"
#define VSP_INITIAL_OFFSET (5 * 1024)
#define VALLOCFRAME_VALUE 2
#define VSP_AFTER_VALLOCFRAME \
(VSP_INITIAL_OFFSET - (VALLOCFRAME_VALUE * 2 * 1024))
#define VALLOCFRAME_IMM ".word 0x6080c802\n" /* vallocframe(#2) */
#define VALLOCFRAME_REG ".word 0x6080d000\n" /* vallocframe(r0) */
#define VDEALLOCFRAME_IMM ".word 0x6a80c802\n" /* vdeallocframe(#2) */
#define VDEALLOCFRAME_REG ".word 0x6940d000\n" /* vdeallocframe(r0) */
static uint32_t read_vsp(void)
{
uint32_t value;
asm volatile(".word 0x6a1dc000\n" /* r0 = vsp */
"%0 = r0\n"
: "=r"(value)
:
: "r0");
return value;
}
static void write_vsp(uint32_t value)
{
asm volatile("r0 = %0\n"
".word 0x6220c01d\n" /* vsp = r0 */
:
: "r"(value)
: "r0");
}
static void write_vframelimit(uint32_t value)
{
asm volatile("r0 = %0\n"
".word 0x6220c01c\n" /* vframelimit = r0 */
:
: "r"(value)
: "r0");
}
static uint32_t __attribute__((noinline)) keep(uint32_t a, uint32_t b)
{
write_vsp(b);
return a * 3;
}
static sigjmp_buf overflow_jmp;
static int overflow_sig;
static int overflow_code;
static void overflow_handler(int sig, siginfo_t *info, void *context)
{
overflow_sig = sig;
overflow_code = info->si_code;
siglongjmp(overflow_jmp, 1);
}
static void test_vallocframe_imm(void)
{
write_vsp(VSP_INITIAL_OFFSET);
write_vframelimit(0);
asm volatile(VALLOCFRAME_IMM);
check32(VSP_AFTER_VALLOCFRAME, read_vsp());
asm volatile(VDEALLOCFRAME_IMM);
check32(VSP_INITIAL_OFFSET, read_vsp());
}
static void test_vallocframe_reg(void)
{
uint32_t value = VALLOCFRAME_VALUE;
write_vsp(VSP_INITIAL_OFFSET);
write_vframelimit(0);
asm volatile("r0 = %0\n" VALLOCFRAME_REG : : "r"(value) : "r0");
check32(VSP_AFTER_VALLOCFRAME, read_vsp());
asm volatile("r0 = %0\n" VDEALLOCFRAME_REG : : "r"(value) : "r0");
check32(VSP_INITIAL_OFFSET, read_vsp());
}
static void test_write_vsp_clobber(void)
{
/* Prevent constant folding so a remains live across write_vsp(). */
static volatile uint32_t x = 111, y = 222;
check32(keep(x, y), 333);
}
static void test_vallocframe_overflow(void)
{
struct sigaction action = {
.sa_sigaction = overflow_handler,
.sa_flags = SA_SIGINFO,
};
sigemptyset(&action.sa_mask);
sigaction(SIGSEGV, &action, NULL);
if (sigsetjmp(overflow_jmp, 1) == 0) {
write_vsp(VSP_INITIAL_OFFSET);
write_vframelimit(VSP_AFTER_VALLOCFRAME + 1);
asm volatile(VALLOCFRAME_IMM);
check32(1, 0);
}
check32(SIGSEGV, overflow_sig);
check32(SEGV_BNDERR, overflow_code);
}
int main(void)
{
test_write_vsp_clobber();
test_vallocframe_imm();
test_vallocframe_reg();
test_vallocframe_overflow();
puts(err ? "FAIL" : "PASS");
return err;
}