diff options
| -rw-r--r-- | tools/testing/selftests/powerpc/harness.c | 6 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/include/reg.h | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/signal/.gitignore | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/signal/Makefile | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/signal/sigfuz.c | 325 |
5 files changed, 334 insertions, 3 deletions
diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c index 9d7166dfad1e..ba89353abfcc 100644 --- a/tools/testing/selftests/powerpc/harness.c +++ b/tools/testing/selftests/powerpc/harness.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #define KILL_TIMEOUT 5 | 22 | #define KILL_TIMEOUT 5 |
| 23 | 23 | ||
| 24 | /* Setting timeout to -1 disables the alarm */ | ||
| 24 | static uint64_t timeout = 120; | 25 | static uint64_t timeout = 120; |
| 25 | 26 | ||
| 26 | int run_test(int (test_function)(void), char *name) | 27 | int run_test(int (test_function)(void), char *name) |
| @@ -43,8 +44,9 @@ int run_test(int (test_function)(void), char *name) | |||
| 43 | 44 | ||
| 44 | setpgid(pid, pid); | 45 | setpgid(pid, pid); |
| 45 | 46 | ||
| 46 | /* Wake us up in timeout seconds */ | 47 | if (timeout != -1) |
| 47 | alarm(timeout); | 48 | /* Wake us up in timeout seconds */ |
| 49 | alarm(timeout); | ||
| 48 | terminated = false; | 50 | terminated = false; |
| 49 | 51 | ||
| 50 | wait: | 52 | wait: |
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h index 96043b9b9829..1e797ae396ee 100644 --- a/tools/testing/selftests/powerpc/include/reg.h +++ b/tools/testing/selftests/powerpc/include/reg.h | |||
| @@ -79,11 +79,13 @@ | |||
| 79 | 79 | ||
| 80 | /* MSR register bits */ | 80 | /* MSR register bits */ |
| 81 | #define MSR_TS_S_LG 33 /* Trans Mem state: Suspended */ | 81 | #define MSR_TS_S_LG 33 /* Trans Mem state: Suspended */ |
| 82 | #define MSR_TS_T_LG 34 /* Trans Mem state: Active */ | ||
| 82 | 83 | ||
| 83 | #define __MASK(X) (1UL<<(X)) | 84 | #define __MASK(X) (1UL<<(X)) |
| 84 | 85 | ||
| 85 | /* macro to check TM MSR bits */ | 86 | /* macro to check TM MSR bits */ |
| 86 | #define MSR_TS_S __MASK(MSR_TS_S_LG) /* Transaction Suspended */ | 87 | #define MSR_TS_S __MASK(MSR_TS_S_LG) /* Transaction Suspended */ |
| 88 | #define MSR_TS_T __MASK(MSR_TS_T_LG) /* Transaction Transactional */ | ||
| 87 | 89 | ||
| 88 | /* Vector Instructions */ | 90 | /* Vector Instructions */ |
| 89 | #define VSX_XX1(xs, ra, rb) (((xs) & 0x1f) << 21 | ((ra) << 16) | \ | 91 | #define VSX_XX1(xs, ra, rb) (((xs) & 0x1f) << 21 | ((ra) << 16) | \ |
diff --git a/tools/testing/selftests/powerpc/signal/.gitignore b/tools/testing/selftests/powerpc/signal/.gitignore index 1b89224a8aab..dca5852a1546 100644 --- a/tools/testing/selftests/powerpc/signal/.gitignore +++ b/tools/testing/selftests/powerpc/signal/.gitignore | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | signal | 1 | signal |
| 2 | signal_tm | 2 | signal_tm |
| 3 | sigfuz | ||
diff --git a/tools/testing/selftests/powerpc/signal/Makefile b/tools/testing/selftests/powerpc/signal/Makefile index 209a958dca12..113838fbbe7f 100644 --- a/tools/testing/selftests/powerpc/signal/Makefile +++ b/tools/testing/selftests/powerpc/signal/Makefile | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | TEST_GEN_PROGS := signal signal_tm | 2 | TEST_GEN_PROGS := signal signal_tm sigfuz |
| 3 | 3 | ||
| 4 | CFLAGS += -maltivec | 4 | CFLAGS += -maltivec |
| 5 | $(OUTPUT)/signal_tm: CFLAGS += -mhtm | 5 | $(OUTPUT)/signal_tm: CFLAGS += -mhtm |
| 6 | $(OUTPUT)/sigfuz: CFLAGS += -pthread -m64 | ||
| 6 | 7 | ||
| 7 | top_srcdir = ../../../../.. | 8 | top_srcdir = ../../../../.. |
| 8 | include ../../lib.mk | 9 | include ../../lib.mk |
diff --git a/tools/testing/selftests/powerpc/signal/sigfuz.c b/tools/testing/selftests/powerpc/signal/sigfuz.c new file mode 100644 index 000000000000..dade00c698c2 --- /dev/null +++ b/tools/testing/selftests/powerpc/signal/sigfuz.c | |||
| @@ -0,0 +1,325 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2018, Breno Leitao, IBM Corp. | ||
| 4 | * Licensed under GPLv2. | ||
| 5 | * | ||
| 6 | * Sigfuz(tm): A PowerPC TM-aware signal fuzzer. | ||
| 7 | * | ||
| 8 | * This is a new selftest that raises SIGUSR1 signals and handles it in a set | ||
| 9 | * of different ways, trying to create different scenario for testing | ||
| 10 | * purpose. | ||
| 11 | * | ||
| 12 | * This test works raising a signal and calling sigreturn interleaved with | ||
| 13 | * TM operations, as starting, suspending and terminating a transaction. The | ||
| 14 | * test depends on random numbers, and, based on them, it sets different TM | ||
| 15 | * states. | ||
| 16 | * | ||
| 17 | * Other than that, the test fills out the user context struct that is passed | ||
| 18 | * to the sigreturn system call with random data, in order to make sure that | ||
| 19 | * the signal handler syscall can handle different and invalid states | ||
| 20 | * properly. | ||
| 21 | * | ||
| 22 | * This selftest has command line parameters to control what kind of tests the | ||
| 23 | * user wants to run, as for example, if a transaction should be started prior | ||
| 24 | * to signal being raised, or, after the signal being raised and before the | ||
| 25 | * sigreturn. If no parameter is given, the default is enabling all options. | ||
| 26 | * | ||
| 27 | * This test does not check if the user context is being read and set | ||
| 28 | * properly by the kernel. Its purpose, at this time, is basically | ||
| 29 | * guaranteeing that the kernel does not crash on invalid scenarios. | ||
| 30 | */ | ||
| 31 | |||
| 32 | #include <stdio.h> | ||
| 33 | #include <limits.h> | ||
| 34 | #include <sys/wait.h> | ||
| 35 | #include <unistd.h> | ||
| 36 | #include <stdlib.h> | ||
| 37 | #include <signal.h> | ||
| 38 | #include <string.h> | ||
| 39 | #include <ucontext.h> | ||
| 40 | #include <sys/mman.h> | ||
| 41 | #include <pthread.h> | ||
| 42 | #include "utils.h" | ||
| 43 | |||
| 44 | /* Selftest defaults */ | ||
| 45 | #define COUNT_MAX 4000 /* Number of interactions */ | ||
| 46 | #define THREADS 16 /* Number of threads */ | ||
| 47 | |||
| 48 | /* Arguments options */ | ||
| 49 | #define ARG_MESS_WITH_TM_AT 0x1 | ||
| 50 | #define ARG_MESS_WITH_TM_BEFORE 0x2 | ||
| 51 | #define ARG_MESS_WITH_MSR_AT 0x4 | ||
| 52 | #define ARG_FOREVER 0x10 | ||
| 53 | #define ARG_COMPLETE (ARG_MESS_WITH_TM_AT | \ | ||
| 54 | ARG_MESS_WITH_TM_BEFORE | \ | ||
| 55 | ARG_MESS_WITH_MSR_AT) | ||
| 56 | |||
| 57 | static int args; | ||
| 58 | static int nthread = THREADS; | ||
| 59 | static int count_max = COUNT_MAX; | ||
| 60 | |||
| 61 | /* checkpoint context */ | ||
| 62 | static ucontext_t *tmp_uc; | ||
| 63 | |||
| 64 | /* Return true with 1/x probability */ | ||
| 65 | static int one_in_chance(int x) | ||
| 66 | { | ||
| 67 | return rand() % x == 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Change TM states */ | ||
| 71 | static void mess_with_tm(void) | ||
| 72 | { | ||
| 73 | /* Starts a transaction 33% of the time */ | ||
| 74 | if (one_in_chance(3)) { | ||
| 75 | asm ("tbegin. ;" | ||
| 76 | "beq 8 ;"); | ||
| 77 | |||
| 78 | /* And suspended half of them */ | ||
| 79 | if (one_in_chance(2)) | ||
| 80 | asm("tsuspend. ;"); | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Call 'tend' in 5% of the runs */ | ||
| 84 | if (one_in_chance(20)) | ||
| 85 | asm("tend. ;"); | ||
| 86 | } | ||
| 87 | |||
| 88 | /* Signal handler that will be invoked with raise() */ | ||
| 89 | static void trap_signal_handler(int signo, siginfo_t *si, void *uc) | ||
| 90 | { | ||
| 91 | ucontext_t *ucp = uc; | ||
| 92 | |||
| 93 | ucp->uc_link = tmp_uc; | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Set uc_link in three possible ways: | ||
| 97 | * - Setting a single 'int' in the whole chunk | ||
| 98 | * - Cloning ucp into uc_link | ||
| 99 | * - Allocating a new memory chunk | ||
| 100 | */ | ||
| 101 | if (one_in_chance(3)) { | ||
| 102 | memset(ucp->uc_link, rand(), sizeof(ucontext_t)); | ||
| 103 | } else if (one_in_chance(2)) { | ||
| 104 | memcpy(ucp->uc_link, uc, sizeof(ucontext_t)); | ||
| 105 | } else if (one_in_chance(2)) { | ||
| 106 | if (tmp_uc) { | ||
| 107 | free(tmp_uc); | ||
| 108 | tmp_uc = NULL; | ||
| 109 | } | ||
| 110 | tmp_uc = malloc(sizeof(ucontext_t)); | ||
| 111 | ucp->uc_link = tmp_uc; | ||
| 112 | /* Trying to cause a major page fault at Kernel level */ | ||
| 113 | madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED); | ||
| 114 | } | ||
| 115 | |||
| 116 | if (args & ARG_MESS_WITH_MSR_AT) { | ||
| 117 | /* Changing the checkpointed registers */ | ||
| 118 | if (one_in_chance(4)) { | ||
| 119 | ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S; | ||
| 120 | } else { | ||
| 121 | if (one_in_chance(2)) { | ||
| 122 | ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= | ||
| 123 | MSR_TS_T; | ||
| 124 | } else if (one_in_chance(2)) { | ||
| 125 | ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= | ||
| 126 | MSR_TS_T | MSR_TS_S; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /* Checking the current register context */ | ||
| 131 | if (one_in_chance(2)) { | ||
| 132 | ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S; | ||
| 133 | } else if (one_in_chance(2)) { | ||
| 134 | if (one_in_chance(2)) | ||
| 135 | ucp->uc_mcontext.gp_regs[PT_MSR] |= | ||
| 136 | MSR_TS_T; | ||
| 137 | else if (one_in_chance(2)) | ||
| 138 | ucp->uc_mcontext.gp_regs[PT_MSR] |= | ||
| 139 | MSR_TS_T | MSR_TS_S; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | if (one_in_chance(20)) { | ||
| 144 | /* Nested transaction start */ | ||
| 145 | if (one_in_chance(5)) | ||
| 146 | mess_with_tm(); | ||
| 147 | |||
| 148 | /* Return without changing any other context info */ | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | |||
| 152 | if (one_in_chance(10)) | ||
| 153 | ucp->uc_mcontext.gp_regs[PT_MSR] = random(); | ||
| 154 | if (one_in_chance(10)) | ||
| 155 | ucp->uc_mcontext.gp_regs[PT_NIP] = random(); | ||
| 156 | if (one_in_chance(10)) | ||
| 157 | ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] = random(); | ||
| 158 | if (one_in_chance(10)) | ||
| 159 | ucp->uc_link->uc_mcontext.gp_regs[PT_NIP] = random(); | ||
| 160 | |||
| 161 | ucp->uc_mcontext.gp_regs[PT_TRAP] = random(); | ||
| 162 | ucp->uc_mcontext.gp_regs[PT_DSISR] = random(); | ||
| 163 | ucp->uc_mcontext.gp_regs[PT_DAR] = random(); | ||
| 164 | ucp->uc_mcontext.gp_regs[PT_ORIG_R3] = random(); | ||
| 165 | ucp->uc_mcontext.gp_regs[PT_XER] = random(); | ||
| 166 | ucp->uc_mcontext.gp_regs[PT_RESULT] = random(); | ||
| 167 | ucp->uc_mcontext.gp_regs[PT_SOFTE] = random(); | ||
| 168 | ucp->uc_mcontext.gp_regs[PT_DSCR] = random(); | ||
| 169 | ucp->uc_mcontext.gp_regs[PT_CTR] = random(); | ||
| 170 | ucp->uc_mcontext.gp_regs[PT_LNK] = random(); | ||
| 171 | ucp->uc_mcontext.gp_regs[PT_CCR] = random(); | ||
| 172 | ucp->uc_mcontext.gp_regs[PT_REGS_COUNT] = random(); | ||
| 173 | |||
| 174 | ucp->uc_link->uc_mcontext.gp_regs[PT_TRAP] = random(); | ||
| 175 | ucp->uc_link->uc_mcontext.gp_regs[PT_DSISR] = random(); | ||
| 176 | ucp->uc_link->uc_mcontext.gp_regs[PT_DAR] = random(); | ||
| 177 | ucp->uc_link->uc_mcontext.gp_regs[PT_ORIG_R3] = random(); | ||
| 178 | ucp->uc_link->uc_mcontext.gp_regs[PT_XER] = random(); | ||
| 179 | ucp->uc_link->uc_mcontext.gp_regs[PT_RESULT] = random(); | ||
| 180 | ucp->uc_link->uc_mcontext.gp_regs[PT_SOFTE] = random(); | ||
| 181 | ucp->uc_link->uc_mcontext.gp_regs[PT_DSCR] = random(); | ||
| 182 | ucp->uc_link->uc_mcontext.gp_regs[PT_CTR] = random(); | ||
| 183 | ucp->uc_link->uc_mcontext.gp_regs[PT_LNK] = random(); | ||
| 184 | ucp->uc_link->uc_mcontext.gp_regs[PT_CCR] = random(); | ||
| 185 | ucp->uc_link->uc_mcontext.gp_regs[PT_REGS_COUNT] = random(); | ||
| 186 | |||
| 187 | if (args & ARG_MESS_WITH_TM_BEFORE) { | ||
| 188 | if (one_in_chance(2)) | ||
| 189 | mess_with_tm(); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | static void seg_signal_handler(int signo, siginfo_t *si, void *uc) | ||
| 194 | { | ||
| 195 | /* Clear exit for process that segfaults */ | ||
| 196 | exit(0); | ||
| 197 | } | ||
| 198 | |||
| 199 | static void *sigfuz_test(void *thrid) | ||
| 200 | { | ||
| 201 | struct sigaction trap_sa, seg_sa; | ||
| 202 | int ret, i = 0; | ||
| 203 | pid_t t; | ||
| 204 | |||
| 205 | tmp_uc = malloc(sizeof(ucontext_t)); | ||
| 206 | |||
| 207 | /* Main signal handler */ | ||
| 208 | trap_sa.sa_flags = SA_SIGINFO; | ||
| 209 | trap_sa.sa_sigaction = trap_signal_handler; | ||
| 210 | |||
| 211 | /* SIGSEGV signal handler */ | ||
| 212 | seg_sa.sa_flags = SA_SIGINFO; | ||
| 213 | seg_sa.sa_sigaction = seg_signal_handler; | ||
| 214 | |||
| 215 | /* The signal handler will enable MSR_TS */ | ||
| 216 | sigaction(SIGUSR1, &trap_sa, NULL); | ||
| 217 | |||
| 218 | /* If it does not crash, it will segfault, avoid it to retest */ | ||
| 219 | sigaction(SIGSEGV, &seg_sa, NULL); | ||
| 220 | |||
| 221 | while (i < count_max) { | ||
| 222 | t = fork(); | ||
| 223 | |||
| 224 | if (t == 0) { | ||
| 225 | /* Once seed per process */ | ||
| 226 | srand(time(NULL) + getpid()); | ||
| 227 | if (args & ARG_MESS_WITH_TM_AT) { | ||
| 228 | if (one_in_chance(2)) | ||
| 229 | mess_with_tm(); | ||
| 230 | } | ||
| 231 | raise(SIGUSR1); | ||
| 232 | exit(0); | ||
| 233 | } else { | ||
| 234 | waitpid(t, &ret, 0); | ||
| 235 | } | ||
| 236 | if (!(args & ARG_FOREVER)) | ||
| 237 | i++; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* If not freed already, free now */ | ||
| 241 | if (tmp_uc) { | ||
| 242 | free(tmp_uc); | ||
| 243 | tmp_uc = NULL; | ||
| 244 | } | ||
| 245 | |||
| 246 | return NULL; | ||
| 247 | } | ||
| 248 | |||
| 249 | static int signal_fuzzer(void) | ||
| 250 | { | ||
| 251 | int t, rc; | ||
| 252 | pthread_t *threads; | ||
| 253 | |||
| 254 | threads = malloc(nthread * sizeof(pthread_t)); | ||
| 255 | |||
| 256 | for (t = 0; t < nthread; t++) { | ||
| 257 | rc = pthread_create(&threads[t], NULL, sigfuz_test, | ||
| 258 | (void *)&t); | ||
| 259 | if (rc) | ||
| 260 | perror("Thread creation error\n"); | ||
| 261 | } | ||
| 262 | |||
| 263 | for (t = 0; t < nthread; t++) { | ||
| 264 | rc = pthread_join(threads[t], NULL); | ||
| 265 | if (rc) | ||
| 266 | perror("Thread join error\n"); | ||
| 267 | } | ||
| 268 | |||
| 269 | free(threads); | ||
| 270 | |||
| 271 | return EXIT_SUCCESS; | ||
| 272 | } | ||
| 273 | |||
| 274 | static void show_help(char *name) | ||
| 275 | { | ||
| 276 | printf("%s: Sigfuzzer for powerpc\n", name); | ||
| 277 | printf("Usage:\n"); | ||
| 278 | printf("\t-b\t Mess with TM before raising a SIGUSR1 signal\n"); | ||
| 279 | printf("\t-a\t Mess with TM after raising a SIGUSR1 signal\n"); | ||
| 280 | printf("\t-m\t Mess with MSR[TS] bits at mcontext\n"); | ||
| 281 | printf("\t-x\t Mess with everything above\n"); | ||
| 282 | printf("\t-f\t Run forever (Press ^C to Quit)\n"); | ||
| 283 | printf("\t-i\t Amount of interactions. (Default = %d)\n", COUNT_MAX); | ||
| 284 | printf("\t-t\t Amount of threads. (Default = %d)\n", THREADS); | ||
| 285 | exit(-1); | ||
| 286 | } | ||
| 287 | |||
| 288 | int main(int argc, char **argv) | ||
| 289 | { | ||
| 290 | int opt; | ||
| 291 | |||
| 292 | while ((opt = getopt(argc, argv, "bamxt:fi:h")) != -1) { | ||
| 293 | if (opt == 'b') { | ||
| 294 | printf("Mess with TM before signal\n"); | ||
| 295 | args |= ARG_MESS_WITH_TM_BEFORE; | ||
| 296 | } else if (opt == 'a') { | ||
| 297 | printf("Mess with TM at signal handler\n"); | ||
| 298 | args |= ARG_MESS_WITH_TM_AT; | ||
| 299 | } else if (opt == 'm') { | ||
| 300 | printf("Mess with MSR[TS] bits in mcontext\n"); | ||
| 301 | args |= ARG_MESS_WITH_MSR_AT; | ||
| 302 | } else if (opt == 'x') { | ||
| 303 | printf("Running with all options enabled\n"); | ||
| 304 | args |= ARG_COMPLETE; | ||
| 305 | } else if (opt == 't') { | ||
| 306 | nthread = atoi(optarg); | ||
| 307 | printf("Threads = %d\n", nthread); | ||
| 308 | } else if (opt == 'f') { | ||
| 309 | args |= ARG_FOREVER; | ||
| 310 | printf("Press ^C to stop\n"); | ||
| 311 | test_harness_set_timeout(-1); | ||
| 312 | } else if (opt == 'i') { | ||
| 313 | count_max = atoi(optarg); | ||
| 314 | printf("Running for %d interactions\n", count_max); | ||
| 315 | } else if (opt == 'h') { | ||
| 316 | show_help(argv[0]); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Default test suite */ | ||
| 321 | if (!args) | ||
| 322 | args = ARG_COMPLETE; | ||
| 323 | |||
| 324 | test_harness(signal_fuzzer, "signal_fuzzer"); | ||
| 325 | } | ||
