aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/testing/selftests/powerpc/Makefile1
-rw-r--r--tools/testing/selftests/powerpc/alignment/.gitignore1
-rw-r--r--tools/testing/selftests/powerpc/benchmarks/exec_target.c7
-rw-r--r--tools/testing/selftests/powerpc/context_switch/.gitignore1
-rw-r--r--tools/testing/selftests/powerpc/context_switch/Makefile5
-rw-r--r--tools/testing/selftests/powerpc/context_switch/cp_abort.c110
-rw-r--r--tools/testing/selftests/powerpc/include/reg.h1
-rw-r--r--tools/testing/selftests/powerpc/ptrace/.gitignore2
-rw-r--r--tools/testing/selftests/powerpc/ptrace/Makefile6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/child.h139
-rw-r--r--tools/testing/selftests/powerpc/ptrace/core-pkey.c461
-rw-r--r--tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c195
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c342
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c327
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace.h38
-rw-r--r--tools/testing/selftests/powerpc/tm/.gitignore1
16 files changed, 1517 insertions, 120 deletions
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index f6b1338730db..201b598558b9 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -17,7 +17,6 @@ SUB_DIRS = alignment \
17 benchmarks \ 17 benchmarks \
18 cache_shape \ 18 cache_shape \
19 copyloops \ 19 copyloops \
20 context_switch \
21 dscr \ 20 dscr \
22 mm \ 21 mm \
23 pmu \ 22 pmu \
diff --git a/tools/testing/selftests/powerpc/alignment/.gitignore b/tools/testing/selftests/powerpc/alignment/.gitignore
index 1d980e3d7039..9d383073b7ad 100644
--- a/tools/testing/selftests/powerpc/alignment/.gitignore
+++ b/tools/testing/selftests/powerpc/alignment/.gitignore
@@ -3,3 +3,4 @@ copy_first_unaligned
3paste_unaligned 3paste_unaligned
4paste_last_unaligned 4paste_last_unaligned
5copy_paste_unaligned_common 5copy_paste_unaligned_common
6alignment_handler
diff --git a/tools/testing/selftests/powerpc/benchmarks/exec_target.c b/tools/testing/selftests/powerpc/benchmarks/exec_target.c
index 3c9c144192be..c14b0fc1edde 100644
--- a/tools/testing/selftests/powerpc/benchmarks/exec_target.c
+++ b/tools/testing/selftests/powerpc/benchmarks/exec_target.c
@@ -6,8 +6,11 @@
6 * Copyright 2018, Anton Blanchard, IBM Corp. 6 * Copyright 2018, Anton Blanchard, IBM Corp.
7 */ 7 */
8 8
9void _exit(int); 9#define _GNU_SOURCE
10#include <unistd.h>
11#include <sys/syscall.h>
12
10void _start(void) 13void _start(void)
11{ 14{
12 _exit(0); 15 syscall(SYS_exit, 0);
13} 16}
diff --git a/tools/testing/selftests/powerpc/context_switch/.gitignore b/tools/testing/selftests/powerpc/context_switch/.gitignore
deleted file mode 100644
index c1431af7b51c..000000000000
--- a/tools/testing/selftests/powerpc/context_switch/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1cp_abort
diff --git a/tools/testing/selftests/powerpc/context_switch/Makefile b/tools/testing/selftests/powerpc/context_switch/Makefile
deleted file mode 100644
index e9351bb4285d..000000000000
--- a/tools/testing/selftests/powerpc/context_switch/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
1TEST_GEN_PROGS := cp_abort
2
3include ../../lib.mk
4
5$(TEST_GEN_PROGS): ../harness.c ../utils.c
diff --git a/tools/testing/selftests/powerpc/context_switch/cp_abort.c b/tools/testing/selftests/powerpc/context_switch/cp_abort.c
deleted file mode 100644
index 5a5b55afda0e..000000000000
--- a/tools/testing/selftests/powerpc/context_switch/cp_abort.c
+++ /dev/null
@@ -1,110 +0,0 @@
1/*
2 * Adapted from Anton Blanchard's context switch microbenchmark.
3 *
4 * Copyright 2009, Anton Blanchard, IBM Corporation.
5 * Copyright 2016, Mikey Neuling, Chris Smart, IBM Corporation.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 * This program tests the copy paste abort functionality of a P9
13 * (or later) by setting up two processes on the same CPU, one
14 * which executes the copy instruction and the other which
15 * executes paste.
16 *
17 * The paste instruction should never succeed, as the cp_abort
18 * instruction is called by the kernel during a context switch.
19 *
20 */
21
22#define _GNU_SOURCE
23
24#include <stdio.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include "utils.h"
28#include <sched.h>
29
30#define READ_FD 0
31#define WRITE_FD 1
32
33#define NUM_LOOPS 1000
34
35/* This defines the "paste" instruction from Power ISA 3.0 Book II, section 4.4. */
36#define PASTE(RA, RB, L, RC) \
37 .long (0x7c00070c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10) | (RC) << (31-31))
38
39int paste(void *i)
40{
41 int cr;
42
43 asm volatile(str(PASTE(0, %1, 1, 1))";"
44 "mfcr %0;"
45 : "=r" (cr)
46 : "b" (i)
47 : "memory"
48 );
49 return cr;
50}
51
52/* This defines the "copy" instruction from Power ISA 3.0 Book II, section 4.4. */
53#define COPY(RA, RB, L) \
54 .long (0x7c00060c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10))
55
56void copy(void *i)
57{
58 asm volatile(str(COPY(0, %0, 1))";"
59 :
60 : "b" (i)
61 : "memory"
62 );
63}
64
65int test_cp_abort(void)
66{
67 /* 128 bytes for a full cache line */
68 char buf[128] __cacheline_aligned;
69 cpu_set_t cpuset;
70 int fd1[2], fd2[2], pid;
71 char c;
72
73 /* only run this test on a P9 or later */
74 SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));
75
76 /*
77 * Run both processes on the same CPU, so that copy is more likely
78 * to leak into a paste.
79 */
80 CPU_ZERO(&cpuset);
81 CPU_SET(pick_online_cpu(), &cpuset);
82 FAIL_IF(sched_setaffinity(0, sizeof(cpuset), &cpuset));
83
84 FAIL_IF(pipe(fd1) || pipe(fd2));
85
86 pid = fork();
87 FAIL_IF(pid < 0);
88
89 if (!pid) {
90 for (int i = 0; i < NUM_LOOPS; i++) {
91 FAIL_IF((write(fd1[WRITE_FD], &c, 1)) != 1);
92 FAIL_IF((read(fd2[READ_FD], &c, 1)) != 1);
93 /* A paste succeeds if CR0 EQ bit is set */
94 FAIL_IF(paste(buf) & 0x20000000);
95 }
96 } else {
97 for (int i = 0; i < NUM_LOOPS; i++) {
98 FAIL_IF((read(fd1[READ_FD], &c, 1)) != 1);
99 copy(buf);
100 FAIL_IF((write(fd2[WRITE_FD], &c, 1) != 1));
101 }
102 }
103 return 0;
104
105}
106
107int main(int argc, char *argv[])
108{
109 return test_harness(test_cp_abort, "cp_abort");
110}
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
index 4afdebcce4cd..7f348c059bc2 100644
--- a/tools/testing/selftests/powerpc/include/reg.h
+++ b/tools/testing/selftests/powerpc/include/reg.h
@@ -54,6 +54,7 @@
54#define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */ 54#define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */
55#define SPRN_DSCR 0x03 /* Data Stream Control Register */ 55#define SPRN_DSCR 0x03 /* Data Stream Control Register */
56#define SPRN_PPR 896 /* Program Priority Register */ 56#define SPRN_PPR 896 /* Program Priority Register */
57#define SPRN_AMR 13 /* Authority Mask Register - problem state */
57 58
58/* TEXASR register bits */ 59/* TEXASR register bits */
59#define TEXASR_FC 0xFE00000000000000 60#define TEXASR_FC 0xFE00000000000000
diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore
index 349acfafc95b..07ec449a2767 100644
--- a/tools/testing/selftests/powerpc/ptrace/.gitignore
+++ b/tools/testing/selftests/powerpc/ptrace/.gitignore
@@ -8,3 +8,5 @@ ptrace-vsx
8ptrace-tm-vsx 8ptrace-tm-vsx
9ptrace-tm-spd-vsx 9ptrace-tm-spd-vsx
10ptrace-tm-spr 10ptrace-tm-spr
11ptrace-hwbreak
12perf-hwbreak
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index 480305266504..28f5b781a553 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -1,7 +1,8 @@
1# SPDX-License-Identifier: GPL-2.0 1# SPDX-License-Identifier: GPL-2.0
2TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ 2TEST_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 4 ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \
5 perf-hwbreak
5 6
6include ../../lib.mk 7include ../../lib.mk
7 8
@@ -9,6 +10,9 @@ all: $(TEST_PROGS)
9 10
10CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie 11CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
11 12
13ptrace-pkey core-pkey: child.h
14ptrace-pkey core-pkey: LDLIBS += -pthread
15
12$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h 16$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
13 17
14clean: 18clean:
diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h
new file mode 100644
index 000000000000..d7275b7b33dc
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/child.h
@@ -0,0 +1,139 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Helper functions to sync execution between parent and child processes.
4 *
5 * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
6 */
7#include <stdio.h>
8#include <stdbool.h>
9#include <semaphore.h>
10
11/*
12 * Information in a shared memory location for synchronization between child and
13 * parent.
14 */
15struct child_sync {
16 /* The parent waits on this semaphore. */
17 sem_t sem_parent;
18
19 /* If true, the child should give up as well. */
20 bool parent_gave_up;
21
22 /* The child waits on this semaphore. */
23 sem_t sem_child;
24
25 /* If true, the parent should give up as well. */
26 bool child_gave_up;
27};
28
29#define CHILD_FAIL_IF(x, sync) \
30 do { \
31 if (x) { \
32 fprintf(stderr, \
33 "[FAIL] Test FAILED on line %d\n", __LINE__); \
34 (sync)->child_gave_up = true; \
35 prod_parent(sync); \
36 return 1; \
37 } \
38 } while (0)
39
40#define PARENT_FAIL_IF(x, sync) \
41 do { \
42 if (x) { \
43 fprintf(stderr, \
44 "[FAIL] Test FAILED on line %d\n", __LINE__); \
45 (sync)->parent_gave_up = true; \
46 prod_child(sync); \
47 return 1; \
48 } \
49 } while (0)
50
51#define PARENT_SKIP_IF_UNSUPPORTED(x, sync) \
52 do { \
53 if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \
54 (sync)->parent_gave_up = true; \
55 prod_child(sync); \
56 SKIP_IF(1); \
57 } \
58 } while (0)
59
60int init_child_sync(struct child_sync *sync)
61{
62 int ret;
63
64 ret = sem_init(&sync->sem_parent, 1, 0);
65 if (ret) {
66 perror("Semaphore initialization failed");
67 return 1;
68 }
69
70 ret = sem_init(&sync->sem_child, 1, 0);
71 if (ret) {
72 perror("Semaphore initialization failed");
73 return 1;
74 }
75
76 return 0;
77}
78
79void destroy_child_sync(struct child_sync *sync)
80{
81 sem_destroy(&sync->sem_parent);
82 sem_destroy(&sync->sem_child);
83}
84
85int wait_child(struct child_sync *sync)
86{
87 int ret;
88
89 /* Wait until the child prods us. */
90 ret = sem_wait(&sync->sem_parent);
91 if (ret) {
92 perror("Error waiting for child");
93 return 1;
94 }
95
96 return sync->child_gave_up;
97}
98
99int prod_child(struct child_sync *sync)
100{
101 int ret;
102
103 /* Unblock the child now. */
104 ret = sem_post(&sync->sem_child);
105 if (ret) {
106 perror("Error prodding child");
107 return 1;
108 }
109
110 return 0;
111}
112
113int wait_parent(struct child_sync *sync)
114{
115 int ret;
116
117 /* Wait until the parent prods us. */
118 ret = sem_wait(&sync->sem_child);
119 if (ret) {
120 perror("Error waiting for parent");
121 return 1;
122 }
123
124 return sync->parent_gave_up;
125}
126
127int prod_parent(struct child_sync *sync)
128{
129 int ret;
130
131 /* Unblock the parent now. */
132 ret = sem_post(&sync->sem_parent);
133 if (ret) {
134 perror("Error prodding parent");
135 return 1;
136 }
137
138 return 0;
139}
diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
new file mode 100644
index 000000000000..36bc312b1f5c
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
@@ -0,0 +1,461 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Ptrace test for Memory Protection Key registers
4 *
5 * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
6 * Copyright (C) 2018 IBM Corporation.
7 */
8#include <limits.h>
9#include <linux/kernel.h>
10#include <sys/mman.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#include <sys/time.h>
14#include <sys/resource.h>
15#include <fcntl.h>
16#include <unistd.h>
17#include "ptrace.h"
18#include "child.h"
19
20#ifndef __NR_pkey_alloc
21#define __NR_pkey_alloc 384
22#endif
23
24#ifndef __NR_pkey_free
25#define __NR_pkey_free 385
26#endif
27
28#ifndef NT_PPC_PKEY
29#define NT_PPC_PKEY 0x110
30#endif
31
32#ifndef PKEY_DISABLE_EXECUTE
33#define PKEY_DISABLE_EXECUTE 0x4
34#endif
35
36#define AMR_BITS_PER_PKEY 2
37#define PKEY_REG_BITS (sizeof(u64) * 8)
38#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
39
40#define CORE_FILE_LIMIT (5 * 1024 * 1024) /* 5 MB should be enough */
41
42static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
43
44static const char user_write[] = "[User Write (Running)]";
45static const char core_read_running[] = "[Core Read (Running)]";
46
47/* Information shared between the parent and the child. */
48struct shared_info {
49 struct child_sync child_sync;
50
51 /* AMR value the parent expects to read in the core file. */
52 unsigned long amr;
53
54 /* IAMR value the parent expects to read in the core file. */
55 unsigned long iamr;
56
57 /* UAMOR value the parent expects to read in the core file. */
58 unsigned long uamor;
59
60 /* When the child crashed. */
61 time_t core_time;
62};
63
64static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
65{
66 return syscall(__NR_pkey_alloc, flags, init_access_rights);
67}
68
69static int sys_pkey_free(int pkey)
70{
71 return syscall(__NR_pkey_free, pkey);
72}
73
74static int increase_core_file_limit(void)
75{
76 struct rlimit rlim;
77 int ret;
78
79 ret = getrlimit(RLIMIT_CORE, &rlim);
80 FAIL_IF(ret);
81
82 if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
83 rlim.rlim_cur = CORE_FILE_LIMIT;
84
85 if (rlim.rlim_max != RLIM_INFINITY &&
86 rlim.rlim_max < CORE_FILE_LIMIT)
87 rlim.rlim_max = CORE_FILE_LIMIT;
88
89 ret = setrlimit(RLIMIT_CORE, &rlim);
90 FAIL_IF(ret);
91 }
92
93 ret = getrlimit(RLIMIT_FSIZE, &rlim);
94 FAIL_IF(ret);
95
96 if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
97 rlim.rlim_cur = CORE_FILE_LIMIT;
98
99 if (rlim.rlim_max != RLIM_INFINITY &&
100 rlim.rlim_max < CORE_FILE_LIMIT)
101 rlim.rlim_max = CORE_FILE_LIMIT;
102
103 ret = setrlimit(RLIMIT_FSIZE, &rlim);
104 FAIL_IF(ret);
105 }
106
107 return TEST_PASS;
108}
109
110static int child(struct shared_info *info)
111{
112 bool disable_execute = true;
113 int pkey1, pkey2, pkey3;
114 int *ptr, ret;
115
116 /* Wait until parent fills out the initial register values. */
117 ret = wait_parent(&info->child_sync);
118 if (ret)
119 return ret;
120
121 ret = increase_core_file_limit();
122 FAIL_IF(ret);
123
124 /* Get some pkeys so that we can change their bits in the AMR. */
125 pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
126 if (pkey1 < 0) {
127 pkey1 = sys_pkey_alloc(0, 0);
128 FAIL_IF(pkey1 < 0);
129
130 disable_execute = false;
131 }
132
133 pkey2 = sys_pkey_alloc(0, 0);
134 FAIL_IF(pkey2 < 0);
135
136 pkey3 = sys_pkey_alloc(0, 0);
137 FAIL_IF(pkey3 < 0);
138
139 info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
140
141 if (disable_execute)
142 info->iamr |= 1ul << pkeyshift(pkey1);
143
144 info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
145
146 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
147 user_write, info->amr, pkey1, pkey2, pkey3);
148
149 mtspr(SPRN_AMR, info->amr);
150
151 /*
152 * We won't use pkey3. This tests whether the kernel restores the UAMOR
153 * permissions after a key is freed.
154 */
155 sys_pkey_free(pkey3);
156
157 info->core_time = time(NULL);
158
159 /* Crash. */
160 ptr = 0;
161 *ptr = 1;
162
163 /* Shouldn't get here. */
164 FAIL_IF(true);
165
166 return TEST_FAIL;
167}
168
169/* Return file size if filename exists and pass sanity check, or zero if not. */
170static off_t try_core_file(const char *filename, struct shared_info *info,
171 pid_t pid)
172{
173 struct stat buf;
174 int ret;
175
176 ret = stat(filename, &buf);
177 if (ret == -1)
178 return TEST_FAIL;
179
180 /* Make sure we're not using a stale core file. */
181 return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
182}
183
184static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
185{
186 return (void *) nhdr + sizeof(*nhdr) +
187 __ALIGN_KERNEL(nhdr->n_namesz, 4) +
188 __ALIGN_KERNEL(nhdr->n_descsz, 4);
189}
190
191static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
192 off_t core_size)
193{
194 unsigned long *regs;
195 Elf64_Phdr *phdr;
196 Elf64_Nhdr *nhdr;
197 size_t phdr_size;
198 void *p = ehdr, *note;
199 int ret;
200
201 ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
202 FAIL_IF(ret);
203
204 FAIL_IF(ehdr->e_type != ET_CORE);
205 FAIL_IF(ehdr->e_machine != EM_PPC64);
206 FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
207
208 /*
209 * e_phnum is at most 65535 so calculating the size of the
210 * program header cannot overflow.
211 */
212 phdr_size = sizeof(*phdr) * ehdr->e_phnum;
213
214 /* Sanity check the program header table location. */
215 FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
216 FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
217
218 /* Find the PT_NOTE segment. */
219 for (phdr = p + ehdr->e_phoff;
220 (void *) phdr < p + ehdr->e_phoff + phdr_size;
221 phdr += ehdr->e_phentsize)
222 if (phdr->p_type == PT_NOTE)
223 break;
224
225 FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
226
227 /* Find the NT_PPC_PKEY note. */
228 for (nhdr = p + phdr->p_offset;
229 (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
230 nhdr = next_note(nhdr))
231 if (nhdr->n_type == NT_PPC_PKEY)
232 break;
233
234 FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
235 FAIL_IF(nhdr->n_descsz == 0);
236
237 p = nhdr;
238 note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
239
240 regs = (unsigned long *) note;
241
242 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
243 core_read_running, regs[0], regs[1], regs[2]);
244
245 FAIL_IF(regs[0] != info->amr);
246 FAIL_IF(regs[1] != info->iamr);
247 FAIL_IF(regs[2] != info->uamor);
248
249 return TEST_PASS;
250}
251
252static int parent(struct shared_info *info, pid_t pid)
253{
254 char *filenames, *filename[3];
255 int fd, i, ret, status;
256 unsigned long regs[3];
257 off_t core_size;
258 void *core;
259
260 /*
261 * Get the initial values for AMR, IAMR and UAMOR and communicate them
262 * to the child.
263 */
264 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
265 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
266 PARENT_FAIL_IF(ret, &info->child_sync);
267
268 info->amr = regs[0];
269 info->iamr = regs[1];
270 info->uamor = regs[2];
271
272 /* Wake up child so that it can set itself up. */
273 ret = prod_child(&info->child_sync);
274 PARENT_FAIL_IF(ret, &info->child_sync);
275
276 ret = wait(&status);
277 if (ret != pid) {
278 printf("Child's exit status not captured\n");
279 return TEST_FAIL;
280 } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
281 printf("Child didn't dump core\n");
282 return TEST_FAIL;
283 }
284
285 /* Construct array of core file names to try. */
286
287 filename[0] = filenames = malloc(PATH_MAX);
288 if (!filenames) {
289 perror("Error allocating memory");
290 return TEST_FAIL;
291 }
292
293 ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
294 if (ret < 0 || ret >= PATH_MAX) {
295 ret = TEST_FAIL;
296 goto out;
297 }
298
299 filename[1] = filename[0] + ret + 1;
300 ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
301 if (ret < 0 || ret >= PATH_MAX - ret - 1) {
302 ret = TEST_FAIL;
303 goto out;
304 }
305 filename[2] = "core";
306
307 for (i = 0; i < 3; i++) {
308 core_size = try_core_file(filename[i], info, pid);
309 if (core_size != TEST_FAIL)
310 break;
311 }
312
313 if (i == 3) {
314 printf("Couldn't find core file\n");
315 ret = TEST_FAIL;
316 goto out;
317 }
318
319 fd = open(filename[i], O_RDONLY);
320 if (fd == -1) {
321 perror("Error opening core file");
322 ret = TEST_FAIL;
323 goto out;
324 }
325
326 core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
327 if (core == (void *) -1) {
328 perror("Error mmaping core file");
329 ret = TEST_FAIL;
330 goto out;
331 }
332
333 ret = check_core_file(info, core, core_size);
334
335 munmap(core, core_size);
336 close(fd);
337 unlink(filename[i]);
338
339 out:
340 free(filenames);
341
342 return ret;
343}
344
345static int write_core_pattern(const char *core_pattern)
346{
347 size_t len = strlen(core_pattern), ret;
348 FILE *f;
349
350 f = fopen(core_pattern_file, "w");
351 if (!f) {
352 perror("Error writing to core_pattern file");
353 return TEST_FAIL;
354 }
355
356 ret = fwrite(core_pattern, 1, len, f);
357 fclose(f);
358 if (ret != len) {
359 perror("Error writing to core_pattern file");
360 return TEST_FAIL;
361 }
362
363 return TEST_PASS;
364}
365
366static int setup_core_pattern(char **core_pattern_, bool *changed_)
367{
368 FILE *f;
369 char *core_pattern;
370 int ret;
371
372 core_pattern = malloc(PATH_MAX);
373 if (!core_pattern) {
374 perror("Error allocating memory");
375 return TEST_FAIL;
376 }
377
378 f = fopen(core_pattern_file, "r");
379 if (!f) {
380 perror("Error opening core_pattern file");
381 ret = TEST_FAIL;
382 goto out;
383 }
384
385 ret = fread(core_pattern, 1, PATH_MAX, f);
386 fclose(f);
387 if (!ret) {
388 perror("Error reading core_pattern file");
389 ret = TEST_FAIL;
390 goto out;
391 }
392
393 /* Check whether we can predict the name of the core file. */
394 if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
395 *changed_ = false;
396 else {
397 ret = write_core_pattern("core-pkey.%p");
398 if (ret)
399 goto out;
400
401 *changed_ = true;
402 }
403
404 *core_pattern_ = core_pattern;
405 ret = TEST_PASS;
406
407 out:
408 if (ret)
409 free(core_pattern);
410
411 return ret;
412}
413
414static int core_pkey(void)
415{
416 char *core_pattern;
417 bool changed_core_pattern;
418 struct shared_info *info;
419 int shm_id;
420 int ret;
421 pid_t pid;
422
423 ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
424 if (ret)
425 return ret;
426
427 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
428 info = shmat(shm_id, NULL, 0);
429
430 ret = init_child_sync(&info->child_sync);
431 if (ret)
432 return ret;
433
434 pid = fork();
435 if (pid < 0) {
436 perror("fork() failed");
437 ret = TEST_FAIL;
438 } else if (pid == 0)
439 ret = child(info);
440 else
441 ret = parent(info, pid);
442
443 shmdt(info);
444
445 if (pid) {
446 destroy_child_sync(&info->child_sync);
447 shmctl(shm_id, IPC_RMID, NULL);
448
449 if (changed_core_pattern)
450 write_core_pattern(core_pattern);
451 }
452
453 free(core_pattern);
454
455 return ret;
456}
457
458int main(int argc, char *argv[])
459{
460 return test_harness(core_pkey, "core_pkey");
461}
diff --git a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
new file mode 100644
index 000000000000..60df0b5e628a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
@@ -0,0 +1,195 @@
1/*
2 * perf events self profiling example test case for hw breakpoints.
3 *
4 * This tests perf PERF_TYPE_BREAKPOINT parameters
5 * 1) tests all variants of the break on read/write flags
6 * 2) tests exclude_user == 0 and 1
7 * 3) test array matches (if DAWR is supported))
8 * 4) test different numbers of breakpoints matches
9 *
10 * Configure this breakpoint, then read and write the data a number of
11 * times. Then check the output count from perf is as expected.
12 *
13 * Based on:
14 * http://ozlabs.org/~anton/junkcode/perf_events_example1.c
15 *
16 * Copyright (C) 2018 Michael Neuling, IBM Corporation.
17 *
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version
21 * 2 of the License, or (at your option) any later version.
22 */
23
24#include <unistd.h>
25#include <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/ioctl.h>
30#include <elf.h>
31#include <pthread.h>
32#include <sys/syscall.h>
33#include <linux/perf_event.h>
34#include <linux/hw_breakpoint.h>
35#include "utils.h"
36
37#define MAX_LOOPS 10000
38
39#define DAWR_LENGTH_MAX ((0x3f + 1) * 8)
40
41static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid,
42 int cpu, int group_fd,
43 unsigned long flags)
44{
45 attr->size = sizeof(*attr);
46 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
47}
48
49static inline bool breakpoint_test(int len)
50{
51 struct perf_event_attr attr;
52 int fd;
53
54 /* setup counters */
55 memset(&attr, 0, sizeof(attr));
56 attr.disabled = 1;
57 attr.type = PERF_TYPE_BREAKPOINT;
58 attr.bp_type = HW_BREAKPOINT_R;
59 /* bp_addr can point anywhere but needs to be aligned */
60 attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800;
61 attr.bp_len = len;
62 fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
63 if (fd < 0)
64 return false;
65 close(fd);
66 return true;
67}
68
69static inline bool perf_breakpoint_supported(void)
70{
71 return breakpoint_test(4);
72}
73
74static inline bool dawr_supported(void)
75{
76 return breakpoint_test(DAWR_LENGTH_MAX);
77}
78
79static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
80{
81 int i,j;
82 struct perf_event_attr attr;
83 size_t res;
84 unsigned long long breaks, needed;
85 int readint;
86 int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)];
87 int *readintalign;
88 volatile int *ptr;
89 int break_fd;
90 int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */
91 volatile int *k;
92
93 /* align to 0x400 boundary as required by DAWR */
94 readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) &
95 0xfffffffffffff800);
96
97 ptr = &readint;
98 if (arraytest)
99 ptr = &readintalign[0];
100
101 /* setup counters */
102 memset(&attr, 0, sizeof(attr));
103 attr.disabled = 1;
104 attr.type = PERF_TYPE_BREAKPOINT;
105 attr.bp_type = readwriteflag;
106 attr.bp_addr = (__u64)ptr;
107 attr.bp_len = sizeof(int);
108 if (arraytest)
109 attr.bp_len = DAWR_LENGTH_MAX;
110 attr.exclude_user = exclude_user;
111 break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
112 if (break_fd < 0) {
113 perror("sys_perf_event_open");
114 exit(1);
115 }
116
117 /* start counters */
118 ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
119
120 /* Test a bunch of reads and writes */
121 k = &readint;
122 for (i = 0; i < loop_num; i++) {
123 if (arraytest)
124 k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]);
125
126 j = *k;
127 *k = j;
128 }
129
130 /* stop counters */
131 ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
132
133 /* read and check counters */
134 res = read(break_fd, &breaks, sizeof(unsigned long long));
135 assert(res == sizeof(unsigned long long));
136 /* we read and write each loop, so subtract the ones we are counting */
137 needed = 0;
138 if (readwriteflag & HW_BREAKPOINT_R)
139 needed += loop_num;
140 if (readwriteflag & HW_BREAKPOINT_W)
141 needed += loop_num;
142 needed = needed * (1 - exclude_user);
143 printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n",
144 (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest);
145 if (breaks != needed) {
146 printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n",
147 (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user);
148 return 1;
149 }
150 close(break_fd);
151
152 return 0;
153}
154
155static int runtest(void)
156{
157 int rwflag;
158 int exclude_user;
159 int ret;
160
161 /*
162 * perf defines rwflag as two bits read and write and at least
163 * one must be set. So range 1-3.
164 */
165 for (rwflag = 1 ; rwflag < 4; rwflag++) {
166 for (exclude_user = 0 ; exclude_user < 2; exclude_user++) {
167 ret = runtestsingle(rwflag, exclude_user, 0);
168 if (ret)
169 return ret;
170
171 /* if we have the dawr, we can do an array test */
172 if (!dawr_supported())
173 continue;
174 ret = runtestsingle(rwflag, exclude_user, 1);
175 if (ret)
176 return ret;
177 }
178 }
179 return 0;
180}
181
182
183static int perf_hwbreak(void)
184{
185 srand ( time(NULL) );
186
187 SKIP_IF(!perf_breakpoint_supported());
188
189 return runtest();
190}
191
192int main(int argc, char *argv[], char **envp)
193{
194 return test_harness(perf_hwbreak, "perf_hwbreak");
195}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c
new file mode 100644
index 000000000000..3066d310f32b
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c
@@ -0,0 +1,342 @@
1// SPDX-License-Identifier: GPL-2.0+
2
3/*
4 * Ptrace test for hw breakpoints
5 *
6 * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
7 *
8 * This test forks and the parent then traces the child doing various
9 * types of ptrace enabled breakpoints
10 *
11 * Copyright (C) 2018 Michael Neuling, IBM Corporation.
12 */
13
14#include <sys/ptrace.h>
15#include <unistd.h>
16#include <stddef.h>
17#include <sys/user.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <signal.h>
21#include <sys/types.h>
22#include <sys/wait.h>
23#include "ptrace.h"
24
25/* Breakpoint access modes */
26enum {
27 BP_X = 1,
28 BP_RW = 2,
29 BP_W = 4,
30};
31
32static pid_t child_pid;
33static struct ppc_debug_info dbginfo;
34
35static void get_dbginfo(void)
36{
37 int ret;
38
39 ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
40 if (ret) {
41 perror("Can't get breakpoint info\n");
42 exit(-1);
43 }
44}
45
46static bool hwbreak_present(void)
47{
48 return (dbginfo.num_data_bps != 0);
49}
50
51static bool dawr_present(void)
52{
53 return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
54}
55
56static void set_breakpoint_addr(void *addr)
57{
58 int ret;
59
60 ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr);
61 if (ret) {
62 perror("Can't set breakpoint addr\n");
63 exit(-1);
64 }
65}
66
67static int set_hwbreakpoint_addr(void *addr, int range)
68{
69 int ret;
70
71 struct ppc_hw_breakpoint info;
72
73 info.version = 1;
74 info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW;
75 info.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
76 if (range > 0)
77 info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
78 info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
79 info.addr = (__u64)addr;
80 info.addr2 = (__u64)addr + range;
81 info.condition_value = 0;
82
83 ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
84 if (ret < 0) {
85 perror("Can't set breakpoint\n");
86 exit(-1);
87 }
88 return ret;
89}
90
91static int del_hwbreakpoint_addr(int watchpoint_handle)
92{
93 int ret;
94
95 ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle);
96 if (ret < 0) {
97 perror("Can't delete hw breakpoint\n");
98 exit(-1);
99 }
100 return ret;
101}
102
103#define DAWR_LENGTH_MAX 512
104
105/* Dummy variables to test read/write accesses */
106static unsigned long long
107 dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
108 __attribute__((aligned(512)));
109static unsigned long long *dummy_var = dummy_array;
110
111static void write_var(int len)
112{
113 long long *plval;
114 char *pcval;
115 short *psval;
116 int *pival;
117
118 switch (len) {
119 case 1:
120 pcval = (char *)dummy_var;
121 *pcval = 0xff;
122 break;
123 case 2:
124 psval = (short *)dummy_var;
125 *psval = 0xffff;
126 break;
127 case 4:
128 pival = (int *)dummy_var;
129 *pival = 0xffffffff;
130 break;
131 case 8:
132 plval = (long long *)dummy_var;
133 *plval = 0xffffffffffffffffLL;
134 break;
135 }
136}
137
138static void read_var(int len)
139{
140 char cval __attribute__((unused));
141 short sval __attribute__((unused));
142 int ival __attribute__((unused));
143 long long lval __attribute__((unused));
144
145 switch (len) {
146 case 1:
147 cval = *(char *)dummy_var;
148 break;
149 case 2:
150 sval = *(short *)dummy_var;
151 break;
152 case 4:
153 ival = *(int *)dummy_var;
154 break;
155 case 8:
156 lval = *(long long *)dummy_var;
157 break;
158 }
159}
160
161/*
162 * Do the r/w accesses to trigger the breakpoints. And run
163 * the usual traps.
164 */
165static void trigger_tests(void)
166{
167 int len, ret;
168
169 ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
170 if (ret) {
171 perror("Can't be traced?\n");
172 return;
173 }
174
175 /* Wake up father so that it sets up the first test */
176 kill(getpid(), SIGUSR1);
177
178 /* Test write watchpoints */
179 for (len = 1; len <= sizeof(long); len <<= 1)
180 write_var(len);
181
182 /* Test read/write watchpoints (on read accesses) */
183 for (len = 1; len <= sizeof(long); len <<= 1)
184 read_var(len);
185
186 /* Test when breakpoint is unset */
187
188 /* Test write watchpoints */
189 for (len = 1; len <= sizeof(long); len <<= 1)
190 write_var(len);
191
192 /* Test read/write watchpoints (on read accesses) */
193 for (len = 1; len <= sizeof(long); len <<= 1)
194 read_var(len);
195}
196
197static void check_success(const char *msg)
198{
199 const char *msg2;
200 int status;
201
202 /* Wait for the child to SIGTRAP */
203 wait(&status);
204
205 msg2 = "Failed";
206
207 if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
208 msg2 = "Child process hit the breakpoint";
209 }
210
211 printf("%s Result: [%s]\n", msg, msg2);
212}
213
214static void launch_watchpoints(char *buf, int mode, int len,
215 struct ppc_debug_info *dbginfo, bool dawr)
216{
217 const char *mode_str;
218 unsigned long data = (unsigned long)(dummy_var);
219 int wh, range;
220
221 data &= ~0x7UL;
222
223 if (mode == BP_W) {
224 data |= (1UL << 1);
225 mode_str = "write";
226 } else {
227 data |= (1UL << 0);
228 data |= (1UL << 1);
229 mode_str = "read";
230 }
231
232 /* Set DABR_TRANSLATION bit */
233 data |= (1UL << 2);
234
235 /* use PTRACE_SET_DEBUGREG breakpoints */
236 set_breakpoint_addr((void *)data);
237 ptrace(PTRACE_CONT, child_pid, NULL, 0);
238 sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
239 check_success(buf);
240 /* Unregister hw brkpoint */
241 set_breakpoint_addr(NULL);
242
243 data = (data & ~7); /* remove dabr control bits */
244
245 /* use PPC_PTRACE_SETHWDEBUG breakpoint */
246 if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
247 return; /* not supported */
248 wh = set_hwbreakpoint_addr((void *)data, 0);
249 ptrace(PTRACE_CONT, child_pid, NULL, 0);
250 sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
251 check_success(buf);
252 /* Unregister hw brkpoint */
253 del_hwbreakpoint_addr(wh);
254
255 /* try a wider range */
256 range = 8;
257 if (dawr)
258 range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1));
259 wh = set_hwbreakpoint_addr((void *)data, range);
260 ptrace(PTRACE_CONT, child_pid, NULL, 0);
261 sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
262 check_success(buf);
263 /* Unregister hw brkpoint */
264 del_hwbreakpoint_addr(wh);
265}
266
267/* Set the breakpoints and check the child successfully trigger them */
268static int launch_tests(bool dawr)
269{
270 char buf[1024];
271 int len, i, status;
272
273 struct ppc_debug_info dbginfo;
274
275 i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
276 if (i) {
277 perror("Can't set breakpoint info\n");
278 exit(-1);
279 }
280 if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
281 printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n");
282
283 /* Write watchpoint */
284 for (len = 1; len <= sizeof(long); len <<= 1)
285 launch_watchpoints(buf, BP_W, len, &dbginfo, dawr);
286
287 /* Read-Write watchpoint */
288 for (len = 1; len <= sizeof(long); len <<= 1)
289 launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr);
290
291 ptrace(PTRACE_CONT, child_pid, NULL, 0);
292
293 /*
294 * Now we have unregistered the breakpoint, access by child
295 * should not cause SIGTRAP.
296 */
297
298 wait(&status);
299
300 if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
301 printf("FAIL: Child process hit the breakpoint, which is not expected\n");
302 ptrace(PTRACE_CONT, child_pid, NULL, 0);
303 return TEST_FAIL;
304 }
305
306 if (WIFEXITED(status))
307 printf("Child exited normally\n");
308
309 return TEST_PASS;
310}
311
312static int ptrace_hwbreak(void)
313{
314 pid_t pid;
315 int ret;
316 bool dawr;
317
318 pid = fork();
319 if (!pid) {
320 trigger_tests();
321 return 0;
322 }
323
324 wait(NULL);
325
326 child_pid = pid;
327
328 get_dbginfo();
329 SKIP_IF(!hwbreak_present());
330 dawr = dawr_present();
331
332 ret = launch_tests(dawr);
333
334 wait(NULL);
335
336 return ret;
337}
338
339int main(int argc, char **argv, char **envp)
340{
341 return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
342}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
new file mode 100644
index 000000000000..5cf631f792cc
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
@@ -0,0 +1,327 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Ptrace test for Memory Protection Key registers
4 *
5 * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
6 * Copyright (C) 2018 IBM Corporation.
7 */
8#include "ptrace.h"
9#include "child.h"
10
11#ifndef __NR_pkey_alloc
12#define __NR_pkey_alloc 384
13#endif
14
15#ifndef __NR_pkey_free
16#define __NR_pkey_free 385
17#endif
18
19#ifndef NT_PPC_PKEY
20#define NT_PPC_PKEY 0x110
21#endif
22
23#ifndef PKEY_DISABLE_EXECUTE
24#define PKEY_DISABLE_EXECUTE 0x4
25#endif
26
27#define AMR_BITS_PER_PKEY 2
28#define PKEY_REG_BITS (sizeof(u64) * 8)
29#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
30
31static const char user_read[] = "[User Read (Running)]";
32static const char user_write[] = "[User Write (Running)]";
33static const char ptrace_read_running[] = "[Ptrace Read (Running)]";
34static const char ptrace_write_running[] = "[Ptrace Write (Running)]";
35
36/* Information shared between the parent and the child. */
37struct shared_info {
38 struct child_sync child_sync;
39
40 /* AMR value the parent expects to read from the child. */
41 unsigned long amr1;
42
43 /* AMR value the parent is expected to write to the child. */
44 unsigned long amr2;
45
46 /* AMR value that ptrace should refuse to write to the child. */
47 unsigned long amr3;
48
49 /* IAMR value the parent expects to read from the child. */
50 unsigned long expected_iamr;
51
52 /* UAMOR value the parent expects to read from the child. */
53 unsigned long expected_uamor;
54
55 /*
56 * IAMR and UAMOR values that ptrace should refuse to write to the child
57 * (even though they're valid ones) because userspace doesn't have
58 * access to those registers.
59 */
60 unsigned long new_iamr;
61 unsigned long new_uamor;
62};
63
64static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
65{
66 return syscall(__NR_pkey_alloc, flags, init_access_rights);
67}
68
69static int sys_pkey_free(int pkey)
70{
71 return syscall(__NR_pkey_free, pkey);
72}
73
74static int child(struct shared_info *info)
75{
76 unsigned long reg;
77 bool disable_execute = true;
78 int pkey1, pkey2, pkey3;
79 int ret;
80
81 /* Wait until parent fills out the initial register values. */
82 ret = wait_parent(&info->child_sync);
83 if (ret)
84 return ret;
85
86 /* Get some pkeys so that we can change their bits in the AMR. */
87 pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
88 if (pkey1 < 0) {
89 pkey1 = sys_pkey_alloc(0, 0);
90 CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
91
92 disable_execute = false;
93 }
94
95 pkey2 = sys_pkey_alloc(0, 0);
96 CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
97
98 pkey3 = sys_pkey_alloc(0, 0);
99 CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);
100
101 info->amr1 |= 3ul << pkeyshift(pkey1);
102 info->amr2 |= 3ul << pkeyshift(pkey2);
103 info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3);
104
105 if (disable_execute)
106 info->expected_iamr |= 1ul << pkeyshift(pkey1);
107
108 info->expected_uamor |= 3ul << pkeyshift(pkey1) |
109 3ul << pkeyshift(pkey2);
110 info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2);
111 info->new_uamor |= 3ul << pkeyshift(pkey1);
112
113 /*
114 * We won't use pkey3. We just want a plausible but invalid key to test
115 * whether ptrace will let us write to AMR bits we are not supposed to.
116 *
117 * This also tests whether the kernel restores the UAMOR permissions
118 * after a key is freed.
119 */
120 sys_pkey_free(pkey3);
121
122 printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
123 user_write, info->amr1, pkey1, pkey2, pkey3);
124
125 mtspr(SPRN_AMR, info->amr1);
126
127 /* Wait for parent to read our AMR value and write a new one. */
128 ret = prod_parent(&info->child_sync);
129 CHILD_FAIL_IF(ret, &info->child_sync);
130
131 ret = wait_parent(&info->child_sync);
132 if (ret)
133 return ret;
134
135 reg = mfspr(SPRN_AMR);
136
137 printf("%-30s AMR: %016lx\n", user_read, reg);
138
139 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
140
141 /*
142 * Wait for parent to try to write an invalid AMR value.
143 */
144 ret = prod_parent(&info->child_sync);
145 CHILD_FAIL_IF(ret, &info->child_sync);
146
147 ret = wait_parent(&info->child_sync);
148 if (ret)
149 return ret;
150
151 reg = mfspr(SPRN_AMR);
152
153 printf("%-30s AMR: %016lx\n", user_read, reg);
154
155 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
156
157 /*
158 * Wait for parent to try to write an IAMR and a UAMOR value. We can't
159 * verify them, but we can verify that the AMR didn't change.
160 */
161 ret = prod_parent(&info->child_sync);
162 CHILD_FAIL_IF(ret, &info->child_sync);
163
164 ret = wait_parent(&info->child_sync);
165 if (ret)
166 return ret;
167
168 reg = mfspr(SPRN_AMR);
169
170 printf("%-30s AMR: %016lx\n", user_read, reg);
171
172 CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
173
174 /* Now let parent now that we are finished. */
175
176 ret = prod_parent(&info->child_sync);
177 CHILD_FAIL_IF(ret, &info->child_sync);
178
179 return TEST_PASS;
180}
181
182static int parent(struct shared_info *info, pid_t pid)
183{
184 unsigned long regs[3];
185 int ret, status;
186
187 /*
188 * Get the initial values for AMR, IAMR and UAMOR and communicate them
189 * to the child.
190 */
191 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
192 PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
193 PARENT_FAIL_IF(ret, &info->child_sync);
194
195 info->amr1 = info->amr2 = info->amr3 = regs[0];
196 info->expected_iamr = info->new_iamr = regs[1];
197 info->expected_uamor = info->new_uamor = regs[2];
198
199 /* Wake up child so that it can set itself up. */
200 ret = prod_child(&info->child_sync);
201 PARENT_FAIL_IF(ret, &info->child_sync);
202
203 ret = wait_child(&info->child_sync);
204 if (ret)
205 return ret;
206
207 /* Verify that we can read the pkey registers from the child. */
208 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
209 PARENT_FAIL_IF(ret, &info->child_sync);
210
211 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
212 ptrace_read_running, regs[0], regs[1], regs[2]);
213
214 PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);
215 PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
216 PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
217
218 /* Write valid AMR value in child. */
219 ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);
220 PARENT_FAIL_IF(ret, &info->child_sync);
221
222 printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);
223
224 /* Wake up child so that it can verify it changed. */
225 ret = prod_child(&info->child_sync);
226 PARENT_FAIL_IF(ret, &info->child_sync);
227
228 ret = wait_child(&info->child_sync);
229 if (ret)
230 return ret;
231
232 /* Write invalid AMR value in child. */
233 ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1);
234 PARENT_FAIL_IF(ret, &info->child_sync);
235
236 printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3);
237
238 /* Wake up child so that it can verify it didn't change. */
239 ret = prod_child(&info->child_sync);
240 PARENT_FAIL_IF(ret, &info->child_sync);
241
242 ret = wait_child(&info->child_sync);
243 if (ret)
244 return ret;
245
246 /* Try to write to IAMR. */
247 regs[0] = info->amr1;
248 regs[1] = info->new_iamr;
249 ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
250 PARENT_FAIL_IF(!ret, &info->child_sync);
251
252 printf("%-30s AMR: %016lx IAMR: %016lx\n",
253 ptrace_write_running, regs[0], regs[1]);
254
255 /* Try to write to IAMR and UAMOR. */
256 regs[2] = info->new_uamor;
257 ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
258 PARENT_FAIL_IF(!ret, &info->child_sync);
259
260 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
261 ptrace_write_running, regs[0], regs[1], regs[2]);
262
263 /* Verify that all registers still have their expected values. */
264 ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
265 PARENT_FAIL_IF(ret, &info->child_sync);
266
267 printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
268 ptrace_read_running, regs[0], regs[1], regs[2]);
269
270 PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);
271 PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
272 PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
273
274 /* Wake up child so that it can verify AMR didn't change and wrap up. */
275 ret = prod_child(&info->child_sync);
276 PARENT_FAIL_IF(ret, &info->child_sync);
277
278 ret = wait(&status);
279 if (ret != pid) {
280 printf("Child's exit status not captured\n");
281 ret = TEST_PASS;
282 } else if (!WIFEXITED(status)) {
283 printf("Child exited abnormally\n");
284 ret = TEST_FAIL;
285 } else
286 ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;
287
288 return ret;
289}
290
291static int ptrace_pkey(void)
292{
293 struct shared_info *info;
294 int shm_id;
295 int ret;
296 pid_t pid;
297
298 shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
299 info = shmat(shm_id, NULL, 0);
300
301 ret = init_child_sync(&info->child_sync);
302 if (ret)
303 return ret;
304
305 pid = fork();
306 if (pid < 0) {
307 perror("fork() failed");
308 ret = TEST_FAIL;
309 } else if (pid == 0)
310 ret = child(info);
311 else
312 ret = parent(info, pid);
313
314 shmdt(info);
315
316 if (pid) {
317 destroy_child_sync(&info->child_sync);
318 shmctl(shm_id, IPC_RMID, NULL);
319 }
320
321 return ret;
322}
323
324int main(int argc, char *argv[])
325{
326 return test_harness(ptrace_pkey, "ptrace_pkey");
327}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
index 19fb825270a1..34201cfa8335 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -102,6 +102,44 @@ int cont_trace(pid_t child)
102 return TEST_PASS; 102 return TEST_PASS;
103} 103}
104 104
105int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
106 int n)
107{
108 struct iovec iov;
109 long ret;
110
111 FAIL_IF(start_trace(child));
112
113 iov.iov_base = regs;
114 iov.iov_len = n * sizeof(unsigned long);
115
116 ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
117 if (ret)
118 return ret;
119
120 FAIL_IF(stop_trace(child));
121
122 return TEST_PASS;
123}
124
125long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[],
126 int n)
127{
128 struct iovec iov;
129 long ret;
130
131 FAIL_IF(start_trace(child));
132
133 iov.iov_base = regs;
134 iov.iov_len = n * sizeof(unsigned long);
135
136 ret = ptrace(PTRACE_SETREGSET, child, type, &iov);
137
138 FAIL_IF(stop_trace(child));
139
140 return ret;
141}
142
105/* TAR, PPR, DSCR */ 143/* TAR, PPR, DSCR */
106int show_tar_registers(pid_t child, unsigned long *out) 144int show_tar_registers(pid_t child, unsigned long *out)
107{ 145{
diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore
index bb90d4b79524..c3ee8393dae8 100644
--- a/tools/testing/selftests/powerpc/tm/.gitignore
+++ b/tools/testing/selftests/powerpc/tm/.gitignore
@@ -14,3 +14,4 @@ tm-signal-context-chk-vsx
14tm-vmx-unavail 14tm-vmx-unavail
15tm-unavailable 15tm-unavailable
16tm-trap 16tm-trap
17tm-sigreturn