mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-28 09:55:18 +00:00
Add some tests that verify that BTI functions correctly for static binaries built with and without BTI support, verifying that SIGILL is generated when expected and is not generated in other situations. Since BTI support is still being rolled out in distributions these tests are built entirely free standing, no libc support is used at all so none of the standard helper functions for kselftest can be used and we open code everything. This also means we aren't testing the kernel support for the dynamic linker, though the test program can be readily adapted for that once it becomes something that we can reliably build and run. These tests were originally written by Dave Martin, I've adapted them for kselftest, mainly around the build system and the output format. Signed-off-by: Mark Brown <broonie@kernel.org> Cc: Dave Martin <Dave.Martin@arm.com> Link: https://lore.kernel.org/r/20210309193731.57247-1-broonie@kernel.org Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
234 lines
4.9 KiB
C
234 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 <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);
|
|
}
|