mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-10-28 14:04:39 +08:00
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:
1 parent
37f482c0f8
commit
21a446d057
11 files changed
+793
-2
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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']},
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user