| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) 2019,2021 Arm Limited | |
| * Original author: Dave Martin <Dave.Martin@arm.com> | |
| */ | |
| #include "system.h" | |
| #include <stdbool.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 "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; | |
| } | |
| /* Does the system have BTI? */ | |
| static bool have_bti; | |
| static void __do_test(void (*trampoline)(void (*)(void)), | |
| void (*fn)(void), | |
| const char *trampoline_name, | |
| const char *name, | |
| int expect_sigill) | |
| { | |
| /* | |
| * Branch Target exceptions should only happen for BTI | |
| * binaries running on a system with BTI: | |
| */ | |
| if (!BTI || !have_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"); | |
| have_bti = true; | |
| } else { | |
| putstr("# HWCAP2_BTI not present\n"); | |
| have_bti = false; | |
| } | |
| 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); | |
| } |