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);
 | 
						|
}
 |