Path: blob/master/tools/testing/selftests/arm64/signal/test_signals_utils.c
26292 views
// SPDX-License-Identifier: GPL-2.01/* Copyright (C) 2019 ARM Limited */23#include <stdio.h>4#include <stdlib.h>5#include <signal.h>6#include <string.h>7#include <unistd.h>8#include <assert.h>9#include <sys/auxv.h>10#include <linux/auxvec.h>11#include <ucontext.h>1213#include <asm/unistd.h>1415#include <kselftest.h>1617#include "test_signals.h"18#include "test_signals_utils.h"19#include "testcases/testcases.h"202122extern struct tdescr *current;2324static int sig_copyctx = SIGTRAP;2526static char const *const feats_names[FMAX_END] = {27" SSBS ",28" SVE ",29" SME ",30" FA64 ",31" SME2 ",32" GCS ",33};3435#define MAX_FEATS_SZ 12836static char feats_string[MAX_FEATS_SZ];3738static inline char *feats_to_string(unsigned long feats)39{40size_t flen = MAX_FEATS_SZ - 1;4142feats_string[0] = '\0';4344for (int i = 0; i < FMAX_END; i++) {45if (feats & (1UL << i)) {46size_t tlen = strlen(feats_names[i]);4748assert(flen > tlen);49flen -= tlen;50strncat(feats_string, feats_names[i], flen);51}52}5354return feats_string;55}5657static void unblock_signal(int signum)58{59sigset_t sset;6061sigemptyset(&sset);62sigaddset(&sset, signum);63sigprocmask(SIG_UNBLOCK, &sset, NULL);64}6566static void default_result(struct tdescr *td, bool force_exit)67{68if (td->result == KSFT_SKIP) {69fprintf(stderr, "==>> completed. SKIP.\n");70} else if (td->pass) {71fprintf(stderr, "==>> completed. PASS(1)\n");72td->result = KSFT_PASS;73} else {74fprintf(stdout, "==>> completed. FAIL(0)\n");75td->result = KSFT_FAIL;76}7778if (force_exit)79exit(td->result);80}8182/*83* The following handle_signal_* helpers are used by main default_handler84* and are meant to return true when signal is handled successfully:85* when false is returned instead, it means that the signal was somehow86* unexpected in that context and it was NOT handled; default_handler will87* take care of such unexpected situations.88*/8990static bool handle_signal_unsupported(struct tdescr *td,91siginfo_t *si, void *uc)92{93if (feats_ok(td))94return false;9596/* Mangling PC to avoid loops on original SIGILL */97((ucontext_t *)uc)->uc_mcontext.pc += 4;9899if (!td->initialized) {100fprintf(stderr,101"Got SIG_UNSUPP @test_init. Ignore.\n");102} else {103fprintf(stderr,104"-- RX SIG_UNSUPP on unsupported feat...OK\n");105td->pass = 1;106default_result(current, 1);107}108109return true;110}111112static bool handle_signal_trigger(struct tdescr *td,113siginfo_t *si, void *uc)114{115td->triggered = 1;116/* ->run was asserted NON-NULL in test_setup() already */117td->run(td, si, uc);118119return true;120}121122static bool handle_signal_ok(struct tdescr *td,123siginfo_t *si, void *uc)124{125/*126* it's a bug in the test code when this assert fail:127* if sig_trig was defined, it must have been used before getting here.128*/129assert(!td->sig_trig || td->triggered);130fprintf(stderr,131"SIG_OK -- SP:0x%llX si_addr@:%p si_code:%d token@:%p offset:%ld\n",132((ucontext_t *)uc)->uc_mcontext.sp,133si->si_addr, si->si_code, td->token, td->token - si->si_addr);134/*135* fake_sigreturn tests, which have sanity_enabled=1, set, at the very136* last time, the token field to the SP address used to place the fake137* sigframe: so token==0 means we never made it to the end,138* segfaulting well-before, and the test is possibly broken.139*/140if (!td->sanity_disabled && !td->token) {141fprintf(stdout,142"current->token ZEROED...test is probably broken!\n");143abort();144}145if (td->sig_ok_code) {146if (si->si_code != td->sig_ok_code) {147fprintf(stdout, "si_code is %d not %d\n",148si->si_code, td->sig_ok_code);149abort();150}151} else {152/*153* Trying to narrow down the SEGV to the ones154* generated by Kernel itself via155* arm64_notify_segfault(). This is a best-effort156* check anyway, and the si_code check may need to157* change if this aspect of the kernel ABI changes.158*/159if (td->sig_ok == SIGSEGV && si->si_code != SEGV_ACCERR) {160fprintf(stdout,161"si_code != SEGV_ACCERR...test is probably broken!\n");162abort();163}164}165td->pass = 1;166/*167* Some tests can lead to SEGV loops: in such a case we want to168* terminate immediately exiting straight away; some others are not169* supposed to outlive the signal handler code, due to the content of170* the fake sigframe which caused the signal itself.171*/172default_result(current, 1);173174return true;175}176177static bool handle_signal_copyctx(struct tdescr *td,178siginfo_t *si, void *uc_in)179{180ucontext_t *uc = uc_in;181struct _aarch64_ctx *head;182struct extra_context *extra, *copied_extra;183size_t offset = 0;184size_t to_copy;185186ASSERT_GOOD_CONTEXT(uc);187188/* Mangling PC to avoid loops on original BRK instr */189uc->uc_mcontext.pc += 4;190191/*192* Check for an preserve any extra data too with fixups.193*/194head = (struct _aarch64_ctx *)uc->uc_mcontext.__reserved;195head = get_header(head, EXTRA_MAGIC, td->live_sz, &offset);196if (head) {197extra = (struct extra_context *)head;198199/*200* The extra buffer must be immediately after the201* extra_context and a 16 byte terminator. Include it202* in the copy, this was previously validated in203* ASSERT_GOOD_CONTEXT().204*/205to_copy = __builtin_offsetof(ucontext_t,206uc_mcontext.__reserved);207to_copy += offset + sizeof(struct extra_context) + 16;208to_copy += extra->size;209copied_extra = (struct extra_context *)&(td->live_uc->uc_mcontext.__reserved[offset]);210} else {211copied_extra = NULL;212to_copy = sizeof(ucontext_t);213}214215if (to_copy > td->live_sz) {216fprintf(stderr,217"Not enough space to grab context, %lu/%lu bytes\n",218td->live_sz, to_copy);219return false;220}221222memcpy(td->live_uc, uc, to_copy);223224/*225* If there was any EXTRA_CONTEXT fix up the size to be the226* struct extra_context and the following terminator record,227* this means that the rest of the code does not need to have228* special handling for the record and we don't need to fix up229* datap for the new location.230*/231if (copied_extra)232copied_extra->head.size = sizeof(*copied_extra) + 16;233234td->live_uc_valid = 1;235fprintf(stderr,236"%lu byte GOOD CONTEXT grabbed from sig_copyctx handler\n",237to_copy);238239return true;240}241242static void default_handler(int signum, siginfo_t *si, void *uc)243{244if (current->sig_unsupp && signum == current->sig_unsupp &&245handle_signal_unsupported(current, si, uc)) {246fprintf(stderr, "Handled SIG_UNSUPP\n");247} else if (current->sig_trig && signum == current->sig_trig &&248handle_signal_trigger(current, si, uc)) {249fprintf(stderr, "Handled SIG_TRIG\n");250} else if (current->sig_ok && signum == current->sig_ok &&251handle_signal_ok(current, si, uc)) {252fprintf(stderr, "Handled SIG_OK\n");253} else if (signum == sig_copyctx && current->live_uc &&254handle_signal_copyctx(current, si, uc)) {255fprintf(stderr, "Handled SIG_COPYCTX\n");256} else {257if (signum == SIGALRM && current->timeout) {258fprintf(stderr, "-- Timeout !\n");259} else {260fprintf(stderr,261"-- RX UNEXPECTED SIGNAL: %d code %d address %p\n",262signum, si->si_code, si->si_addr);263}264default_result(current, 1);265}266}267268static int default_setup(struct tdescr *td)269{270struct sigaction sa;271272sa.sa_sigaction = default_handler;273sa.sa_flags = SA_SIGINFO | SA_RESTART;274sa.sa_flags |= td->sa_flags;275sigemptyset(&sa.sa_mask);276/* uncatchable signals naturally skipped ... */277for (int sig = 1; sig < 32; sig++)278sigaction(sig, &sa, NULL);279/*280* RT Signals default disposition is Term but they cannot be281* generated by the Kernel in response to our tests; so just catch282* them all and report them as UNEXPECTED signals.283*/284for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++)285sigaction(sig, &sa, NULL);286287/* just in case...unblock explicitly all we need */288if (td->sig_trig)289unblock_signal(td->sig_trig);290if (td->sig_ok)291unblock_signal(td->sig_ok);292if (td->sig_unsupp)293unblock_signal(td->sig_unsupp);294295if (td->timeout) {296unblock_signal(SIGALRM);297alarm(td->timeout);298}299fprintf(stderr, "Registered handlers for all signals.\n");300301return 1;302}303304static inline int default_trigger(struct tdescr *td)305{306return !raise(td->sig_trig);307}308309int test_init(struct tdescr *td)310{311if (td->sig_trig == sig_copyctx) {312fprintf(stdout,313"Signal %d is RESERVED, cannot be used as a trigger. Aborting\n",314sig_copyctx);315return 0;316}317/* just in case */318unblock_signal(sig_copyctx);319320td->minsigstksz = getauxval(AT_MINSIGSTKSZ);321if (!td->minsigstksz)322td->minsigstksz = MINSIGSTKSZ;323fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz);324325if (td->feats_required || td->feats_incompatible) {326td->feats_supported = 0;327/*328* Checking for CPU required features using both the329* auxval and the arm64 MRS Emulation to read sysregs.330*/331if (getauxval(AT_HWCAP) & HWCAP_SSBS)332td->feats_supported |= FEAT_SSBS;333if (getauxval(AT_HWCAP) & HWCAP_SVE)334td->feats_supported |= FEAT_SVE;335if (getauxval(AT_HWCAP2) & HWCAP2_SME)336td->feats_supported |= FEAT_SME;337if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)338td->feats_supported |= FEAT_SME_FA64;339if (getauxval(AT_HWCAP2) & HWCAP2_SME2)340td->feats_supported |= FEAT_SME2;341if (getauxval(AT_HWCAP) & HWCAP_GCS)342td->feats_supported |= FEAT_GCS;343if (feats_ok(td)) {344if (td->feats_required & td->feats_supported)345fprintf(stderr,346"Required Features: [%s] supported\n",347feats_to_string(td->feats_required &348td->feats_supported));349if (!(td->feats_incompatible & td->feats_supported))350fprintf(stderr,351"Incompatible Features: [%s] absent\n",352feats_to_string(td->feats_incompatible));353} else {354if ((td->feats_required & td->feats_supported) !=355td->feats_supported)356fprintf(stderr,357"Required Features: [%s] NOT supported\n",358feats_to_string(td->feats_required &359~td->feats_supported));360if (td->feats_incompatible & td->feats_supported)361fprintf(stderr,362"Incompatible Features: [%s] supported\n",363feats_to_string(td->feats_incompatible &364~td->feats_supported));365366367td->result = KSFT_SKIP;368return 0;369}370}371372/* Perform test specific additional initialization */373if (td->init && !td->init(td)) {374fprintf(stderr, "FAILED Testcase initialization.\n");375return 0;376}377td->initialized = 1;378fprintf(stderr, "Testcase initialized.\n");379380return 1;381}382383int test_setup(struct tdescr *td)384{385/* assert core invariants symptom of a rotten testcase */386assert(current);387assert(td);388assert(td->name);389assert(td->run);390391/* Default result is FAIL if test setup fails */392td->result = KSFT_FAIL;393if (td->setup)394return td->setup(td);395else396return default_setup(td);397}398399int test_run(struct tdescr *td)400{401if (td->trigger)402return td->trigger(td);403else if (td->sig_trig)404return default_trigger(td);405else406return td->run(td, NULL, NULL);407}408409void test_result(struct tdescr *td)410{411if (td->initialized && td->result != KSFT_SKIP && td->check_result)412td->check_result(td);413default_result(td, 0);414}415416void test_cleanup(struct tdescr *td)417{418if (td->cleanup)419td->cleanup(td);420}421422423