Re: [PATCH v3 02/11] kselftest: arm64: adds first test and common utils

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Amit

thanks for the review.

On 12/08/2019 13:43, Amit Kachhap wrote:
> Hi Cristian,
> 
> On 8/2/19 10:32 PM, 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
>>
>> 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
>
> I suppose *.h can be removed from the targets here.

*.h are in the pre-reqs, $(PROGS) represent the targets and it's comprised by the *.c file contained in testcases/ (excluding testcases.c)

If I remove the *.h from this rule, targets won't be rebuilt when headers are changed (like after having added an hypotethical inline)...

or am I missing something else ?


> 
> 
>> +	@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>
> Headers can be added in alphabetically order.
> 
Ok I'll do.

Cheers

Cristian

> Thanks,
> Amit D
>> +
>> +/*
>> + * 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
>>




[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux