Hi On 02/08/2019 18:02, Cristian Marussi wrote: > Added some arm64/signal specific boilerplate and utility code to help > further testcase development. > > A simple testcase and related helpers are also introduced in this commit: > mangle_pstate_invalid_compat_toggle is a simple mangle testcase which > messes with the ucontext_t from within the sig_handler, trying to toggle > PSTATE state bits to switch the system between 32bit/64bit execution state. > Expects SIGSEGV on test PASS. > > Signed-off-by: Cristian Marussi <cristian.marussi@xxxxxxx> > --- > A few fixes: > - test_arm64_signals.sh runner script generation has been reviewed in order to > be safe against the .gitignore > - using kselftest.h officially provided defines for tests' return values > - removed SAFE_WRITE()/dump_uc() > - looking for si_code==SEGV_ACCERR on SEGV test cases to better understand if > the sigfault had been directly triggered by Kernel > --- > tools/testing/selftests/arm64/Makefile | 2 +- > .../testing/selftests/arm64/signal/.gitignore | 6 + > tools/testing/selftests/arm64/signal/Makefile | 88 ++++++ > tools/testing/selftests/arm64/signal/README | 59 ++++ > .../arm64/signal/test_arm64_signals.src_shell | 55 ++++ > .../selftests/arm64/signal/test_signals.c | 26 ++ > .../selftests/arm64/signal/test_signals.h | 137 +++++++++ > .../arm64/signal/test_signals_utils.c | 261 ++++++++++++++++++ > .../arm64/signal/test_signals_utils.h | 13 + > .../arm64/signal/testcases/.gitignore | 1 + > .../mangle_pstate_invalid_compat_toggle.c | 25 ++ > .../arm64/signal/testcases/testcases.c | 150 ++++++++++ > .../arm64/signal/testcases/testcases.h | 83 ++++++ > 13 files changed, 905 insertions(+), 1 deletion(-) > create mode 100644 tools/testing/selftests/arm64/signal/.gitignore > create mode 100644 tools/testing/selftests/arm64/signal/Makefile > create mode 100644 tools/testing/selftests/arm64/signal/README > create mode 100755 tools/testing/selftests/arm64/signal/test_arm64_signals.src_shell > create mode 100644 tools/testing/selftests/arm64/signal/test_signals.c > create mode 100644 tools/testing/selftests/arm64/signal/test_signals.h > create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.c > create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.h > create mode 100644 tools/testing/selftests/arm64/signal/testcases/.gitignore > create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_compat_toggle.c > create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.c > create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.h > One known issue with this patch, which I've just discovered once I upgraded my local toolchain, is a missing include and a feature_test_macros needed by testcases.h (related to to siginfo_t) like in: diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.h b/tools/testing/selftests/arm64/signal/testcases/testcases.h index 624717c71b1d..80831ffc0bbd 100644 --- a/tools/testing/selftests/arm64/signal/testcases/testcases.h +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.h @@ -7,6 +7,7 @@ #include <unistd.h> #include <ucontext.h> #include <assert.h> +#include <signal.h> /* Architecture specific sigframe definitions */ #include <asm/sigcontext.h> diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile index b3dcf315b5a4..a32d5656bb20 100644 --- a/tools/testing/selftests/arm64/signal/Makefile +++ b/tools/testing/selftests/arm64/signal/Makefile @@ -16,7 +16,7 @@ # and standalone builds top_srcdir = ../../../../.. -CFLAGS += -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/ +CFLAGS += -D_GNU_SOURCE -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/ SRCS := $(filter-out testcases/testcases.c,$(wildcard testcases/*.c)) PROGS := $(patsubst %.c,%,$(SRCS)) Cheers Cristian > diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile > index 03a0d4f71218..af59dc74e0dc 100644 > --- a/tools/testing/selftests/arm64/Makefile > +++ b/tools/testing/selftests/arm64/Makefile > @@ -6,7 +6,7 @@ ARCH ?= $(shell uname -m) > ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/) > > ifeq ("x$(ARCH)", "xarm64") > -SUBDIRS := > +SUBDIRS := signal > else > SUBDIRS := > endif > diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore > new file mode 100644 > index 000000000000..434f65c15f03 > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/.gitignore > @@ -0,0 +1,6 @@ > +# Helper script's internal testcases list (TPROGS) is regenerated > +# each time by Makefile on standalone (non KSFT driven) runs. > +# Committing such list creates a dependency between testcases > +# patches such that they are no more easily revertable. Just ignore. > +test_arm64_signals.src_shell > +test_arm64_signals.sh > diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile > new file mode 100644 > index 000000000000..8c8d08be4b0d > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/Makefile > @@ -0,0 +1,88 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (C) 2019 ARM Limited > + > +# Supports also standalone invokation out of KSFT-tree > +# Compile standalone and run on your device with: > +# > +# $ make -C tools/testing/selftests/arm64/signal INSTALL_PATH=<your-dir> install > +# > +# Run standalone on device with: > +# > +# $ <your-device-instdir>/test_arm64_signals.sh [-k|-v] > +# > +# If INSTALL_PATH= is NOT provided it will default to ./install > + > +# A proper top_srcdir is needed both by KSFT(lib.mk) > +# and standalone builds > +top_srcdir = ../../../../.. > + > +CFLAGS += -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/ > +SRCS := $(filter-out testcases/testcases.c,$(wildcard testcases/*.c)) > +PROGS := $(patsubst %.c,%,$(SRCS)) > + > +# Guessing as best as we can where the Kernel headers > +# could have been installed depending on ENV config and > +# type of invocation. > +ifeq ($(KBUILD_OUTPUT),) > +khdr_dir = $(top_srcdir)/usr/include > +else > +ifeq (0,$(MAKELEVEL)) > +khdr_dir = $(KBUILD_OUTPUT)/usr/include > +else > +# the KSFT preferred location when KBUILD_OUTPUT is set > +khdr_dir = $(KBUILD_OUTPUT)/kselftest/usr/include > +endif > +endif > + > +CFLAGS += -I$(khdr_dir) > + > +# Standalone run > +ifeq (0,$(MAKELEVEL)) > +CC := $(CROSS_COMPILE)gcc > +RUNNER_SRC = test_arm64_signals.src_shell > +RUNNER = test_arm64_signals.sh > +INSTALL_PATH ?= install/ > + > +all: $(RUNNER) > + > +$(RUNNER): $(PROGS) > + cp $(RUNNER_SRC) $(RUNNER) > + sed -i -e 's#PROGS=.*#PROGS="$(PROGS)"#' $@ > + > +install: all > + mkdir -p $(INSTALL_PATH)/testcases > + cp $(PROGS) $(INSTALL_PATH)/testcases > + cp $(RUNNER) $(INSTALL_PATH)/ > + > +.PHONY clean: > + rm -f $(PROGS) > +# KSFT run > +else > +# Generated binaries to be installed by top KSFT script > +TEST_GEN_PROGS := $(notdir $(PROGS)) > + > +# Get Kernel headers installed and use them. > +KSFT_KHDR_INSTALL := 1 > + > +# This include mk will also mangle the TEST_GEN_PROGS list > +# to account for any OUTPUT target-dirs optionally provided > +# by the toplevel makefile > +include ../../lib.mk > + > +$(TEST_GEN_PROGS): $(PROGS) > + cp $(PROGS) $(OUTPUT)/ > + > +clean: > + $(CLEAN) > + rm -f $(PROGS) > +endif > + > +# Common test-unit targets to build common-layout test-cases executables > +# Needs secondary expansion to properly include the testcase c-file in pre-reqs > +.SECONDEXPANSION: > +$(PROGS): test_signals.c test_signals_utils.c testcases/testcases.c $$@.c test_signals.h test_signals_utils.h testcases/testcases.h > + @if [ ! -d $(khdr_dir) ]; then \ > + echo -n "\n!!! WARNING: $(khdr_dir) NOT FOUND."; \ > + echo "===> Are you sure Kernel Headers have been installed properly ?\n"; \ > + fi > + $(CC) $(CFLAGS) $^ -o $@ > diff --git a/tools/testing/selftests/arm64/signal/README b/tools/testing/selftests/arm64/signal/README > new file mode 100644 > index 000000000000..53f005f7910a > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/README > @@ -0,0 +1,59 @@ > +KSelfTest arm64/signal/ > +======================= > + > +Signals Tests > ++++++++++++++ > + > +- Tests are built around a common main compilation unit: such shared main > + enforces a standard sequence of operations needed to perform a single > + signal-test (setup/trigger/run/result/cleanup) > + > +- The above mentioned ops are configurable on a test-by-test basis: each test > + is described (and configured) using the descriptor signals.h::struct tdescr > + > +- Each signal testcase is compiled into its own executable: a separate > + executable is used for each test since many tests complete successfully > + by receiving some kind of fatal signal from the Kernel, so it's safer > + to run each test unit in its own standalone process, so as to start each > + test from a clean slate. > + > +- New tests can be simply defined in testcases/ dir providing a proper struct > + tdescr overriding all the defaults we wish to change (as of now providing a > + custom run method is mandatory though) > + > +- Signals' test-cases hereafter defined belong currently to two > + principal families: > + > + - 'mangle_' tests: a real signal (SIGUSR1) is raised and used as a trigger > + and then the test case code messes-up with the sigframe ucontext_t from > + inside the sighandler itself. > + > + - 'fake_sigreturn_' tests: a brand new custom artificial sigframe structure > + is placed on the stack and a sigreturn syscall is called to simulate a > + real signal return. This kind of tests does not use a trigger usually and > + they are just fired using some simple included assembly trampoline code. > + > + - Most of these tests are successfully passing if the process gets killed by > + some fatal signal: usually SIGSEGV or SIGBUS. Since while writing this > + kind of tests it is extremely easy in fact to end-up injecting other > + unrelated SEGV bugs in the testcases, it becomes extremely tricky to > + be really sure that the tests are really addressing what they are meant > + to address and they are not instead falling apart due to unplanned bugs > + in the test code. > + In order to alleviate the misery of the life of such test-developer, a few > + helpers are provided: > + > + - a couple of ASSERT_BAD/GOOD_CONTEXT() macros to easily parse a ucontext_t > + and verify if it is indeed GOOD or BAD (depending on what we were > + expecting), using the same logic/perspective as in the arm64 Kernel signals > + routines. > + > + - a sanity mechanism to be used in 'fake_sigreturn_'-alike tests: enabled by > + default it takes care to verify that the test-execution had at least > + successfully progressed up to the stage of triggering the fake sigreturn > + call. > + > + In both cases test results are expected in terms of: > + - some fatal signal sent by the Kernel to the test process > + or > + - analyzing some final regs state > diff --git a/tools/testing/selftests/arm64/signal/test_arm64_signals.src_shell b/tools/testing/selftests/arm64/signal/test_arm64_signals.src_shell > new file mode 100755 > index 000000000000..163e941e2997 > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/test_arm64_signals.src_shell > @@ -0,0 +1,55 @@ > +#!/bin/sh > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (C) 2019 ARM Limited > + > +ret=0 > +keep_on_fail=0 > +err_out="2> /dev/null" > + > +usage() { > + echo "Usage: `basename $0` [-v] [-k]" > + exit 1 > +} > + > +# avoiding getopt to avoid compatibility issues on targets > +# with limited resources > +while [ $# -gt 0 ] > +do > + case $1 in > + "-k") > + keep_on_fail=1 > + ;; > + "-v") > + err_out= > + ;; > + *) > + usage > + ;; > + esac > + shift > +done > + > +TPROGS= > + > +tot=$(echo $TPROGS | wc -w) > + > +# Tests are expected in testcases/ subdir inside the installation path > +workdir="`dirname $0 2>/dev/null`" > +[ -n $workdir ] && cd $workdir > + > +passed=0 > +run=0 > +for test in $TPROGS > +do > + run=$((run + 1)) > + eval ./$test $err_out > + if [ $? != 0 ]; then > + [ $keep_on_fail = 0 ] && echo "===>>> FAILED:: $test <<<===" && ret=1 && break > + else > + passed=$((passed + 1)) > + fi > +done > + > +echo "==>> PASSED: $passed/$run on $tot available tests." > + > +exit $ret > diff --git a/tools/testing/selftests/arm64/signal/test_signals.c b/tools/testing/selftests/arm64/signal/test_signals.c > new file mode 100644 > index 000000000000..3447d7011aec > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/test_signals.c > @@ -0,0 +1,26 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (C) 2019 ARM Limited */ > + > +#include <kselftest.h> > + > +#include "test_signals.h" > +#include "test_signals_utils.h" > + > +struct tdescr *current; > +extern struct tdescr tde; > + > +int main(int argc, char *argv[]) > +{ > + current = &tde; > + > + ksft_print_msg("%s :: %s - SIG_TRIG:%d SIG_OK:%d -- current:%p\n", > + current->name, current->descr, current->sig_trig, > + current->sig_ok, current); > + if (test_setup(current)) { > + if (test_run(current)) > + test_result(current); > + test_cleanup(current); > + } > + > + return current->pass ? KSFT_PASS : KSFT_FAIL; > +} > diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h > new file mode 100644 > index 000000000000..85db3ac44b32 > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/test_signals.h > @@ -0,0 +1,137 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (C) 2019 ARM Limited */ > + > +#ifndef __TEST_SIGNALS_H__ > +#define __TEST_SIGNALS_H__ > + > +#include <assert.h> > +#include <stdbool.h> > +#include <signal.h> > +#include <ucontext.h> > +#include <stdint.h> > + > +/* > + * Using ARCH specific and sanitized Kernel headers installed by KSFT > + * framework since we asked for it by setting flag KSFT_KHDR_INSTALL > + * in our Makefile. > + */ > +#include <asm/ptrace.h> > +#include <asm/hwcap.h> > + > +/* pasted from include/linux/stringify.h */ > +#define __stringify_1(x...) #x > +#define __stringify(x...) __stringify_1(x) > + > +/* > + * Reads a sysreg using the, possibly provided, S3_ encoding in order to > + * avoid inject any dependency on the used toolchain regarding possibly > + * still unsupported ARMv8 extensions. > + * > + * Using a standard mnemonic here to indicate the specific sysreg (like SSBS) > + * would introduce a compile-time dependency on possibly unsupported ARMv8 > + * Extensions: you could end-up failing to build the test depending on the > + * available toolchain. > + * This is undesirable since some tests, even if specifically targeted at some > + * ARMv8 Extensions, can be plausibly run even on hardware lacking the above > + * optional ARM features. (SSBS bit preservation is an example: Kernel handles > + * it transparently not caring at all about the effective set of supported > + * features). > + * On the other side we will expect to observe different behaviours if the > + * feature is supported or not: usually getting a SIGILL when trying to use > + * unsupported features. For this reason we have anyway in place some > + * preliminary run-time checks about the cpu effectively supported features. > + * > + * This helper macro is meant to be used for regs readable at EL0, BUT some > + * EL1 sysregs are indeed readable too through MRS emulation Kernel-mechanism > + * if the required reg is included in the supported encoding space: > + * > + * Documentation/arm64/cpu-feature-regsiters.txt > + * > + * "The infrastructure emulates only the following system register space: > + * Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7 > + */ > +#define get_regval(regname, out) \ > + asm volatile("mrs %0, " __stringify(regname) : "=r" (out) :: "memory") > + > +/* Regs encoding and masks naming copied in from sysreg.h */ > +#define SYS_ID_AA64MMFR1_EL1 S3_0_C0_C7_1 /* MRS Emulated */ > +#define SYS_ID_AA64MMFR2_EL1 S3_0_C0_C7_2 /* MRS Emulated */ > +#define ID_AA64MMFR1_PAN_SHIFT 20 > +#define ID_AA64MMFR2_UAO_SHIFT 4 > + > +/* Local Helpers */ > +#define IS_PAN_SUPPORTED(val) \ > + (!!((val) & (0xfUL << ID_AA64MMFR1_PAN_SHIFT))) > +#define IS_UAO_SUPPORTED(val) \ > + (!!((val) & (0xfUL << ID_AA64MMFR2_UAO_SHIFT))) > + > +#define S3_MRS_SSBS_SYSREG S3_3_C4_C2_6 /* EL0 supported */ > + > +/* > + * Feature flags used in tdescr.feats_required to specify > + * any feature by the test > + */ > +enum { > + FSSBS_BIT, > + FPAN_BIT, > + FUAO_BIT, > + FMAX_END > +}; > + > +#define FEAT_SSBS (1UL << FSSBS_BIT) > +#define FEAT_PAN (1UL << FPAN_BIT) > +#define FEAT_UAO (1UL << FUAO_BIT) > + > +/* > + * A descriptor used to describe and configure a test case. > + * Fields with a non-trivial meaning are described inline in the following. > + */ > +struct tdescr { > + /* KEEP THIS FIELD FIRST for easier lookup from assembly */ > + void *token; > + /* when disabled token based sanity checking is skipped in handler */ > + bool sanity_disabled; > + /* just a name for the test-case; manadatory field */ > + char *name; > + char *descr; > + unsigned long feats_required; > + /* bitmask of effectively supported feats: populated at run-time */ > + unsigned long feats_supported; > + bool feats_ok; > + bool initialized; > + unsigned int minsigstksz; > + /* signum used as a test trigger. Zero if no trigger-signal is used */ > + int sig_trig; > + /* > + * signum considered as a successful test completion. > + * Zero when no signal is expected on success > + */ > + int sig_ok; > + /* signum expected on unsupported CPU features. */ > + int sig_unsupp; > + /* a timeout in second for test completion */ > + unsigned int timeout; > + bool triggered; > + bool pass; > + /* optional sa_flags for the installed handler */ > + int sa_flags; > + ucontext_t saved_uc; > + > + /* a setup function to be called before test starts */ > + int (*setup)(struct tdescr *td); > + void (*cleanup)(struct tdescr *td); > + > + /* an optional function to be used as a trigger for test starting */ > + int (*trigger)(struct tdescr *td); > + /* > + * the actual test-core: invoked differently depending on the > + * presence of the trigger function above; this is mandatory > + */ > + int (*run)(struct tdescr *td, siginfo_t *si, ucontext_t *uc); > + > + /* an optional function for custom results' processing */ > + void (*check_result)(struct tdescr *td); > + > + void *priv; > +}; > +#endif > diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c > new file mode 100644 > index 000000000000..ac0055f6340b > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c > @@ -0,0 +1,261 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (C) 2019 ARM Limited */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <signal.h> > +#include <string.h> > +#include <unistd.h> > +#include <assert.h> > +#include <sys/auxv.h> > +#include <linux/auxvec.h> > +#include <ucontext.h> > + > +#include "test_signals.h" > +#include "test_signals_utils.h" > +#include "testcases/testcases.h" > + > +extern struct tdescr *current; > + > +static char *feats_store[FMAX_END] = { > + "SSBS", > + "PAN", > + "UAO" > +}; > + > +#define MAX_FEATS_SZ 128 > +static inline char *feats_to_string(unsigned long feats) > +{ > + static char feats_string[MAX_FEATS_SZ]; > + > + for (int i = 0; i < FMAX_END && feats_store[i][0]; i++) { > + if (feats & 1UL << i) > + snprintf(feats_string, MAX_FEATS_SZ - 1, "%s %s ", > + feats_string, feats_store[i]); > + } > + > + return feats_string; > +} > + > +static void unblock_signal(int signum) > +{ > + sigset_t sset; > + > + sigemptyset(&sset); > + sigaddset(&sset, signum); > + sigprocmask(SIG_UNBLOCK, &sset, NULL); > +} > + > +static void default_result(struct tdescr *td, bool force_exit) > +{ > + if (td->pass) > + fprintf(stderr, "==>> completed. PASS(1)\n"); > + else > + fprintf(stdout, "==>> completed. FAIL(0)\n"); > + if (force_exit) > + exit(td->pass ? EXIT_SUCCESS : EXIT_FAILURE); > +} > + > +static inline bool are_feats_ok(struct tdescr *td) > +{ > + return td ? td->feats_required == td->feats_supported : 0; > +} > + > +static void default_handler(int signum, siginfo_t *si, void *uc) > +{ > + if (current->sig_trig && signum == current->sig_trig) { > + fprintf(stderr, "Handling SIG_TRIG\n"); > + current->triggered = 1; > + /* ->run was asserted NON-NULL in test_setup() already */ > + current->run(current, si, uc); > + } else if (signum == SIGILL && !current->initialized) { > + /* > + * A SIGILL here while still not initialized means we failed > + * even to asses the existence of features during init > + */ > + fprintf(stdout, > + "Got SIGILL test_init. Marking ALL features UNSUPPORTED.\n"); > + current->feats_supported = 0; > + } else if (current->sig_ok && signum == current->sig_ok) { > + /* it's a bug in the test code when this assert fail */ > + assert(!current->sig_trig || current->triggered); > + fprintf(stderr, > + "SIG_OK -- SP:%p si_addr@:0x%p si_code:%d token@:0x%p offset:%ld\n", > + ((ucontext_t *)uc)->uc_mcontext.sp, > + si->si_addr, si->si_code, current->token, > + current->token - si->si_addr); > + /* > + * fake_sigreturn tests, which have sanity_enabled=1, set, at > + * the very last time, the token field to the SP address used > + * to place the fake sigframe: so token==0 means we never made > + * it to the end, segfaulting well-before, and the test is > + * possibly broken. > + */ > + if (!current->sanity_disabled && !current->token) { > + fprintf(stdout, > + "current->token ZEROED...test is probably broken!\n"); > + assert(0); > + } > + /* > + * Trying to narrow down the SEGV to the ones generated by > + * Kernel itself via arm64_notify_segfault() > + */ > + if (current->sig_ok == SIGSEGV && si->si_code != SEGV_ACCERR) { > + fprintf(stdout, > + "si_code != SEGV_ACCERR...test is probably broken!\n"); > + assert(0); > + } > + fprintf(stderr, "Handling SIG_OK\n"); > + current->pass = 1; > + /* > + * Some tests can lead to SEGV loops: in such a case we want > + * to terminate immediately exiting straight away > + */ > + default_result(current, 1); > + } else { > + if (signum == current->sig_unsupp && !are_feats_ok(current)) { > + fprintf(stderr, "-- RX SIG_UNSUPP on unsupported feature...OK\n"); > + current->pass = 1; > + } else if (signum == SIGALRM && current->timeout) { > + fprintf(stderr, "-- Timeout !\n"); > + } else { > + fprintf(stderr, > + "-- RX UNEXPECTED SIGNAL: %d\n", signum); > + } > + default_result(current, 1); > + } > +} > + > +static int default_setup(struct tdescr *td) > +{ > + struct sigaction sa; > + > + sa.sa_sigaction = default_handler; > + sa.sa_flags = SA_SIGINFO; > + if (td->sa_flags) > + sa.sa_flags |= td->sa_flags; > + sigemptyset(&sa.sa_mask); > + /* uncatchable signals naturally skipped ... */ > + for (int sig = 1; sig < 32; sig++) > + sigaction(sig, &sa, NULL); > + /* > + * RT Signals default disposition is Term but they cannot be > + * generated by the Kernel in response to our tests; so just catch > + * them all and report them as UNEXPECTED signals. > + */ > + for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++) > + sigaction(sig, &sa, NULL); > + > + /* just in case...unblock explicitly all we need */ > + if (td->sig_trig) > + unblock_signal(td->sig_trig); > + if (td->sig_ok) > + unblock_signal(td->sig_ok); > + if (td->sig_unsupp) > + unblock_signal(td->sig_unsupp); > + > + if (td->timeout) { > + unblock_signal(SIGALRM); > + alarm(td->timeout); > + } > + fprintf(stderr, "Registered handlers for all signals.\n"); > + > + return 1; > +} > + > +static inline int default_trigger(struct tdescr *td) > +{ > + return !raise(td->sig_trig); > +} > + > +static int test_init(struct tdescr *td) > +{ > + td->minsigstksz = getauxval(AT_MINSIGSTKSZ); > + if (!td->minsigstksz) > + td->minsigstksz = MINSIGSTKSZ; > + fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz); > + > + if (td->feats_required) { > + bool feats_ok = false; > + td->feats_supported = 0; > + /* > + * Checking for CPU required features using both the > + * auxval and the arm64 MRS Emulation to read sysregs. > + */ > + if (getauxval(AT_HWCAP) & HWCAP_CPUID) { > + uint64_t val = 0; > + > + if (td->feats_required & FEAT_SSBS) { > + /* Uses HWCAP to check capability */ > + if (getauxval(AT_HWCAP) & HWCAP_SSBS) > + td->feats_supported |= FEAT_SSBS; > + } > + if (td->feats_required & FEAT_PAN) { > + /* Uses MRS emulation to check capability */ > + get_regval(SYS_ID_AA64MMFR1_EL1, val); > + if (IS_PAN_SUPPORTED(val)) > + td->feats_supported |= FEAT_PAN; > + } > + if (td->feats_required & FEAT_UAO) { > + /* Uses MRS emulation to check capability */ > + get_regval(SYS_ID_AA64MMFR2_EL1 , val); > + if (IS_UAO_SUPPORTED(val)) > + td->feats_supported |= FEAT_UAO; > + } > + } else { > + fprintf(stderr, > + "HWCAP_CPUID NOT available. Mark ALL feats UNSUPPORTED.\n"); > + } > + feats_ok = are_feats_ok(td); > + fprintf(stderr, > + "Required Features: [%s] %ssupported\n", > + feats_ok ? feats_to_string(td->feats_supported) : > + feats_to_string(td->feats_required ^ td->feats_supported), > + !feats_ok ? "NOT " : ""); > + } > + > + td->initialized = 1; > + return 1; > +} > + > +int test_setup(struct tdescr *td) > +{ > + /* assert core invariants symptom of a rotten testcase */ > + assert(current); > + assert(td); > + assert(td->name); > + assert(td->run); > + > + if (!test_init(td)) > + return 0; > + > + if (td->setup) > + return td->setup(td); > + else > + return default_setup(td); > +} > + > +int test_run(struct tdescr *td) > +{ > + if (td->sig_trig) { > + if (td->trigger) > + return td->trigger(td); > + else > + return default_trigger(td); > + } else { > + return td->run(td, NULL, NULL); > + } > +} > + > +void test_result(struct tdescr *td) > +{ > + if (td->check_result) > + td->check_result(td); > + default_result(td, 0); > +} > + > +void test_cleanup(struct tdescr *td) > +{ > + if (td->cleanup) > + td->cleanup(td); > +} > diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h b/tools/testing/selftests/arm64/signal/test_signals_utils.h > new file mode 100644 > index 000000000000..8658d1a7d4b9 > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (C) 2019 ARM Limited */ > + > +#ifndef __TEST_SIGNALS_UTILS_H__ > +#define __TEST_SIGNALS_UTILS_H__ > + > +#include "test_signals.h" > + > +int test_setup(struct tdescr *td); > +void test_cleanup(struct tdescr *td); > +int test_run(struct tdescr *td); > +void test_result(struct tdescr *td); > +#endif > diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore > new file mode 100644 > index 000000000000..8651272e3cfc > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore > @@ -0,0 +1 @@ > +mangle_pstate_invalid_compat_toggle > diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_compat_toggle.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_compat_toggle.c > new file mode 100644 > index 000000000000..971193e7501b > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_compat_toggle.c > @@ -0,0 +1,25 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (C) 2019 ARM Limited */ > + > +#include "test_signals_utils.h" > +#include "testcases.h" > + > +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, > + ucontext_t *uc) > +{ > + ASSERT_GOOD_CONTEXT(uc); > + > + /* This config should trigger a SIGSEGV by Kernel */ > + uc->uc_mcontext.pstate ^= PSR_MODE32_BIT; > + > + return 1; > +} > + > +struct tdescr tde = { > + .sanity_disabled = true, > + .name = "MANGLE_PSTATE_INVALID_STATE_TOGGLE", > + .descr = "Mangling uc_mcontext with INVALID STATE_TOGGLE", > + .sig_trig = SIGUSR1, > + .sig_ok = SIGSEGV, > + .run = mangle_invalid_pstate_run, > +}; > diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c > new file mode 100644 > index 000000000000..a59785092e1f > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c > @@ -0,0 +1,150 @@ > +#include "testcases.h" > + > +struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic, > + size_t resv_sz, size_t *offset) > +{ > + size_t offs = 0; > + struct _aarch64_ctx *found = NULL; > + > + if (!head || resv_sz < HDR_SZ) > + return found; > + > + do { > + if (head->magic == magic) { > + found = head; > + break; > + } > + offs += head->size; > + head = GET_RESV_NEXT_HEAD(head); > + } while (offs < resv_sz - HDR_SZ); > + > + if (offset) > + *offset = offs; > + > + return found; > +} > + > +bool validate_extra_context(struct extra_context *extra, char **err) > +{ > + struct _aarch64_ctx *term; > + > + if (!extra || !err) > + return false; > + > + fprintf(stderr, "Validating EXTRA...\n"); > + term = GET_RESV_NEXT_HEAD(extra); > + if (!term || term->magic || term->size) { > + *err = "UN-Terminated EXTRA context"; > + return false; > + } > + if (extra->datap & 0x0fUL) > + *err = "Extra DATAP misaligned"; > + else if (extra->size & 0x0fUL) > + *err = "Extra SIZE misaligned"; > + else if (extra->datap != (uint64_t)term + sizeof(*term)) > + *err = "Extra DATAP misplaced (not contiguos)"; > + if (*err) > + return false; > + > + return true; > +} > + > +bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err) > +{ > + bool terminated = false; > + size_t offs = 0; > + int flags = 0; > + struct extra_context *extra = NULL; > + struct _aarch64_ctx *head = > + (struct _aarch64_ctx *)uc->uc_mcontext.__reserved; > + > + if (!err) > + return false; > + /* Walk till the end terminator verifying __reserved contents */ > + while (head && !terminated && offs < resv_sz) { > + if ((uint64_t)head & 0x0fUL) { > + *err = "Misaligned HEAD"; > + return false; > + } > + > + switch (head->magic) { > + case 0: > + if (head->size) > + *err = "Bad size for MAGIC0"; > + else > + terminated = true; > + break; > + case FPSIMD_MAGIC: > + if (flags & FPSIMD_CTX) > + *err = "Multiple FPSIMD_MAGIC"; > + else if (head->size != > + sizeof(struct fpsimd_context)) > + *err = "Bad size for fpsimd_context"; > + flags |= FPSIMD_CTX; > + break; > + case ESR_MAGIC: > + if (head->size != sizeof(struct esr_context)) > + fprintf(stderr, > + "Bad size for esr_context is not an error...just ignore.\n"); > + break; > + case SVE_MAGIC: > + if (flags & SVE_CTX) > + *err = "Multiple SVE_MAGIC"; > + else if (head->size != > + sizeof(struct sve_context)) > + *err = "Bad size for sve_context"; > + flags |= SVE_CTX; > + break; > + case EXTRA_MAGIC: > + if (flags & EXTRA_CTX) > + *err = "Multiple EXTRA_MAGIC"; > + else if (head->size != > + sizeof(struct extra_context)) > + *err = "Bad size for extra_context"; > + flags |= EXTRA_CTX; > + extra = (struct extra_context *)head; > + break; > + case KSFT_BAD_MAGIC: > + /* > + * This is a BAD magic header defined > + * artificially by a testcase and surely > + * unknown to the Kernel parse_user_sigframe(). > + * It MUST cause a Kernel induced SEGV > + */ > + *err = "BAD MAGIC !"; > + break; > + default: > + /* > + * A still unknown Magic: potentially freshly added > + * to the Kernel code and still unknown to the > + * tests. > + */ > + fprintf(stdout, > + "SKIP Unknown MAGIC: 0x%X - Is KSFT arm64/signal up to date ?\n", > + head->magic); > + break; > + } > + > + if (*err) > + return false; > + > + offs += head->size; > + if (resv_sz - offs < sizeof(*head)) { > + *err = "HEAD Overrun"; > + return false; > + } > + > + if (flags & EXTRA_CTX) > + if (!validate_extra_context(extra, err)) > + return false; > + > + head = GET_RESV_NEXT_HEAD(head); > + } > + > + if (terminated && !(flags & FPSIMD_CTX)) { > + *err = "Missing FPSIMD"; > + return false; > + } > + > + return true; > +} > diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.h b/tools/testing/selftests/arm64/signal/testcases/testcases.h > new file mode 100644 > index 000000000000..624717c71b1d > --- /dev/null > +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.h > @@ -0,0 +1,83 @@ > +#ifndef __TESTCASES_H__ > +#define __TESTCASES_H__ > + > +#include <stdio.h> > +#include <stdbool.h> > +#include <stdint.h> > +#include <unistd.h> > +#include <ucontext.h> > +#include <assert.h> > + > +/* Architecture specific sigframe definitions */ > +#include <asm/sigcontext.h> > + > +#define FPSIMD_CTX (1 << 0) > +#define SVE_CTX (1 << 1) > +#define EXTRA_CTX (1 << 2) > + > +#define KSFT_BAD_MAGIC 0xdeadbeef > + > +#define HDR_SZ \ > + sizeof(struct _aarch64_ctx) > + > +#define GET_SF_RESV_HEAD(sf) \ > + (struct _aarch64_ctx *)(&(sf).uc.uc_mcontext.__reserved) > + > +#define GET_SF_RESV_SIZE(sf) \ > + sizeof((sf).uc.uc_mcontext.__reserved) > + > +#define GET_UCP_RESV_SIZE(ucp) \ > + sizeof((ucp)->uc_mcontext.__reserved) > + > +#define ASSERT_BAD_CONTEXT(uc) do { \ > + char *err = NULL; \ > + assert(!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err));\ > + if (err) \ > + fprintf(stderr, \ > + "Using badly built context - ERR: %s\n", err); \ > +} while(0) > + > +#define ASSERT_GOOD_CONTEXT(uc) do { \ > + char *err = NULL; \ > + if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \ > + if (err) \ > + fprintf(stderr, \ > + "Detected BAD context - ERR: %s\n", err);\ > + assert(0); \ > + } else { \ > + fprintf(stderr, "uc context validated.\n"); \ > + } \ > +} while(0) > + > +/* head->size accounts both for payload and header _aarch64_ctx size ! */ > +#define GET_RESV_NEXT_HEAD(h) \ > + (struct _aarch64_ctx *)((char *)(h) + (h)->size) > + > +struct fake_sigframe { > + siginfo_t info; > + ucontext_t uc; > +}; > + > + > +bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err); > + > +bool validate_extra_context(struct extra_context *extra, char **err); > + > +struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic, > + size_t resv_sz, size_t *offset); > + > +static inline struct _aarch64_ctx *get_terminator(struct _aarch64_ctx *head, > + size_t resv_sz, > + size_t *offset) > +{ > + return get_header(head, 0, resv_sz, offset); > +} > + > +static inline void write_terminator_record(struct _aarch64_ctx *tail) > +{ > + if (tail) { > + tail->magic = 0; > + tail->size = 0; > + } > +} > +#endif >