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@arm.com
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
I discovered an additional issue with this patch while building some additional SVE related arm64/signal testcases (ofr a different patch series), which is related to the logic of the helper get_header() that is flawed under some specific conditions (not exposed by the test-cases included in this series).
A fix like the one below will go into V4, once reviews have settled.
diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c index a59785092e1f..0727b3987bbd 100644 --- a/tools/testing/selftests/arm64/signal/testcases/testcases.c +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c @@ -10,9 +10,16 @@ struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic, return found;
do { + /* + * account for the fact that: + * - we could be in search of the terminator itself + * - a terminator should anyway terminate the loop + */ if (head->magic == magic) { found = head; break; + } else if (!head->magic) { + break; } offs += head->size; head = GET_RESV_NEXT_HEAD(head);
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