diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/testing/selftests/powerpc/Makefile | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/include/reg.h | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/include/utils.h | 18 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/.gitignore | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/Makefile | 4 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/wild_bctr.c | 155 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c | 8 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/ptrace/Makefile | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c | 228 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/security/Makefile | 9 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/security/rfi_flush.c | 132 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/tm/tm-tmspr.c | 27 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/tm/tm-unavailable.c | 9 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/tm/tm.h | 9 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/utils.c | 152 |
15 files changed, 735 insertions, 25 deletions
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 201b598558b9..b3ad909aefbc 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile | |||
| @@ -28,7 +28,8 @@ SUB_DIRS = alignment \ | |||
| 28 | tm \ | 28 | tm \ |
| 29 | vphn \ | 29 | vphn \ |
| 30 | math \ | 30 | math \ |
| 31 | ptrace | 31 | ptrace \ |
| 32 | security | ||
| 32 | 33 | ||
| 33 | endif | 34 | endif |
| 34 | 35 | ||
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h index 7f348c059bc2..52b4710469d2 100644 --- a/tools/testing/selftests/powerpc/include/reg.h +++ b/tools/testing/selftests/powerpc/include/reg.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | : "memory") | 17 | : "memory") |
| 18 | 18 | ||
| 19 | #define mb() asm volatile("sync" : : : "memory"); | 19 | #define mb() asm volatile("sync" : : : "memory"); |
| 20 | #define barrier() asm volatile("" : : : "memory"); | ||
| 20 | 21 | ||
| 21 | #define SPRN_MMCR2 769 | 22 | #define SPRN_MMCR2 769 |
| 22 | #define SPRN_MMCRA 770 | 23 | #define SPRN_MMCRA 770 |
diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index c58c370828b4..49621822d7c3 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <stdint.h> | 11 | #include <stdint.h> |
| 12 | #include <stdbool.h> | 12 | #include <stdbool.h> |
| 13 | #include <linux/auxvec.h> | 13 | #include <linux/auxvec.h> |
| 14 | #include <linux/perf_event.h> | ||
| 14 | #include "reg.h" | 15 | #include "reg.h" |
| 15 | 16 | ||
| 16 | /* Avoid headaches with PRI?64 - just use %ll? always */ | 17 | /* Avoid headaches with PRI?64 - just use %ll? always */ |
| @@ -31,6 +32,15 @@ void *get_auxv_entry(int type); | |||
| 31 | 32 | ||
| 32 | int pick_online_cpu(void); | 33 | int pick_online_cpu(void); |
| 33 | 34 | ||
| 35 | int read_debugfs_file(char *debugfs_file, int *result); | ||
| 36 | int write_debugfs_file(char *debugfs_file, int result); | ||
| 37 | void set_dscr(unsigned long val); | ||
| 38 | int perf_event_open_counter(unsigned int type, | ||
| 39 | unsigned long config, int group_fd); | ||
| 40 | int perf_event_enable(int fd); | ||
| 41 | int perf_event_disable(int fd); | ||
| 42 | int perf_event_reset(int fd); | ||
| 43 | |||
| 34 | static inline bool have_hwcap(unsigned long ftr) | 44 | static inline bool have_hwcap(unsigned long ftr) |
| 35 | { | 45 | { |
| 36 | return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; | 46 | return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; |
| @@ -80,4 +90,12 @@ do { \ | |||
| 80 | #define PPC_FEATURE2_ARCH_3_00 0x00800000 | 90 | #define PPC_FEATURE2_ARCH_3_00 0x00800000 |
| 81 | #endif | 91 | #endif |
| 82 | 92 | ||
| 93 | #if defined(__powerpc64__) | ||
| 94 | #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP] | ||
| 95 | #elif defined(__powerpc__) | ||
| 96 | #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.uc_regs->gregs[PT_NIP] | ||
| 97 | #else | ||
| 98 | #error implement UCONTEXT_NIA | ||
| 99 | #endif | ||
| 100 | |||
| 83 | #endif /* _SELFTESTS_POWERPC_UTILS_H */ | 101 | #endif /* _SELFTESTS_POWERPC_UTILS_H */ |
diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 7d7c42ed6de9..ba919308fe30 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore | |||
| @@ -2,4 +2,5 @@ hugetlb_vs_thp_test | |||
| 2 | subpage_prot | 2 | subpage_prot |
| 3 | tempfile | 3 | tempfile |
| 4 | prot_sao | 4 | prot_sao |
| 5 | segv_errors \ No newline at end of file | 5 | segv_errors |
| 6 | wild_bctr \ No newline at end of file | ||
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 33ced6e0ad25..43d68420e363 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | noarg: | 2 | noarg: |
| 3 | $(MAKE) -C ../ | 3 | $(MAKE) -C ../ |
| 4 | 4 | ||
| 5 | TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors | 5 | TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr |
| 6 | TEST_GEN_FILES := tempfile | 6 | TEST_GEN_FILES := tempfile |
| 7 | 7 | ||
| 8 | top_srcdir = ../../../../.. | 8 | top_srcdir = ../../../../.. |
| @@ -12,6 +12,8 @@ $(TEST_GEN_PROGS): ../harness.c | |||
| 12 | 12 | ||
| 13 | $(OUTPUT)/prot_sao: ../utils.c | 13 | $(OUTPUT)/prot_sao: ../utils.c |
| 14 | 14 | ||
| 15 | $(OUTPUT)/wild_bctr: CFLAGS += -m64 | ||
| 16 | |||
| 15 | $(OUTPUT)/tempfile: | 17 | $(OUTPUT)/tempfile: |
| 16 | dd if=/dev/zero of=$@ bs=64k count=1 | 18 | dd if=/dev/zero of=$@ bs=64k count=1 |
| 17 | 19 | ||
diff --git a/tools/testing/selftests/powerpc/mm/wild_bctr.c b/tools/testing/selftests/powerpc/mm/wild_bctr.c new file mode 100644 index 000000000000..1b0e9e9a2ddc --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/wild_bctr.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Copyright 2018, Michael Ellerman, IBM Corp. | ||
| 4 | * | ||
| 5 | * Test that an out-of-bounds branch to counter behaves as expected. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <setjmp.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <string.h> | ||
| 12 | #include <sys/mman.h> | ||
| 13 | #include <sys/types.h> | ||
| 14 | #include <sys/wait.h> | ||
| 15 | #include <ucontext.h> | ||
| 16 | #include <unistd.h> | ||
| 17 | |||
| 18 | #include "utils.h" | ||
| 19 | |||
| 20 | |||
| 21 | #define BAD_NIP 0x788c545a18000000ull | ||
| 22 | |||
| 23 | static struct pt_regs signal_regs; | ||
| 24 | static jmp_buf setjmp_env; | ||
| 25 | |||
| 26 | static void save_regs(ucontext_t *ctxt) | ||
| 27 | { | ||
| 28 | struct pt_regs *regs = ctxt->uc_mcontext.regs; | ||
| 29 | |||
| 30 | memcpy(&signal_regs, regs, sizeof(signal_regs)); | ||
| 31 | } | ||
| 32 | |||
| 33 | static void segv_handler(int signum, siginfo_t *info, void *ctxt_v) | ||
| 34 | { | ||
| 35 | save_regs(ctxt_v); | ||
| 36 | longjmp(setjmp_env, 1); | ||
| 37 | } | ||
| 38 | |||
| 39 | static void usr2_handler(int signum, siginfo_t *info, void *ctxt_v) | ||
| 40 | { | ||
| 41 | save_regs(ctxt_v); | ||
| 42 | } | ||
| 43 | |||
| 44 | static int ok(void) | ||
| 45 | { | ||
| 46 | printf("Everything is OK in here.\n"); | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | #define REG_POISON 0x5a5aUL | ||
| 51 | #define POISONED_REG(n) ((REG_POISON << 48) | ((n) << 32) | (REG_POISON << 16) | (n)) | ||
| 52 | |||
| 53 | static inline void poison_regs(void) | ||
| 54 | { | ||
| 55 | #define POISON_REG(n) \ | ||
| 56 | "lis " __stringify(n) "," __stringify(REG_POISON) ";" \ | ||
| 57 | "addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" \ | ||
| 58 | "sldi " __stringify(n) "," __stringify(n) ", 32 ;" \ | ||
| 59 | "oris " __stringify(n) "," __stringify(n) "," __stringify(REG_POISON) ";" \ | ||
| 60 | "addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" | ||
| 61 | |||
| 62 | asm (POISON_REG(15) | ||
| 63 | POISON_REG(16) | ||
| 64 | POISON_REG(17) | ||
| 65 | POISON_REG(18) | ||
| 66 | POISON_REG(19) | ||
| 67 | POISON_REG(20) | ||
| 68 | POISON_REG(21) | ||
| 69 | POISON_REG(22) | ||
| 70 | POISON_REG(23) | ||
| 71 | POISON_REG(24) | ||
| 72 | POISON_REG(25) | ||
| 73 | POISON_REG(26) | ||
| 74 | POISON_REG(27) | ||
| 75 | POISON_REG(28) | ||
| 76 | POISON_REG(29) | ||
| 77 | : // inputs | ||
| 78 | : // outputs | ||
| 79 | : "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", | ||
| 80 | "26", "27", "28", "29" | ||
| 81 | ); | ||
| 82 | #undef POISON_REG | ||
| 83 | } | ||
| 84 | |||
| 85 | static int check_regs(void) | ||
| 86 | { | ||
| 87 | unsigned long i; | ||
| 88 | |||
| 89 | for (i = 15; i <= 29; i++) | ||
| 90 | FAIL_IF(signal_regs.gpr[i] != POISONED_REG(i)); | ||
| 91 | |||
| 92 | printf("Regs OK\n"); | ||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | static void dump_regs(void) | ||
| 97 | { | ||
| 98 | for (int i = 0; i < 32; i += 4) { | ||
| 99 | printf("r%02d 0x%016lx r%02d 0x%016lx " \ | ||
| 100 | "r%02d 0x%016lx r%02d 0x%016lx\n", | ||
| 101 | i, signal_regs.gpr[i], | ||
| 102 | i+1, signal_regs.gpr[i+1], | ||
| 103 | i+2, signal_regs.gpr[i+2], | ||
| 104 | i+3, signal_regs.gpr[i+3]); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | int test_wild_bctr(void) | ||
| 109 | { | ||
| 110 | int (*func_ptr)(void); | ||
| 111 | struct sigaction segv = { | ||
| 112 | .sa_sigaction = segv_handler, | ||
| 113 | .sa_flags = SA_SIGINFO | ||
| 114 | }; | ||
| 115 | struct sigaction usr2 = { | ||
| 116 | .sa_sigaction = usr2_handler, | ||
| 117 | .sa_flags = SA_SIGINFO | ||
| 118 | }; | ||
| 119 | |||
| 120 | FAIL_IF(sigaction(SIGSEGV, &segv, NULL)); | ||
| 121 | FAIL_IF(sigaction(SIGUSR2, &usr2, NULL)); | ||
| 122 | |||
| 123 | bzero(&signal_regs, sizeof(signal_regs)); | ||
| 124 | |||
| 125 | if (setjmp(setjmp_env) == 0) { | ||
| 126 | func_ptr = ok; | ||
| 127 | func_ptr(); | ||
| 128 | |||
| 129 | kill(getpid(), SIGUSR2); | ||
| 130 | printf("Regs before:\n"); | ||
| 131 | dump_regs(); | ||
| 132 | bzero(&signal_regs, sizeof(signal_regs)); | ||
| 133 | |||
| 134 | poison_regs(); | ||
| 135 | |||
| 136 | func_ptr = (int (*)(void))BAD_NIP; | ||
| 137 | func_ptr(); | ||
| 138 | |||
| 139 | FAIL_IF(1); /* we didn't segv? */ | ||
| 140 | } | ||
| 141 | |||
| 142 | FAIL_IF(signal_regs.nip != BAD_NIP); | ||
| 143 | |||
| 144 | printf("All good - took SEGV as expected branching to 0x%llx\n", BAD_NIP); | ||
| 145 | |||
| 146 | dump_regs(); | ||
| 147 | FAIL_IF(check_regs()); | ||
| 148 | |||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | int main(void) | ||
| 153 | { | ||
| 154 | return test_harness(test_wild_bctr, "wild_bctr"); | ||
| 155 | } | ||
diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c index ed3239bbfae2..ee1e9ca22f0d 100644 --- a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c +++ b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c | |||
| @@ -65,14 +65,6 @@ static int unprotect_region(void) | |||
| 65 | extern char __start___ex_table[]; | 65 | extern char __start___ex_table[]; |
| 66 | extern char __stop___ex_table[]; | 66 | extern char __stop___ex_table[]; |
| 67 | 67 | ||
| 68 | #if defined(__powerpc64__) | ||
| 69 | #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP] | ||
| 70 | #elif defined(__powerpc__) | ||
| 71 | #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.uc_regs->gregs[PT_NIP] | ||
| 72 | #else | ||
| 73 | #error implement UCONTEXT_NIA | ||
| 74 | #endif | ||
| 75 | |||
| 76 | struct extbl_entry { | 68 | struct extbl_entry { |
| 77 | int insn; | 69 | int insn; |
| 78 | int fixup; | 70 | int fixup; |
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile index 923d531265f8..9b35ca8e8f13 100644 --- a/tools/testing/selftests/powerpc/ptrace/Makefile +++ b/tools/testing/selftests/powerpc/ptrace/Makefile | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ | 2 | TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ |
| 3 | ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ | 3 | ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ |
| 4 | ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \ | 4 | ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \ |
| 5 | perf-hwbreak | 5 | perf-hwbreak ptrace-syscall |
| 6 | 6 | ||
| 7 | top_srcdir = ../../../../.. | 7 | top_srcdir = ../../../../.. |
| 8 | include ../../lib.mk | 8 | include ../../lib.mk |
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c b/tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c new file mode 100644 index 000000000000..3353210dcdbd --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and | ||
| 4 | * PTRACE_GETREG. This test basically create a child process that executes | ||
| 5 | * syscalls and the parent process check if it is being traced appropriated. | ||
| 6 | * | ||
| 7 | * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c | ||
| 8 | * test, and it was adapted to run on Powerpc by | ||
| 9 | * Breno Leitao <leitao@debian.org> | ||
| 10 | */ | ||
| 11 | #define _GNU_SOURCE | ||
| 12 | |||
| 13 | #include <sys/ptrace.h> | ||
| 14 | #include <sys/types.h> | ||
| 15 | #include <sys/wait.h> | ||
| 16 | #include <sys/syscall.h> | ||
| 17 | #include <sys/user.h> | ||
| 18 | #include <unistd.h> | ||
| 19 | #include <errno.h> | ||
| 20 | #include <stddef.h> | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <err.h> | ||
| 23 | #include <string.h> | ||
| 24 | #include <sys/auxv.h> | ||
| 25 | #include "utils.h" | ||
| 26 | |||
| 27 | /* Bitness-agnostic defines for user_regs_struct fields. */ | ||
| 28 | #define user_syscall_nr gpr[0] | ||
| 29 | #define user_arg0 gpr[3] | ||
| 30 | #define user_arg1 gpr[4] | ||
| 31 | #define user_arg2 gpr[5] | ||
| 32 | #define user_arg3 gpr[6] | ||
| 33 | #define user_arg4 gpr[7] | ||
| 34 | #define user_arg5 gpr[8] | ||
| 35 | #define user_ip nip | ||
| 36 | |||
| 37 | #define PTRACE_SYSEMU 0x1d | ||
| 38 | |||
| 39 | static int nerrs; | ||
| 40 | |||
| 41 | static void wait_trap(pid_t chld) | ||
| 42 | { | ||
| 43 | siginfo_t si; | ||
| 44 | |||
| 45 | if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0) | ||
| 46 | err(1, "waitid"); | ||
| 47 | if (si.si_pid != chld) | ||
| 48 | errx(1, "got unexpected pid in event\n"); | ||
| 49 | if (si.si_code != CLD_TRAPPED) | ||
| 50 | errx(1, "got unexpected event type %d\n", si.si_code); | ||
| 51 | } | ||
| 52 | |||
| 53 | static void test_ptrace_syscall_restart(void) | ||
| 54 | { | ||
| 55 | int status; | ||
| 56 | struct pt_regs regs; | ||
| 57 | pid_t chld; | ||
| 58 | |||
| 59 | printf("[RUN]\tptrace-induced syscall restart\n"); | ||
| 60 | |||
| 61 | chld = fork(); | ||
| 62 | if (chld < 0) | ||
| 63 | err(1, "fork"); | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Child process is running 4 syscalls after ptrace. | ||
| 67 | * | ||
| 68 | * 1) getpid() | ||
| 69 | * 2) gettid() | ||
| 70 | * 3) tgkill() -> Send SIGSTOP | ||
| 71 | * 4) gettid() -> Where the tests will happen essentially | ||
| 72 | */ | ||
| 73 | if (chld == 0) { | ||
| 74 | if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) | ||
| 75 | err(1, "PTRACE_TRACEME"); | ||
| 76 | |||
| 77 | pid_t pid = getpid(), tid = syscall(SYS_gettid); | ||
| 78 | |||
| 79 | printf("\tChild will make one syscall\n"); | ||
| 80 | syscall(SYS_tgkill, pid, tid, SIGSTOP); | ||
| 81 | |||
| 82 | syscall(SYS_gettid, 10, 11, 12, 13, 14, 15); | ||
| 83 | _exit(0); | ||
| 84 | } | ||
| 85 | /* Parent process below */ | ||
| 86 | |||
| 87 | /* Wait for SIGSTOP sent by tgkill above. */ | ||
| 88 | if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status)) | ||
| 89 | err(1, "waitpid"); | ||
| 90 | |||
| 91 | printf("[RUN]\tSYSEMU\n"); | ||
| 92 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | ||
| 93 | err(1, "PTRACE_SYSEMU"); | ||
| 94 | wait_trap(chld); | ||
| 95 | |||
| 96 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | ||
| 97 | err(1, "PTRACE_GETREGS"); | ||
| 98 | |||
| 99 | /* | ||
| 100 | * Ptrace trapped prior to executing the syscall, thus r3 still has | ||
| 101 | * the syscall number instead of the sys_gettid() result | ||
| 102 | */ | ||
| 103 | if (regs.user_syscall_nr != SYS_gettid || | ||
| 104 | regs.user_arg0 != 10 || regs.user_arg1 != 11 || | ||
| 105 | regs.user_arg2 != 12 || regs.user_arg3 != 13 || | ||
| 106 | regs.user_arg4 != 14 || regs.user_arg5 != 15) { | ||
| 107 | printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", | ||
| 108 | (unsigned long)regs.user_syscall_nr, | ||
| 109 | (unsigned long)regs.user_arg0, | ||
| 110 | (unsigned long)regs.user_arg1, | ||
| 111 | (unsigned long)regs.user_arg2, | ||
| 112 | (unsigned long)regs.user_arg3, | ||
| 113 | (unsigned long)regs.user_arg4, | ||
| 114 | (unsigned long)regs.user_arg5); | ||
| 115 | nerrs++; | ||
| 116 | } else { | ||
| 117 | printf("[OK]\tInitial nr and args are correct\n"); } | ||
| 118 | |||
| 119 | printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n", | ||
| 120 | (unsigned long)regs.user_ip); | ||
| 121 | |||
| 122 | /* | ||
| 123 | * Rewind to retry the same syscall again. This will basically test | ||
| 124 | * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS. | ||
| 125 | */ | ||
| 126 | regs.user_ip -= 4; | ||
| 127 | if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) | ||
| 128 | err(1, "PTRACE_SETREGS"); | ||
| 129 | |||
| 130 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | ||
| 131 | err(1, "PTRACE_SYSEMU"); | ||
| 132 | wait_trap(chld); | ||
| 133 | |||
| 134 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | ||
| 135 | err(1, "PTRACE_GETREGS"); | ||
| 136 | |||
| 137 | if (regs.user_syscall_nr != SYS_gettid || | ||
| 138 | regs.user_arg0 != 10 || regs.user_arg1 != 11 || | ||
| 139 | regs.user_arg2 != 12 || regs.user_arg3 != 13 || | ||
| 140 | regs.user_arg4 != 14 || regs.user_arg5 != 15) { | ||
| 141 | printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", | ||
| 142 | (unsigned long)regs.user_syscall_nr, | ||
| 143 | (unsigned long)regs.user_arg0, | ||
| 144 | (unsigned long)regs.user_arg1, | ||
| 145 | (unsigned long)regs.user_arg2, | ||
| 146 | (unsigned long)regs.user_arg3, | ||
| 147 | (unsigned long)regs.user_arg4, | ||
| 148 | (unsigned long)regs.user_arg5); | ||
| 149 | nerrs++; | ||
| 150 | } else { | ||
| 151 | printf("[OK]\tRestarted nr and args are correct\n"); | ||
| 152 | } | ||
| 153 | |||
| 154 | printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n", | ||
| 155 | (unsigned long)regs.user_ip); | ||
| 156 | |||
| 157 | /* | ||
| 158 | * Inject a new syscall (getpid) in the same place the previous | ||
| 159 | * syscall (gettid), rewind and re-execute. | ||
| 160 | */ | ||
| 161 | regs.user_syscall_nr = SYS_getpid; | ||
| 162 | regs.user_arg0 = 20; | ||
| 163 | regs.user_arg1 = 21; | ||
| 164 | regs.user_arg2 = 22; | ||
| 165 | regs.user_arg3 = 23; | ||
| 166 | regs.user_arg4 = 24; | ||
| 167 | regs.user_arg5 = 25; | ||
| 168 | regs.user_ip -= 4; | ||
| 169 | |||
| 170 | if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) | ||
| 171 | err(1, "PTRACE_SETREGS"); | ||
| 172 | |||
| 173 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | ||
| 174 | err(1, "PTRACE_SYSEMU"); | ||
| 175 | wait_trap(chld); | ||
| 176 | |||
| 177 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | ||
| 178 | err(1, "PTRACE_GETREGS"); | ||
| 179 | |||
| 180 | /* Check that ptrace stopped at the new syscall that was | ||
| 181 | * injected, and guarantee that it haven't executed, i.e, user_args | ||
| 182 | * contain the arguments and not the syscall return value, for | ||
| 183 | * instance. | ||
| 184 | */ | ||
| 185 | if (regs.user_syscall_nr != SYS_getpid | ||
| 186 | || regs.user_arg0 != 20 || regs.user_arg1 != 21 | ||
| 187 | || regs.user_arg2 != 22 || regs.user_arg3 != 23 | ||
| 188 | || regs.user_arg4 != 24 || regs.user_arg5 != 25) { | ||
| 189 | |||
| 190 | printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", | ||
| 191 | (unsigned long)regs.user_syscall_nr, | ||
| 192 | (unsigned long)regs.user_arg0, | ||
| 193 | (unsigned long)regs.user_arg1, | ||
| 194 | (unsigned long)regs.user_arg2, | ||
| 195 | (unsigned long)regs.user_arg3, | ||
| 196 | (unsigned long)regs.user_arg4, | ||
| 197 | (unsigned long)regs.user_arg5); | ||
| 198 | nerrs++; | ||
| 199 | } else { | ||
| 200 | printf("[OK]\tReplacement nr and args are correct\n"); | ||
| 201 | } | ||
| 202 | |||
| 203 | if (ptrace(PTRACE_CONT, chld, 0, 0) != 0) | ||
| 204 | err(1, "PTRACE_CONT"); | ||
| 205 | |||
| 206 | if (waitpid(chld, &status, 0) != chld) | ||
| 207 | err(1, "waitpid"); | ||
| 208 | |||
| 209 | /* Guarantee that the process executed properly, returning 0 */ | ||
| 210 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | ||
| 211 | printf("[FAIL]\tChild failed\n"); | ||
| 212 | nerrs++; | ||
| 213 | } else { | ||
| 214 | printf("[OK]\tChild exited cleanly\n"); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | int ptrace_syscall(void) | ||
| 219 | { | ||
| 220 | test_ptrace_syscall_restart(); | ||
| 221 | |||
| 222 | return nerrs; | ||
| 223 | } | ||
| 224 | |||
| 225 | int main(void) | ||
| 226 | { | ||
| 227 | return test_harness(ptrace_syscall, "ptrace_syscall"); | ||
| 228 | } | ||
diff --git a/tools/testing/selftests/powerpc/security/Makefile b/tools/testing/selftests/powerpc/security/Makefile new file mode 100644 index 000000000000..44690f1bb26a --- /dev/null +++ b/tools/testing/selftests/powerpc/security/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | |||
| 3 | TEST_GEN_PROGS := rfi_flush | ||
| 4 | |||
| 5 | CFLAGS += -I../../../../../usr/include | ||
| 6 | |||
| 7 | include ../../lib.mk | ||
| 8 | |||
| 9 | $(TEST_GEN_PROGS): ../harness.c ../utils.c | ||
diff --git a/tools/testing/selftests/powerpc/security/rfi_flush.c b/tools/testing/selftests/powerpc/security/rfi_flush.c new file mode 100644 index 000000000000..564ed45bbf73 --- /dev/null +++ b/tools/testing/selftests/powerpc/security/rfi_flush.c | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | |||
| 3 | /* | ||
| 4 | * Copyright 2018 IBM Corporation. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #define __SANE_USERSPACE_TYPES__ | ||
| 8 | |||
| 9 | #include <sys/types.h> | ||
| 10 | #include <stdint.h> | ||
| 11 | #include <malloc.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | #include <stdlib.h> | ||
| 14 | #include <string.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include "utils.h" | ||
| 17 | |||
| 18 | #define CACHELINE_SIZE 128 | ||
| 19 | |||
| 20 | struct perf_event_read { | ||
| 21 | __u64 nr; | ||
| 22 | __u64 l1d_misses; | ||
| 23 | }; | ||
| 24 | |||
| 25 | static inline __u64 load(void *addr) | ||
| 26 | { | ||
| 27 | __u64 tmp; | ||
| 28 | |||
| 29 | asm volatile("ld %0,0(%1)" : "=r"(tmp) : "b"(addr)); | ||
| 30 | |||
| 31 | return tmp; | ||
| 32 | } | ||
| 33 | |||
| 34 | static void syscall_loop(char *p, unsigned long iterations, | ||
| 35 | unsigned long zero_size) | ||
| 36 | { | ||
| 37 | for (unsigned long i = 0; i < iterations; i++) { | ||
| 38 | for (unsigned long j = 0; j < zero_size; j += CACHELINE_SIZE) | ||
| 39 | load(p + j); | ||
| 40 | getppid(); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | int rfi_flush_test(void) | ||
| 45 | { | ||
| 46 | char *p; | ||
| 47 | int repetitions = 10; | ||
| 48 | int fd, passes = 0, iter, rc = 0; | ||
| 49 | struct perf_event_read v; | ||
| 50 | __u64 l1d_misses_total = 0; | ||
| 51 | unsigned long iterations = 100000, zero_size = 24 * 1024; | ||
| 52 | int rfi_flush_org, rfi_flush; | ||
| 53 | |||
| 54 | SKIP_IF(geteuid() != 0); | ||
| 55 | |||
| 56 | if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_org)) { | ||
| 57 | perror("Unable to read powerpc/rfi_flush debugfs file"); | ||
| 58 | SKIP_IF(1); | ||
| 59 | } | ||
| 60 | |||
| 61 | rfi_flush = rfi_flush_org; | ||
| 62 | |||
| 63 | fd = perf_event_open_counter(PERF_TYPE_RAW, /* L1d miss */ 0x400f0, -1); | ||
| 64 | FAIL_IF(fd < 0); | ||
| 65 | |||
| 66 | p = (char *)memalign(zero_size, CACHELINE_SIZE); | ||
| 67 | |||
| 68 | FAIL_IF(perf_event_enable(fd)); | ||
| 69 | |||
| 70 | set_dscr(1); | ||
| 71 | |||
| 72 | iter = repetitions; | ||
| 73 | |||
| 74 | again: | ||
| 75 | FAIL_IF(perf_event_reset(fd)); | ||
| 76 | |||
| 77 | syscall_loop(p, iterations, zero_size); | ||
| 78 | |||
| 79 | FAIL_IF(read(fd, &v, sizeof(v)) != sizeof(v)); | ||
| 80 | |||
| 81 | /* Expect at least zero_size/CACHELINE_SIZE misses per iteration */ | ||
| 82 | if (v.l1d_misses >= (iterations * zero_size / CACHELINE_SIZE) && rfi_flush) | ||
| 83 | passes++; | ||
| 84 | else if (v.l1d_misses < iterations && !rfi_flush) | ||
| 85 | passes++; | ||
| 86 | |||
| 87 | l1d_misses_total += v.l1d_misses; | ||
| 88 | |||
| 89 | while (--iter) | ||
| 90 | goto again; | ||
| 91 | |||
| 92 | if (passes < repetitions) { | ||
| 93 | printf("FAIL (L1D misses with rfi_flush=%d: %llu %c %lu) [%d/%d failures]\n", | ||
| 94 | rfi_flush, l1d_misses_total, rfi_flush ? '<' : '>', | ||
| 95 | rfi_flush ? (repetitions * iterations * zero_size / CACHELINE_SIZE) : iterations, | ||
| 96 | repetitions - passes, repetitions); | ||
| 97 | rc = 1; | ||
| 98 | } else | ||
| 99 | printf("PASS (L1D misses with rfi_flush=%d: %llu %c %lu) [%d/%d pass]\n", | ||
| 100 | rfi_flush, l1d_misses_total, rfi_flush ? '>' : '<', | ||
| 101 | rfi_flush ? (repetitions * iterations * zero_size / CACHELINE_SIZE) : iterations, | ||
| 102 | passes, repetitions); | ||
| 103 | |||
| 104 | if (rfi_flush == rfi_flush_org) { | ||
| 105 | rfi_flush = !rfi_flush_org; | ||
| 106 | if (write_debugfs_file("powerpc/rfi_flush", rfi_flush) < 0) { | ||
| 107 | perror("error writing to powerpc/rfi_flush debugfs file"); | ||
| 108 | return 1; | ||
| 109 | } | ||
| 110 | iter = repetitions; | ||
| 111 | l1d_misses_total = 0; | ||
| 112 | passes = 0; | ||
| 113 | goto again; | ||
| 114 | } | ||
| 115 | |||
| 116 | perf_event_disable(fd); | ||
| 117 | close(fd); | ||
| 118 | |||
| 119 | set_dscr(0); | ||
| 120 | |||
| 121 | if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_org) < 0) { | ||
| 122 | perror("unable to restore original value of powerpc/rfi_flush debugfs file"); | ||
| 123 | return 1; | ||
| 124 | } | ||
| 125 | |||
| 126 | return rc; | ||
| 127 | } | ||
| 128 | |||
| 129 | int main(int argc, char *argv[]) | ||
| 130 | { | ||
| 131 | return test_harness(rfi_flush_test, "rfi_flush_test"); | ||
| 132 | } | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm-tmspr.c b/tools/testing/selftests/powerpc/tm/tm-tmspr.c index 2bda81c7bf23..df1d7d4b1c89 100644 --- a/tools/testing/selftests/powerpc/tm/tm-tmspr.c +++ b/tools/testing/selftests/powerpc/tm/tm-tmspr.c | |||
| @@ -98,7 +98,7 @@ void texasr(void *in) | |||
| 98 | 98 | ||
| 99 | int test_tmspr() | 99 | int test_tmspr() |
| 100 | { | 100 | { |
| 101 | pthread_t thread; | 101 | pthread_t *thread; |
| 102 | int thread_num; | 102 | int thread_num; |
| 103 | unsigned long i; | 103 | unsigned long i; |
| 104 | 104 | ||
| @@ -107,21 +107,28 @@ int test_tmspr() | |||
| 107 | /* To cause some context switching */ | 107 | /* To cause some context switching */ |
| 108 | thread_num = 10 * sysconf(_SC_NPROCESSORS_ONLN); | 108 | thread_num = 10 * sysconf(_SC_NPROCESSORS_ONLN); |
| 109 | 109 | ||
| 110 | thread = malloc(thread_num * sizeof(pthread_t)); | ||
| 111 | if (thread == NULL) | ||
| 112 | return EXIT_FAILURE; | ||
| 113 | |||
| 110 | /* Test TFIAR and TFHAR */ | 114 | /* Test TFIAR and TFHAR */ |
| 111 | for (i = 0 ; i < thread_num ; i += 2){ | 115 | for (i = 0; i < thread_num; i += 2) { |
| 112 | if (pthread_create(&thread, NULL, (void*)tfiar_tfhar, (void *)i)) | 116 | if (pthread_create(&thread[i], NULL, (void *)tfiar_tfhar, |
| 117 | (void *)i)) | ||
| 113 | return EXIT_FAILURE; | 118 | return EXIT_FAILURE; |
| 114 | } | 119 | } |
| 115 | if (pthread_join(thread, NULL) != 0) | ||
| 116 | return EXIT_FAILURE; | ||
| 117 | |||
| 118 | /* Test TEXASR */ | 120 | /* Test TEXASR */ |
| 119 | for (i = 0 ; i < thread_num ; i++){ | 121 | for (i = 1; i < thread_num; i += 2) { |
| 120 | if (pthread_create(&thread, NULL, (void*)texasr, (void *)i)) | 122 | if (pthread_create(&thread[i], NULL, (void *)texasr, (void *)i)) |
| 121 | return EXIT_FAILURE; | 123 | return EXIT_FAILURE; |
| 122 | } | 124 | } |
| 123 | if (pthread_join(thread, NULL) != 0) | 125 | |
| 124 | return EXIT_FAILURE; | 126 | for (i = 0; i < thread_num; i++) { |
| 127 | if (pthread_join(thread[i], NULL) != 0) | ||
| 128 | return EXIT_FAILURE; | ||
| 129 | } | ||
| 130 | |||
| 131 | free(thread); | ||
| 125 | 132 | ||
| 126 | if (passed) | 133 | if (passed) |
| 127 | return 0; | 134 | return 0; |
diff --git a/tools/testing/selftests/powerpc/tm/tm-unavailable.c b/tools/testing/selftests/powerpc/tm/tm-unavailable.c index 156c8e750259..09894f4ff62e 100644 --- a/tools/testing/selftests/powerpc/tm/tm-unavailable.c +++ b/tools/testing/selftests/powerpc/tm/tm-unavailable.c | |||
| @@ -236,7 +236,8 @@ void *tm_una_ping(void *input) | |||
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | /* Check if we were not expecting a failure and a it occurred. */ | 238 | /* Check if we were not expecting a failure and a it occurred. */ |
| 239 | if (!expecting_failure() && is_failure(cr_)) { | 239 | if (!expecting_failure() && is_failure(cr_) && |
| 240 | !failure_is_reschedule()) { | ||
| 240 | printf("\n\tUnexpected transaction failure 0x%02lx\n\t", | 241 | printf("\n\tUnexpected transaction failure 0x%02lx\n\t", |
| 241 | failure_code()); | 242 | failure_code()); |
| 242 | return (void *) -1; | 243 | return (void *) -1; |
| @@ -244,9 +245,11 @@ void *tm_una_ping(void *input) | |||
| 244 | 245 | ||
| 245 | /* | 246 | /* |
| 246 | * Check if TM failed due to the cause we were expecting. 0xda is a | 247 | * Check if TM failed due to the cause we were expecting. 0xda is a |
| 247 | * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause. | 248 | * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause, unless |
| 249 | * it was caused by a reschedule. | ||
| 248 | */ | 250 | */ |
| 249 | if (is_failure(cr_) && !failure_is_unavailable()) { | 251 | if (is_failure(cr_) && !failure_is_unavailable() && |
| 252 | !failure_is_reschedule()) { | ||
| 250 | printf("\n\tUnexpected failure cause 0x%02lx\n\t", | 253 | printf("\n\tUnexpected failure cause 0x%02lx\n\t", |
| 251 | failure_code()); | 254 | failure_code()); |
| 252 | return (void *) -1; | 255 | return (void *) -1; |
diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index df4204247d45..5518b1d4ef8b 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h | |||
| @@ -52,6 +52,15 @@ static inline bool failure_is_unavailable(void) | |||
| 52 | return (failure_code() & TM_CAUSE_FAC_UNAV) == TM_CAUSE_FAC_UNAV; | 52 | return (failure_code() & TM_CAUSE_FAC_UNAV) == TM_CAUSE_FAC_UNAV; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | static inline bool failure_is_reschedule(void) | ||
| 56 | { | ||
| 57 | if ((failure_code() & TM_CAUSE_RESCHED) == TM_CAUSE_RESCHED || | ||
| 58 | (failure_code() & TM_CAUSE_KVM_RESCHED) == TM_CAUSE_KVM_RESCHED) | ||
| 59 | return true; | ||
| 60 | |||
| 61 | return false; | ||
| 62 | } | ||
| 63 | |||
| 55 | static inline bool failure_is_nesting(void) | 64 | static inline bool failure_is_nesting(void) |
| 56 | { | 65 | { |
| 57 | return (__builtin_get_texasru() & 0x400000); | 66 | return (__builtin_get_texasru() & 0x400000); |
diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c index aa8fc1e6365b..43c342845be0 100644 --- a/tools/testing/selftests/powerpc/utils.c +++ b/tools/testing/selftests/powerpc/utils.c | |||
| @@ -10,16 +10,22 @@ | |||
| 10 | #include <fcntl.h> | 10 | #include <fcntl.h> |
| 11 | #include <link.h> | 11 | #include <link.h> |
| 12 | #include <sched.h> | 12 | #include <sched.h> |
| 13 | #include <signal.h> | ||
| 13 | #include <stdio.h> | 14 | #include <stdio.h> |
| 15 | #include <stdlib.h> | ||
| 14 | #include <string.h> | 16 | #include <string.h> |
| 17 | #include <sys/ioctl.h> | ||
| 15 | #include <sys/stat.h> | 18 | #include <sys/stat.h> |
| 16 | #include <sys/types.h> | 19 | #include <sys/types.h> |
| 17 | #include <sys/utsname.h> | 20 | #include <sys/utsname.h> |
| 18 | #include <unistd.h> | 21 | #include <unistd.h> |
| 22 | #include <asm/unistd.h> | ||
| 23 | #include <linux/limits.h> | ||
| 19 | 24 | ||
| 20 | #include "utils.h" | 25 | #include "utils.h" |
| 21 | 26 | ||
| 22 | static char auxv[4096]; | 27 | static char auxv[4096]; |
| 28 | extern unsigned int dscr_insn[]; | ||
| 23 | 29 | ||
| 24 | int read_auxv(char *buf, ssize_t buf_size) | 30 | int read_auxv(char *buf, ssize_t buf_size) |
| 25 | { | 31 | { |
| @@ -121,3 +127,149 @@ bool is_ppc64le(void) | |||
| 121 | 127 | ||
| 122 | return strcmp(uts.machine, "ppc64le") == 0; | 128 | return strcmp(uts.machine, "ppc64le") == 0; |
| 123 | } | 129 | } |
| 130 | |||
| 131 | int read_debugfs_file(char *debugfs_file, int *result) | ||
| 132 | { | ||
| 133 | int rc = -1, fd; | ||
| 134 | char path[PATH_MAX]; | ||
| 135 | char value[16]; | ||
| 136 | |||
| 137 | strcpy(path, "/sys/kernel/debug/"); | ||
| 138 | strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); | ||
| 139 | |||
| 140 | if ((fd = open(path, O_RDONLY)) < 0) | ||
| 141 | return rc; | ||
| 142 | |||
| 143 | if ((rc = read(fd, value, sizeof(value))) < 0) | ||
| 144 | return rc; | ||
| 145 | |||
| 146 | value[15] = 0; | ||
| 147 | *result = atoi(value); | ||
| 148 | close(fd); | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | int write_debugfs_file(char *debugfs_file, int result) | ||
| 154 | { | ||
| 155 | int rc = -1, fd; | ||
| 156 | char path[PATH_MAX]; | ||
| 157 | char value[16]; | ||
| 158 | |||
| 159 | strcpy(path, "/sys/kernel/debug/"); | ||
| 160 | strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); | ||
| 161 | |||
| 162 | if ((fd = open(path, O_WRONLY)) < 0) | ||
| 163 | return rc; | ||
| 164 | |||
| 165 | snprintf(value, 16, "%d", result); | ||
| 166 | |||
| 167 | if ((rc = write(fd, value, strlen(value))) < 0) | ||
| 168 | return rc; | ||
| 169 | |||
| 170 | close(fd); | ||
| 171 | |||
| 172 | return 0; | ||
| 173 | } | ||
| 174 | |||
| 175 | static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, | ||
| 176 | int cpu, int group_fd, unsigned long flags) | ||
| 177 | { | ||
| 178 | return syscall(__NR_perf_event_open, hw_event, pid, cpu, | ||
| 179 | group_fd, flags); | ||
| 180 | } | ||
| 181 | |||
| 182 | static void perf_event_attr_init(struct perf_event_attr *event_attr, | ||
| 183 | unsigned int type, | ||
| 184 | unsigned long config) | ||
| 185 | { | ||
| 186 | memset(event_attr, 0, sizeof(*event_attr)); | ||
| 187 | |||
| 188 | event_attr->type = type; | ||
| 189 | event_attr->size = sizeof(struct perf_event_attr); | ||
| 190 | event_attr->config = config; | ||
| 191 | event_attr->read_format = PERF_FORMAT_GROUP; | ||
| 192 | event_attr->disabled = 1; | ||
| 193 | event_attr->exclude_kernel = 1; | ||
| 194 | event_attr->exclude_hv = 1; | ||
| 195 | event_attr->exclude_guest = 1; | ||
| 196 | } | ||
| 197 | |||
| 198 | int perf_event_open_counter(unsigned int type, | ||
| 199 | unsigned long config, int group_fd) | ||
| 200 | { | ||
| 201 | int fd; | ||
| 202 | struct perf_event_attr event_attr; | ||
| 203 | |||
| 204 | perf_event_attr_init(&event_attr, type, config); | ||
| 205 | |||
| 206 | fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); | ||
| 207 | |||
| 208 | if (fd < 0) | ||
| 209 | perror("perf_event_open() failed"); | ||
| 210 | |||
| 211 | return fd; | ||
| 212 | } | ||
| 213 | |||
| 214 | int perf_event_enable(int fd) | ||
| 215 | { | ||
| 216 | if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { | ||
| 217 | perror("error while enabling perf events"); | ||
| 218 | return -1; | ||
| 219 | } | ||
| 220 | |||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | int perf_event_disable(int fd) | ||
| 225 | { | ||
| 226 | if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { | ||
| 227 | perror("error disabling perf events"); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | int perf_event_reset(int fd) | ||
| 235 | { | ||
| 236 | if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { | ||
| 237 | perror("error resetting perf events"); | ||
| 238 | return -1; | ||
| 239 | } | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static void sigill_handler(int signr, siginfo_t *info, void *unused) | ||
| 245 | { | ||
| 246 | static int warned = 0; | ||
| 247 | ucontext_t *ctx = (ucontext_t *)unused; | ||
| 248 | unsigned long *pc = &UCONTEXT_NIA(ctx); | ||
| 249 | |||
| 250 | if (*pc == (unsigned long)&dscr_insn) { | ||
| 251 | if (!warned++) | ||
| 252 | printf("WARNING: Skipping over dscr setup. Consider running 'ppc64_cpu --dscr=1' manually.\n"); | ||
| 253 | *pc += 4; | ||
| 254 | } else { | ||
| 255 | printf("SIGILL at %p\n", pc); | ||
| 256 | abort(); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | void set_dscr(unsigned long val) | ||
| 261 | { | ||
| 262 | static int init = 0; | ||
| 263 | struct sigaction sa; | ||
| 264 | |||
| 265 | if (!init) { | ||
| 266 | memset(&sa, 0, sizeof(sa)); | ||
| 267 | sa.sa_sigaction = sigill_handler; | ||
| 268 | sa.sa_flags = SA_SIGINFO; | ||
| 269 | if (sigaction(SIGILL, &sa, NULL)) | ||
| 270 | perror("sigill_handler"); | ||
| 271 | init = 1; | ||
| 272 | } | ||
| 273 | |||
| 274 | asm volatile("dscr_insn: mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR)); | ||
| 275 | } | ||
