ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/tools/testing/selftests/powerpc/signal/.gitignore b/marvell/linux/tools/testing/selftests/powerpc/signal/.gitignore
new file mode 100644
index 0000000..dca5852
--- /dev/null
+++ b/marvell/linux/tools/testing/selftests/powerpc/signal/.gitignore
@@ -0,0 +1,3 @@
+signal
+signal_tm
+sigfuz
diff --git a/marvell/linux/tools/testing/selftests/powerpc/signal/Makefile b/marvell/linux/tools/testing/selftests/powerpc/signal/Makefile
new file mode 100644
index 0000000..113838f
--- /dev/null
+++ b/marvell/linux/tools/testing/selftests/powerpc/signal/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+TEST_GEN_PROGS := signal signal_tm sigfuz
+
+CFLAGS += -maltivec
+$(OUTPUT)/signal_tm: CFLAGS += -mhtm
+$(OUTPUT)/sigfuz: CFLAGS += -pthread -m64
+
+top_srcdir = ../../../../..
+include ../../lib.mk
+
+$(TEST_GEN_PROGS): ../harness.c ../utils.c signal.S
diff --git a/marvell/linux/tools/testing/selftests/powerpc/signal/sigfuz.c b/marvell/linux/tools/testing/selftests/powerpc/signal/sigfuz.c
new file mode 100644
index 0000000..dade00c
--- /dev/null
+++ b/marvell/linux/tools/testing/selftests/powerpc/signal/sigfuz.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018, Breno Leitao, IBM Corp.
+ * Licensed under GPLv2.
+ *
+ * Sigfuz(tm): A PowerPC TM-aware signal fuzzer.
+ *
+ * This is a new selftest that raises SIGUSR1 signals and handles it in a set
+ * of different ways, trying to create different scenario for testing
+ * purpose.
+ *
+ * This test works raising a signal and calling sigreturn interleaved with
+ * TM operations, as starting, suspending and terminating a transaction. The
+ * test depends on random numbers, and, based on them, it sets different TM
+ * states.
+ *
+ * Other than that, the test fills out the user context struct that is passed
+ * to the sigreturn system call with random data, in order to make sure that
+ * the signal handler syscall can handle different and invalid states
+ * properly.
+ *
+ * This selftest has command line parameters to control what kind of tests the
+ * user wants to run, as for example, if a transaction should be started prior
+ * to signal being raised, or, after the signal being raised and before the
+ * sigreturn. If no parameter is given, the default is enabling all options.
+ *
+ * This test does not check if the user context is being read and set
+ * properly by the kernel. Its purpose, at this time, is basically
+ * guaranteeing that the kernel does not crash on invalid scenarios.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ucontext.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include "utils.h"
+
+/* Selftest defaults */
+#define COUNT_MAX 4000 /* Number of interactions */
+#define THREADS 16 /* Number of threads */
+
+/* Arguments options */
+#define ARG_MESS_WITH_TM_AT 0x1
+#define ARG_MESS_WITH_TM_BEFORE 0x2
+#define ARG_MESS_WITH_MSR_AT 0x4
+#define ARG_FOREVER 0x10
+#define ARG_COMPLETE (ARG_MESS_WITH_TM_AT | \
+ ARG_MESS_WITH_TM_BEFORE | \
+ ARG_MESS_WITH_MSR_AT)
+
+static int args;
+static int nthread = THREADS;
+static int count_max = COUNT_MAX;
+
+/* checkpoint context */
+static ucontext_t *tmp_uc;
+
+/* Return true with 1/x probability */
+static int one_in_chance(int x)
+{
+ return rand() % x == 0;
+}
+
+/* Change TM states */
+static void mess_with_tm(void)
+{
+ /* Starts a transaction 33% of the time */
+ if (one_in_chance(3)) {
+ asm ("tbegin. ;"
+ "beq 8 ;");
+
+ /* And suspended half of them */
+ if (one_in_chance(2))
+ asm("tsuspend. ;");
+ }
+
+ /* Call 'tend' in 5% of the runs */
+ if (one_in_chance(20))
+ asm("tend. ;");
+}
+
+/* Signal handler that will be invoked with raise() */
+static void trap_signal_handler(int signo, siginfo_t *si, void *uc)
+{
+ ucontext_t *ucp = uc;
+
+ ucp->uc_link = tmp_uc;
+
+ /*
+ * Set uc_link in three possible ways:
+ * - Setting a single 'int' in the whole chunk
+ * - Cloning ucp into uc_link
+ * - Allocating a new memory chunk
+ */
+ if (one_in_chance(3)) {
+ memset(ucp->uc_link, rand(), sizeof(ucontext_t));
+ } else if (one_in_chance(2)) {
+ memcpy(ucp->uc_link, uc, sizeof(ucontext_t));
+ } else if (one_in_chance(2)) {
+ if (tmp_uc) {
+ free(tmp_uc);
+ tmp_uc = NULL;
+ }
+ tmp_uc = malloc(sizeof(ucontext_t));
+ ucp->uc_link = tmp_uc;
+ /* Trying to cause a major page fault at Kernel level */
+ madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED);
+ }
+
+ if (args & ARG_MESS_WITH_MSR_AT) {
+ /* Changing the checkpointed registers */
+ if (one_in_chance(4)) {
+ ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
+ } else {
+ if (one_in_chance(2)) {
+ ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
+ MSR_TS_T;
+ } else if (one_in_chance(2)) {
+ ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
+ MSR_TS_T | MSR_TS_S;
+ }
+ }
+
+ /* Checking the current register context */
+ if (one_in_chance(2)) {
+ ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
+ } else if (one_in_chance(2)) {
+ if (one_in_chance(2))
+ ucp->uc_mcontext.gp_regs[PT_MSR] |=
+ MSR_TS_T;
+ else if (one_in_chance(2))
+ ucp->uc_mcontext.gp_regs[PT_MSR] |=
+ MSR_TS_T | MSR_TS_S;
+ }
+ }
+
+ if (one_in_chance(20)) {
+ /* Nested transaction start */
+ if (one_in_chance(5))
+ mess_with_tm();
+
+ /* Return without changing any other context info */
+ return;
+ }
+
+ if (one_in_chance(10))
+ ucp->uc_mcontext.gp_regs[PT_MSR] = random();
+ if (one_in_chance(10))
+ ucp->uc_mcontext.gp_regs[PT_NIP] = random();
+ if (one_in_chance(10))
+ ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] = random();
+ if (one_in_chance(10))
+ ucp->uc_link->uc_mcontext.gp_regs[PT_NIP] = random();
+
+ ucp->uc_mcontext.gp_regs[PT_TRAP] = random();
+ ucp->uc_mcontext.gp_regs[PT_DSISR] = random();
+ ucp->uc_mcontext.gp_regs[PT_DAR] = random();
+ ucp->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
+ ucp->uc_mcontext.gp_regs[PT_XER] = random();
+ ucp->uc_mcontext.gp_regs[PT_RESULT] = random();
+ ucp->uc_mcontext.gp_regs[PT_SOFTE] = random();
+ ucp->uc_mcontext.gp_regs[PT_DSCR] = random();
+ ucp->uc_mcontext.gp_regs[PT_CTR] = random();
+ ucp->uc_mcontext.gp_regs[PT_LNK] = random();
+ ucp->uc_mcontext.gp_regs[PT_CCR] = random();
+ ucp->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
+
+ ucp->uc_link->uc_mcontext.gp_regs[PT_TRAP] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_DSISR] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_DAR] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_XER] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_RESULT] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_SOFTE] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_DSCR] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_CTR] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_LNK] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_CCR] = random();
+ ucp->uc_link->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
+
+ if (args & ARG_MESS_WITH_TM_BEFORE) {
+ if (one_in_chance(2))
+ mess_with_tm();
+ }
+}
+
+static void seg_signal_handler(int signo, siginfo_t *si, void *uc)
+{
+ /* Clear exit for process that segfaults */
+ exit(0);
+}
+
+static void *sigfuz_test(void *thrid)
+{
+ struct sigaction trap_sa, seg_sa;
+ int ret, i = 0;
+ pid_t t;
+
+ tmp_uc = malloc(sizeof(ucontext_t));
+
+ /* Main signal handler */
+ trap_sa.sa_flags = SA_SIGINFO;
+ trap_sa.sa_sigaction = trap_signal_handler;
+
+ /* SIGSEGV signal handler */
+ seg_sa.sa_flags = SA_SIGINFO;
+ seg_sa.sa_sigaction = seg_signal_handler;
+
+ /* The signal handler will enable MSR_TS */
+ sigaction(SIGUSR1, &trap_sa, NULL);
+
+ /* If it does not crash, it will segfault, avoid it to retest */
+ sigaction(SIGSEGV, &seg_sa, NULL);
+
+ while (i < count_max) {
+ t = fork();
+
+ if (t == 0) {
+ /* Once seed per process */
+ srand(time(NULL) + getpid());
+ if (args & ARG_MESS_WITH_TM_AT) {
+ if (one_in_chance(2))
+ mess_with_tm();
+ }
+ raise(SIGUSR1);
+ exit(0);
+ } else {
+ waitpid(t, &ret, 0);
+ }
+ if (!(args & ARG_FOREVER))
+ i++;
+ }
+
+ /* If not freed already, free now */
+ if (tmp_uc) {
+ free(tmp_uc);
+ tmp_uc = NULL;
+ }
+
+ return NULL;
+}
+
+static int signal_fuzzer(void)
+{
+ int t, rc;
+ pthread_t *threads;
+
+ threads = malloc(nthread * sizeof(pthread_t));
+
+ for (t = 0; t < nthread; t++) {
+ rc = pthread_create(&threads[t], NULL, sigfuz_test,
+ (void *)&t);
+ if (rc)
+ perror("Thread creation error\n");
+ }
+
+ for (t = 0; t < nthread; t++) {
+ rc = pthread_join(threads[t], NULL);
+ if (rc)
+ perror("Thread join error\n");
+ }
+
+ free(threads);
+
+ return EXIT_SUCCESS;
+}
+
+static void show_help(char *name)
+{
+ printf("%s: Sigfuzzer for powerpc\n", name);
+ printf("Usage:\n");
+ printf("\t-b\t Mess with TM before raising a SIGUSR1 signal\n");
+ printf("\t-a\t Mess with TM after raising a SIGUSR1 signal\n");
+ printf("\t-m\t Mess with MSR[TS] bits at mcontext\n");
+ printf("\t-x\t Mess with everything above\n");
+ printf("\t-f\t Run forever (Press ^C to Quit)\n");
+ printf("\t-i\t Amount of interactions. (Default = %d)\n", COUNT_MAX);
+ printf("\t-t\t Amount of threads. (Default = %d)\n", THREADS);
+ exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "bamxt:fi:h")) != -1) {
+ if (opt == 'b') {
+ printf("Mess with TM before signal\n");
+ args |= ARG_MESS_WITH_TM_BEFORE;
+ } else if (opt == 'a') {
+ printf("Mess with TM at signal handler\n");
+ args |= ARG_MESS_WITH_TM_AT;
+ } else if (opt == 'm') {
+ printf("Mess with MSR[TS] bits in mcontext\n");
+ args |= ARG_MESS_WITH_MSR_AT;
+ } else if (opt == 'x') {
+ printf("Running with all options enabled\n");
+ args |= ARG_COMPLETE;
+ } else if (opt == 't') {
+ nthread = atoi(optarg);
+ printf("Threads = %d\n", nthread);
+ } else if (opt == 'f') {
+ args |= ARG_FOREVER;
+ printf("Press ^C to stop\n");
+ test_harness_set_timeout(-1);
+ } else if (opt == 'i') {
+ count_max = atoi(optarg);
+ printf("Running for %d interactions\n", count_max);
+ } else if (opt == 'h') {
+ show_help(argv[0]);
+ }
+ }
+
+ /* Default test suite */
+ if (!args)
+ args = ARG_COMPLETE;
+
+ test_harness(signal_fuzzer, "signal_fuzzer");
+}
diff --git a/marvell/linux/tools/testing/selftests/powerpc/signal/signal.S b/marvell/linux/tools/testing/selftests/powerpc/signal/signal.S
new file mode 100644
index 0000000..228fba4
--- /dev/null
+++ b/marvell/linux/tools/testing/selftests/powerpc/signal/signal.S
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2015, Cyril Bur, IBM Corp.
+ */
+
+#include "basic_asm.h"
+
+/* long signal_self(pid_t pid, int sig); */
+FUNC_START(signal_self)
+ li r0,37 /* sys_kill */
+ /* r3 already has our pid in it */
+ /* r4 already has signal type in it */
+ sc
+ bc 4,3,1f
+ subfze r3,r3
+1: blr
+FUNC_END(signal_self)
+
+/* long tm_signal_self(pid_t pid, int sig, int *ret); */
+FUNC_START(tm_signal_self)
+ PUSH_BASIC_STACK(8)
+ std r5,STACK_FRAME_PARAM(0)(sp) /* ret */
+ tbegin.
+ beq 1f
+ tsuspend.
+ li r0,37 /* sys_kill */
+ /* r3 already has our pid in it */
+ /* r4 already has signal type in it */
+ sc
+ ld r5,STACK_FRAME_PARAM(0)(sp) /* ret */
+ bc 4,3,2f
+ subfze r3,r3
+2: std r3,0(r5)
+ tabort. 0
+ tresume. /* Be nice to some cleanup, jumps back to tbegin then to 1: */
+ /*
+ * Transaction should be proper doomed and we should never get
+ * here
+ */
+ li r3,1
+ POP_BASIC_STACK(8)
+ blr
+1: li r3,0
+ POP_BASIC_STACK(8)
+ blr
+FUNC_END(tm_signal_self)
diff --git a/marvell/linux/tools/testing/selftests/powerpc/signal/signal.c b/marvell/linux/tools/testing/selftests/powerpc/signal/signal.c
new file mode 100644
index 0000000..766e484
--- /dev/null
+++ b/marvell/linux/tools/testing/selftests/powerpc/signal/signal.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2016, Cyril Bur, IBM Corp.
+ *
+ * Sending one self a signal should always get delivered.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <altivec.h>
+
+#include "utils.h"
+
+#define MAX_ATTEMPT 500000
+#define TIMEOUT 5
+
+extern long signal_self(pid_t pid, int sig);
+
+static sig_atomic_t signaled;
+static sig_atomic_t fail;
+
+static void signal_handler(int sig)
+{
+ if (sig == SIGUSR1)
+ signaled = 1;
+ else
+ fail = 1;
+}
+
+static int test_signal()
+{
+ int i;
+ struct sigaction act;
+ pid_t ppid = getpid();
+ pid_t pid;
+
+ act.sa_handler = signal_handler;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ if (sigaction(SIGUSR1, &act, NULL) < 0) {
+ perror("sigaction SIGUSR1");
+ exit(1);
+ }
+ if (sigaction(SIGALRM, &act, NULL) < 0) {
+ perror("sigaction SIGALRM");
+ exit(1);
+ }
+
+ /* Don't do this for MAX_ATTEMPT, its simply too long */
+ for(i = 0; i < 1000; i++) {
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0) {
+ signal_self(ppid, SIGUSR1);
+ exit(1);
+ } else {
+ alarm(0); /* Disable any pending */
+ alarm(2);
+ while (!signaled && !fail)
+ asm volatile("": : :"memory");
+ if (!signaled) {
+ fprintf(stderr, "Didn't get signal from child\n");
+ FAIL_IF(1); /* For the line number */
+ }
+ /* Otherwise we'll loop too fast and fork() will eventually fail */
+ waitpid(pid, NULL, 0);
+ }
+ }
+
+ for (i = 0; i < MAX_ATTEMPT; i++) {
+ long rc;
+
+ alarm(0); /* Disable any pending */
+ signaled = 0;
+ alarm(TIMEOUT);
+ rc = signal_self(ppid, SIGUSR1);
+ if (rc) {
+ fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx",
+ i, fail, rc);
+ FAIL_IF(1); /* For the line number */
+ }
+ while (!signaled && !fail)
+ asm volatile("": : :"memory");
+ if (!signaled) {
+ fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx",
+ i, fail, rc);
+ FAIL_IF(1); /* For the line number */
+ }
+ }
+
+ return 0;
+}
+
+int main(void)
+{
+ test_harness_set_timeout(300);
+ return test_harness(test_signal, "signal");
+}
diff --git a/marvell/linux/tools/testing/selftests/powerpc/signal/signal_tm.c b/marvell/linux/tools/testing/selftests/powerpc/signal/signal_tm.c
new file mode 100644
index 0000000..5bf2224
--- /dev/null
+++ b/marvell/linux/tools/testing/selftests/powerpc/signal/signal_tm.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2016, Cyril Bur, IBM Corp.
+ *
+ * Sending one self a signal should always get delivered.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <altivec.h>
+
+#include "utils.h"
+#include "../tm/tm.h"
+
+#define MAX_ATTEMPT 500000
+#define TIMEOUT 10
+
+extern long tm_signal_self(pid_t pid, int sig, long *ret);
+
+static sig_atomic_t signaled;
+static sig_atomic_t fail;
+
+static void signal_handler(int sig)
+{
+ if (tcheck_active()) {
+ fail = 2;
+ return;
+ }
+
+ if (sig == SIGUSR1)
+ signaled = 1;
+ else
+ fail = 1;
+}
+
+static int test_signal_tm()
+{
+ int i;
+ struct sigaction act;
+
+ act.sa_handler = signal_handler;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ if (sigaction(SIGUSR1, &act, NULL) < 0) {
+ perror("sigaction SIGUSR1");
+ exit(1);
+ }
+ if (sigaction(SIGALRM, &act, NULL) < 0) {
+ perror("sigaction SIGALRM");
+ exit(1);
+ }
+
+ SKIP_IF(!have_htm());
+
+ for (i = 0; i < MAX_ATTEMPT; i++) {
+ /*
+ * If anything bad happens in ASM and we fail to set ret
+ * because *handwave* TM this will cause failure
+ */
+ long ret = 0xdead;
+ long rc = 0xbeef;
+
+ alarm(0); /* Disable any pending */
+ signaled = 0;
+ alarm(TIMEOUT);
+ FAIL_IF(tcheck_transactional());
+ rc = tm_signal_self(getpid(), SIGUSR1, &ret);
+ if (ret == 0xdead)
+ /*
+ * This basically means the transaction aborted before we
+ * even got to the suspend... this is crazy but it
+ * happens.
+ * Yes this also means we might never make forward
+ * progress... the alarm() will trip eventually...
+ */
+ continue;
+
+ if (rc || ret) {
+ /* Ret is actually an errno */
+ printf("TEXASR 0x%016lx, TFIAR 0x%016lx\n",
+ __builtin_get_texasr(), __builtin_get_tfiar());
+ fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx ret=0x%lx\n",
+ i, fail, rc, ret);
+ FAIL_IF(ret);
+ }
+ while(!signaled && !fail)
+ asm volatile("": : :"memory");
+ if (!signaled) {
+ fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx ret=0x%lx\n",
+ i, fail, rc, ret);
+ FAIL_IF(fail); /* For the line number */
+ }
+ }
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(test_signal_tm, "signal_tm");
+}