forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
4.9 KiB
235 lines
4.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2019,2021 Arm Limited |
|
* Original author: Dave Martin <[email protected]> |
|
*/ |
|
|
|
#include "system.h" |
|
|
|
#include <stddef.h> |
|
#include <linux/errno.h> |
|
#include <linux/auxvec.h> |
|
#include <linux/signal.h> |
|
#include <asm/sigcontext.h> |
|
#include <asm/ucontext.h> |
|
|
|
typedef struct ucontext ucontext_t; |
|
|
|
#include "btitest.h" |
|
#include "compiler.h" |
|
#include "signal.h" |
|
|
|
#define EXPECTED_TESTS 18 |
|
|
|
static volatile unsigned int test_num = 1; |
|
static unsigned int test_passed; |
|
static unsigned int test_failed; |
|
static unsigned int test_skipped; |
|
|
|
static void fdputs(int fd, const char *str) |
|
{ |
|
size_t len = 0; |
|
const char *p = str; |
|
|
|
while (*p++) |
|
++len; |
|
|
|
write(fd, str, len); |
|
} |
|
|
|
static void putstr(const char *str) |
|
{ |
|
fdputs(1, str); |
|
} |
|
|
|
static void putnum(unsigned int num) |
|
{ |
|
char c; |
|
|
|
if (num / 10) |
|
putnum(num / 10); |
|
|
|
c = '0' + (num % 10); |
|
write(1, &c, 1); |
|
} |
|
|
|
#define puttestname(test_name, trampoline_name) do { \ |
|
putstr(test_name); \ |
|
putstr("/"); \ |
|
putstr(trampoline_name); \ |
|
} while (0) |
|
|
|
void print_summary(void) |
|
{ |
|
putstr("# Totals: pass:"); |
|
putnum(test_passed); |
|
putstr(" fail:"); |
|
putnum(test_failed); |
|
putstr(" xfail:0 xpass:0 skip:"); |
|
putnum(test_skipped); |
|
putstr(" error:0\n"); |
|
} |
|
|
|
static const char *volatile current_test_name; |
|
static const char *volatile current_trampoline_name; |
|
static volatile int sigill_expected, sigill_received; |
|
|
|
static void handler(int n, siginfo_t *si __always_unused, |
|
void *uc_ __always_unused) |
|
{ |
|
ucontext_t *uc = uc_; |
|
|
|
putstr("# \t[SIGILL in "); |
|
puttestname(current_test_name, current_trampoline_name); |
|
putstr(", BTYPE="); |
|
write(1, &"00011011"[((uc->uc_mcontext.pstate & PSR_BTYPE_MASK) |
|
>> PSR_BTYPE_SHIFT) * 2], 2); |
|
if (!sigill_expected) { |
|
putstr("]\n"); |
|
putstr("not ok "); |
|
putnum(test_num); |
|
putstr(" "); |
|
puttestname(current_test_name, current_trampoline_name); |
|
putstr("(unexpected SIGILL)\n"); |
|
print_summary(); |
|
exit(128 + n); |
|
} |
|
|
|
putstr(" (expected)]\n"); |
|
sigill_received = 1; |
|
/* zap BTYPE so that resuming the faulting code will work */ |
|
uc->uc_mcontext.pstate &= ~PSR_BTYPE_MASK; |
|
} |
|
|
|
static int skip_all; |
|
|
|
static void __do_test(void (*trampoline)(void (*)(void)), |
|
void (*fn)(void), |
|
const char *trampoline_name, |
|
const char *name, |
|
int expect_sigill) |
|
{ |
|
if (skip_all) { |
|
test_skipped++; |
|
putstr("ok "); |
|
putnum(test_num); |
|
putstr(" "); |
|
puttestname(name, trampoline_name); |
|
putstr(" # SKIP\n"); |
|
|
|
return; |
|
} |
|
|
|
/* Branch Target exceptions should only happen in BTI binaries: */ |
|
if (!BTI) |
|
expect_sigill = 0; |
|
|
|
sigill_expected = expect_sigill; |
|
sigill_received = 0; |
|
current_test_name = name; |
|
current_trampoline_name = trampoline_name; |
|
|
|
trampoline(fn); |
|
|
|
if (expect_sigill && !sigill_received) { |
|
putstr("not ok "); |
|
test_failed++; |
|
} else { |
|
putstr("ok "); |
|
test_passed++; |
|
} |
|
putnum(test_num++); |
|
putstr(" "); |
|
puttestname(name, trampoline_name); |
|
putstr("\n"); |
|
} |
|
|
|
#define do_test(expect_sigill_br_x0, \ |
|
expect_sigill_br_x16, \ |
|
expect_sigill_blr, \ |
|
name) \ |
|
do { \ |
|
__do_test(call_using_br_x0, name, "call_using_br_x0", #name, \ |
|
expect_sigill_br_x0); \ |
|
__do_test(call_using_br_x16, name, "call_using_br_x16", #name, \ |
|
expect_sigill_br_x16); \ |
|
__do_test(call_using_blr, name, "call_using_blr", #name, \ |
|
expect_sigill_blr); \ |
|
} while (0) |
|
|
|
void start(int *argcp) |
|
{ |
|
struct sigaction sa; |
|
void *const *p; |
|
const struct auxv_entry { |
|
unsigned long type; |
|
unsigned long val; |
|
} *auxv; |
|
unsigned long hwcap = 0, hwcap2 = 0; |
|
|
|
putstr("TAP version 13\n"); |
|
putstr("1.."); |
|
putnum(EXPECTED_TESTS); |
|
putstr("\n"); |
|
|
|
/* Gross hack for finding AT_HWCAP2 from the initial process stack: */ |
|
p = (void *const *)argcp + 1 + *argcp + 1; /* start of environment */ |
|
/* step over environment */ |
|
while (*p++) |
|
; |
|
for (auxv = (const struct auxv_entry *)p; auxv->type != AT_NULL; ++auxv) { |
|
switch (auxv->type) { |
|
case AT_HWCAP: |
|
hwcap = auxv->val; |
|
break; |
|
case AT_HWCAP2: |
|
hwcap2 = auxv->val; |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
if (hwcap & HWCAP_PACA) |
|
putstr("# HWCAP_PACA present\n"); |
|
else |
|
putstr("# HWCAP_PACA not present\n"); |
|
|
|
if (hwcap2 & HWCAP2_BTI) { |
|
putstr("# HWCAP2_BTI present\n"); |
|
if (!(hwcap & HWCAP_PACA)) |
|
putstr("# Bad hardware? Expect problems.\n"); |
|
} else { |
|
putstr("# HWCAP2_BTI not present\n"); |
|
skip_all = 1; |
|
} |
|
|
|
putstr("# Test binary"); |
|
if (!BTI) |
|
putstr(" not"); |
|
putstr(" built for BTI\n"); |
|
|
|
sa.sa_handler = (sighandler_t)(void *)handler; |
|
sa.sa_flags = SA_SIGINFO; |
|
sigemptyset(&sa.sa_mask); |
|
sigaction(SIGILL, &sa, NULL); |
|
sigaddset(&sa.sa_mask, SIGILL); |
|
sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL); |
|
|
|
do_test(1, 1, 1, nohint_func); |
|
do_test(1, 1, 1, bti_none_func); |
|
do_test(1, 0, 0, bti_c_func); |
|
do_test(0, 0, 1, bti_j_func); |
|
do_test(0, 0, 0, bti_jc_func); |
|
do_test(1, 0, 0, paciasp_func); |
|
|
|
print_summary(); |
|
|
|
if (test_num - 1 != EXPECTED_TESTS) |
|
putstr("# WARNING - EXPECTED TEST COUNT WRONG\n"); |
|
|
|
if (test_failed) |
|
exit(1); |
|
else |
|
exit(0); |
|
}
|
|
|