Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | // 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); } |