236 lines
4.9 KiB
C
236 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2019,2021 Arm Limited
|
|
* Original author: Dave Martin <Dave.Martin@arm.com>
|
|
*/
|
|
|
|
#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);
|
|
}
|