summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/powerpc/ptrace
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/powerpc/ptrace')
-rw-r--r--tools/testing/selftests/powerpc/ptrace/.gitignore16
-rw-r--r--tools/testing/selftests/powerpc/ptrace/Makefile15
-rw-r--r--tools/testing/selftests/powerpc/ptrace/child.h139
-rw-r--r--tools/testing/selftests/powerpc/ptrace/core-pkey.c462
-rw-r--r--tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c308
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c119
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h70
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c550
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c330
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c228
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tar.c134
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tar.h46
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c154
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c165
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c170
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c180
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c163
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c156
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c163
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c115
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h123
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace.h745
22 files changed, 4551 insertions, 0 deletions
diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore
new file mode 100644
index 000000000..0e96150b7
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/.gitignore
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+ptrace-gpr
+ptrace-tm-gpr
+ptrace-tm-spd-gpr
+ptrace-tar
+ptrace-tm-tar
+ptrace-tm-spd-tar
+ptrace-vsx
+ptrace-tm-vsx
+ptrace-tm-spd-vsx
+ptrace-tm-spr
+ptrace-hwbreak
+perf-hwbreak
+core-pkey
+ptrace-pkey
+ptrace-syscall
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
new file mode 100644
index 000000000..8d3f006c9
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+TEST_GEN_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
+ ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
+ ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \
+ perf-hwbreak ptrace-syscall
+
+top_srcdir = ../../../../..
+include ../../lib.mk
+
+CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
+
+$(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: child.h
+$(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: LDLIBS += -pthread
+
+$(TEST_GEN_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h
new file mode 100644
index 000000000..d7275b7b3
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/child.h
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Helper functions to sync execution between parent and child processes.
+ *
+ * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
+ */
+#include <stdio.h>
+#include <stdbool.h>
+#include <semaphore.h>
+
+/*
+ * Information in a shared memory location for synchronization between child and
+ * parent.
+ */
+struct child_sync {
+ /* The parent waits on this semaphore. */
+ sem_t sem_parent;
+
+ /* If true, the child should give up as well. */
+ bool parent_gave_up;
+
+ /* The child waits on this semaphore. */
+ sem_t sem_child;
+
+ /* If true, the parent should give up as well. */
+ bool child_gave_up;
+};
+
+#define CHILD_FAIL_IF(x, sync) \
+ do { \
+ if (x) { \
+ fprintf(stderr, \
+ "[FAIL] Test FAILED on line %d\n", __LINE__); \
+ (sync)->child_gave_up = true; \
+ prod_parent(sync); \
+ return 1; \
+ } \
+ } while (0)
+
+#define PARENT_FAIL_IF(x, sync) \
+ do { \
+ if (x) { \
+ fprintf(stderr, \
+ "[FAIL] Test FAILED on line %d\n", __LINE__); \
+ (sync)->parent_gave_up = true; \
+ prod_child(sync); \
+ return 1; \
+ } \
+ } while (0)
+
+#define PARENT_SKIP_IF_UNSUPPORTED(x, sync) \
+ do { \
+ if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \
+ (sync)->parent_gave_up = true; \
+ prod_child(sync); \
+ SKIP_IF(1); \
+ } \
+ } while (0)
+
+int init_child_sync(struct child_sync *sync)
+{
+ int ret;
+
+ ret = sem_init(&sync->sem_parent, 1, 0);
+ if (ret) {
+ perror("Semaphore initialization failed");
+ return 1;
+ }
+
+ ret = sem_init(&sync->sem_child, 1, 0);
+ if (ret) {
+ perror("Semaphore initialization failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+void destroy_child_sync(struct child_sync *sync)
+{
+ sem_destroy(&sync->sem_parent);
+ sem_destroy(&sync->sem_child);
+}
+
+int wait_child(struct child_sync *sync)
+{
+ int ret;
+
+ /* Wait until the child prods us. */
+ ret = sem_wait(&sync->sem_parent);
+ if (ret) {
+ perror("Error waiting for child");
+ return 1;
+ }
+
+ return sync->child_gave_up;
+}
+
+int prod_child(struct child_sync *sync)
+{
+ int ret;
+
+ /* Unblock the child now. */
+ ret = sem_post(&sync->sem_child);
+ if (ret) {
+ perror("Error prodding child");
+ return 1;
+ }
+
+ return 0;
+}
+
+int wait_parent(struct child_sync *sync)
+{
+ int ret;
+
+ /* Wait until the parent prods us. */
+ ret = sem_wait(&sync->sem_child);
+ if (ret) {
+ perror("Error waiting for parent");
+ return 1;
+ }
+
+ return sync->parent_gave_up;
+}
+
+int prod_parent(struct child_sync *sync)
+{
+ int ret;
+
+ /* Unblock the parent now. */
+ ret = sem_post(&sync->sem_parent);
+ if (ret) {
+ perror("Error prodding parent");
+ return 1;
+ }
+
+ return 0;
+}
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 000000000..bbc05ffc5
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ptrace test for Memory Protection Key registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ * Copyright (C) 2018 IBM Corporation.
+ */
+#include <limits.h>
+#include <linux/kernel.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "ptrace.h"
+#include "child.h"
+
+#ifndef __NR_pkey_alloc
+#define __NR_pkey_alloc 384
+#endif
+
+#ifndef __NR_pkey_free
+#define __NR_pkey_free 385
+#endif
+
+#ifndef NT_PPC_PKEY
+#define NT_PPC_PKEY 0x110
+#endif
+
+#ifndef PKEY_DISABLE_EXECUTE
+#define PKEY_DISABLE_EXECUTE 0x4
+#endif
+
+#define AMR_BITS_PER_PKEY 2
+#define PKEY_REG_BITS (sizeof(u64) * 8)
+#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
+
+#define CORE_FILE_LIMIT (5 * 1024 * 1024) /* 5 MB should be enough */
+
+static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
+
+static const char user_write[] = "[User Write (Running)]";
+static const char core_read_running[] = "[Core Read (Running)]";
+
+/* Information shared between the parent and the child. */
+struct shared_info {
+ struct child_sync child_sync;
+
+ /* AMR value the parent expects to read in the core file. */
+ unsigned long amr;
+
+ /* IAMR value the parent expects to read in the core file. */
+ unsigned long iamr;
+
+ /* UAMOR value the parent expects to read in the core file. */
+ unsigned long uamor;
+
+ /* When the child crashed. */
+ time_t core_time;
+};
+
+static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
+{
+ return syscall(__NR_pkey_alloc, flags, init_access_rights);
+}
+
+static int sys_pkey_free(int pkey)
+{
+ return syscall(__NR_pkey_free, pkey);
+}
+
+static int increase_core_file_limit(void)
+{
+ struct rlimit rlim;
+ int ret;
+
+ ret = getrlimit(RLIMIT_CORE, &rlim);
+ FAIL_IF(ret);
+
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
+ rlim.rlim_cur = CORE_FILE_LIMIT;
+
+ if (rlim.rlim_max != RLIM_INFINITY &&
+ rlim.rlim_max < CORE_FILE_LIMIT)
+ rlim.rlim_max = CORE_FILE_LIMIT;
+
+ ret = setrlimit(RLIMIT_CORE, &rlim);
+ FAIL_IF(ret);
+ }
+
+ ret = getrlimit(RLIMIT_FSIZE, &rlim);
+ FAIL_IF(ret);
+
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
+ rlim.rlim_cur = CORE_FILE_LIMIT;
+
+ if (rlim.rlim_max != RLIM_INFINITY &&
+ rlim.rlim_max < CORE_FILE_LIMIT)
+ rlim.rlim_max = CORE_FILE_LIMIT;
+
+ ret = setrlimit(RLIMIT_FSIZE, &rlim);
+ FAIL_IF(ret);
+ }
+
+ return TEST_PASS;
+}
+
+static int child(struct shared_info *info)
+{
+ bool disable_execute = true;
+ int pkey1, pkey2, pkey3;
+ int *ptr, ret;
+
+ /* Wait until parent fills out the initial register values. */
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ ret = increase_core_file_limit();
+ FAIL_IF(ret);
+
+ /* Get some pkeys so that we can change their bits in the AMR. */
+ pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
+ if (pkey1 < 0) {
+ pkey1 = sys_pkey_alloc(0, 0);
+ FAIL_IF(pkey1 < 0);
+
+ disable_execute = false;
+ }
+
+ pkey2 = sys_pkey_alloc(0, 0);
+ FAIL_IF(pkey2 < 0);
+
+ pkey3 = sys_pkey_alloc(0, 0);
+ FAIL_IF(pkey3 < 0);
+
+ info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
+
+ if (disable_execute)
+ info->iamr |= 1ul << pkeyshift(pkey1);
+ else
+ info->iamr &= ~(1ul << pkeyshift(pkey1));
+
+ info->iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3));
+
+ info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
+
+ printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
+ user_write, info->amr, pkey1, pkey2, pkey3);
+
+ set_amr(info->amr);
+
+ /*
+ * We won't use pkey3. This tests whether the kernel restores the UAMOR
+ * permissions after a key is freed.
+ */
+ sys_pkey_free(pkey3);
+
+ info->core_time = time(NULL);
+
+ /* Crash. */
+ ptr = 0;
+ *ptr = 1;
+
+ /* Shouldn't get here. */
+ FAIL_IF(true);
+
+ return TEST_FAIL;
+}
+
+/* Return file size if filename exists and pass sanity check, or zero if not. */
+static off_t try_core_file(const char *filename, struct shared_info *info,
+ pid_t pid)
+{
+ struct stat buf;
+ int ret;
+
+ ret = stat(filename, &buf);
+ if (ret == -1)
+ return TEST_FAIL;
+
+ /* Make sure we're not using a stale core file. */
+ return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
+}
+
+static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
+{
+ return (void *) nhdr + sizeof(*nhdr) +
+ __ALIGN_KERNEL(nhdr->n_namesz, 4) +
+ __ALIGN_KERNEL(nhdr->n_descsz, 4);
+}
+
+static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
+ off_t core_size)
+{
+ unsigned long *regs;
+ Elf64_Phdr *phdr;
+ Elf64_Nhdr *nhdr;
+ size_t phdr_size;
+ void *p = ehdr, *note;
+ int ret;
+
+ ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
+ FAIL_IF(ret);
+
+ FAIL_IF(ehdr->e_type != ET_CORE);
+ FAIL_IF(ehdr->e_machine != EM_PPC64);
+ FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
+
+ /*
+ * e_phnum is at most 65535 so calculating the size of the
+ * program header cannot overflow.
+ */
+ phdr_size = sizeof(*phdr) * ehdr->e_phnum;
+
+ /* Sanity check the program header table location. */
+ FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
+ FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
+
+ /* Find the PT_NOTE segment. */
+ for (phdr = p + ehdr->e_phoff;
+ (void *) phdr < p + ehdr->e_phoff + phdr_size;
+ phdr += ehdr->e_phentsize)
+ if (phdr->p_type == PT_NOTE)
+ break;
+
+ FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
+
+ /* Find the NT_PPC_PKEY note. */
+ for (nhdr = p + phdr->p_offset;
+ (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
+ nhdr = next_note(nhdr))
+ if (nhdr->n_type == NT_PPC_PKEY)
+ break;
+
+ FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
+ FAIL_IF(nhdr->n_descsz == 0);
+
+ p = nhdr;
+ note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
+
+ regs = (unsigned long *) note;
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ core_read_running, regs[0], regs[1], regs[2]);
+
+ FAIL_IF(regs[0] != info->amr);
+ FAIL_IF(regs[1] != info->iamr);
+ FAIL_IF(regs[2] != info->uamor);
+
+ return TEST_PASS;
+}
+
+static int parent(struct shared_info *info, pid_t pid)
+{
+ char *filenames, *filename[3];
+ int fd, i, ret, status;
+ unsigned long regs[3];
+ off_t core_size;
+ void *core;
+
+ /*
+ * Get the initial values for AMR, IAMR and UAMOR and communicate them
+ * to the child.
+ */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ info->amr = regs[0];
+ info->iamr = regs[1];
+ info->uamor = regs[2];
+
+ /* Wake up child so that it can set itself up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait(&status);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
+ printf("Child didn't dump core\n");
+ return TEST_FAIL;
+ }
+
+ /* Construct array of core file names to try. */
+
+ filename[0] = filenames = malloc(PATH_MAX);
+ if (!filenames) {
+ perror("Error allocating memory");
+ return TEST_FAIL;
+ }
+
+ ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
+ if (ret < 0 || ret >= PATH_MAX) {
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ filename[1] = filename[0] + ret + 1;
+ ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
+ if (ret < 0 || ret >= PATH_MAX - ret - 1) {
+ ret = TEST_FAIL;
+ goto out;
+ }
+ filename[2] = "core";
+
+ for (i = 0; i < 3; i++) {
+ core_size = try_core_file(filename[i], info, pid);
+ if (core_size != TEST_FAIL)
+ break;
+ }
+
+ if (i == 3) {
+ printf("Couldn't find core file\n");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ fd = open(filename[i], O_RDONLY);
+ if (fd == -1) {
+ perror("Error opening core file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (core == (void *) -1) {
+ perror("Error mmaping core file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ ret = check_core_file(info, core, core_size);
+
+ munmap(core, core_size);
+ close(fd);
+ unlink(filename[i]);
+
+ out:
+ free(filenames);
+
+ return ret;
+}
+
+static int write_core_pattern(const char *core_pattern)
+{
+ size_t len = strlen(core_pattern), ret;
+ FILE *f;
+
+ f = fopen(core_pattern_file, "w");
+ SKIP_IF_MSG(!f, "Try with root privileges");
+
+ ret = fwrite(core_pattern, 1, len, f);
+ fclose(f);
+ if (ret != len) {
+ perror("Error writing to core_pattern file");
+ return TEST_FAIL;
+ }
+
+ return TEST_PASS;
+}
+
+static int setup_core_pattern(char **core_pattern_, bool *changed_)
+{
+ FILE *f;
+ char *core_pattern;
+ int ret;
+
+ core_pattern = malloc(PATH_MAX);
+ if (!core_pattern) {
+ perror("Error allocating memory");
+ return TEST_FAIL;
+ }
+
+ f = fopen(core_pattern_file, "r");
+ if (!f) {
+ perror("Error opening core_pattern file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ ret = fread(core_pattern, 1, PATH_MAX, f);
+ fclose(f);
+ if (!ret) {
+ perror("Error reading core_pattern file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ /* Check whether we can predict the name of the core file. */
+ if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
+ *changed_ = false;
+ else {
+ ret = write_core_pattern("core-pkey.%p");
+ if (ret)
+ goto out;
+
+ *changed_ = true;
+ }
+
+ *core_pattern_ = core_pattern;
+ ret = TEST_PASS;
+
+ out:
+ if (ret)
+ free(core_pattern);
+
+ return ret;
+}
+
+static int core_pkey(void)
+{
+ char *core_pattern;
+ bool changed_core_pattern;
+ struct shared_info *info;
+ int shm_id;
+ int ret;
+ pid_t pid;
+
+ ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
+ if (ret)
+ return ret;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
+ info = shmat(shm_id, NULL, 0);
+
+ ret = init_child_sync(&info->child_sync);
+ if (ret)
+ return ret;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ ret = TEST_FAIL;
+ } else if (pid == 0)
+ ret = child(info);
+ else
+ ret = parent(info, pid);
+
+ shmdt(info);
+
+ if (pid) {
+ destroy_child_sync(&info->child_sync);
+ shmctl(shm_id, IPC_RMID, NULL);
+
+ if (changed_core_pattern)
+ write_core_pattern(core_pattern);
+ }
+
+ free(core_pattern);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(core_pkey, "core_pkey");
+}
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 000000000..c1f324afd
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * perf events self profiling example test case for hw breakpoints.
+ *
+ * This tests perf PERF_TYPE_BREAKPOINT parameters
+ * 1) tests all variants of the break on read/write flags
+ * 2) tests exclude_user == 0 and 1
+ * 3) test array matches (if DAWR is supported))
+ * 4) test different numbers of breakpoints matches
+ *
+ * Configure this breakpoint, then read and write the data a number of
+ * times. Then check the output count from perf is as expected.
+ *
+ * Based on:
+ * http://ozlabs.org/~anton/junkcode/perf_events_example1.c
+ *
+ * Copyright (C) 2018 Michael Neuling, IBM Corporation.
+ */
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <elf.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <linux/perf_event.h>
+#include <linux/hw_breakpoint.h>
+#include "utils.h"
+
+#define MAX_LOOPS 10000
+
+#define DAWR_LENGTH_MAX ((0x3f + 1) * 8)
+
+static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid,
+ int cpu, int group_fd,
+ unsigned long flags)
+{
+ attr->size = sizeof(*attr);
+ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+static inline bool breakpoint_test(int len)
+{
+ struct perf_event_attr attr;
+ int fd;
+
+ /* setup counters */
+ memset(&attr, 0, sizeof(attr));
+ attr.disabled = 1;
+ attr.type = PERF_TYPE_BREAKPOINT;
+ attr.bp_type = HW_BREAKPOINT_R;
+ /* bp_addr can point anywhere but needs to be aligned */
+ attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800;
+ attr.bp_len = len;
+ fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ if (fd < 0)
+ return false;
+ close(fd);
+ return true;
+}
+
+static inline bool perf_breakpoint_supported(void)
+{
+ return breakpoint_test(4);
+}
+
+static inline bool dawr_supported(void)
+{
+ return breakpoint_test(DAWR_LENGTH_MAX);
+}
+
+static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
+{
+ int i,j;
+ struct perf_event_attr attr;
+ size_t res;
+ unsigned long long breaks, needed;
+ int readint;
+ int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)];
+ int *readintalign;
+ volatile int *ptr;
+ int break_fd;
+ int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */
+ volatile int *k;
+
+ /* align to 0x400 boundary as required by DAWR */
+ readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) &
+ 0xfffffffffffff800);
+
+ ptr = &readint;
+ if (arraytest)
+ ptr = &readintalign[0];
+
+ /* setup counters */
+ memset(&attr, 0, sizeof(attr));
+ attr.disabled = 1;
+ attr.type = PERF_TYPE_BREAKPOINT;
+ attr.bp_type = readwriteflag;
+ attr.bp_addr = (__u64)ptr;
+ attr.bp_len = sizeof(int);
+ if (arraytest)
+ attr.bp_len = DAWR_LENGTH_MAX;
+ attr.exclude_user = exclude_user;
+ break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ if (break_fd < 0) {
+ perror("sys_perf_event_open");
+ exit(1);
+ }
+
+ /* start counters */
+ ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
+
+ /* Test a bunch of reads and writes */
+ k = &readint;
+ for (i = 0; i < loop_num; i++) {
+ if (arraytest)
+ k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]);
+
+ j = *k;
+ *k = j;
+ }
+
+ /* stop counters */
+ ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
+
+ /* read and check counters */
+ res = read(break_fd, &breaks, sizeof(unsigned long long));
+ assert(res == sizeof(unsigned long long));
+ /* we read and write each loop, so subtract the ones we are counting */
+ needed = 0;
+ if (readwriteflag & HW_BREAKPOINT_R)
+ needed += loop_num;
+ if (readwriteflag & HW_BREAKPOINT_W)
+ needed += loop_num;
+ needed = needed * (1 - exclude_user);
+ printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n",
+ (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest);
+ if (breaks != needed) {
+ printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n",
+ (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user);
+ return 1;
+ }
+ close(break_fd);
+
+ return 0;
+}
+
+static int runtest_dar_outside(void)
+{
+ void *target;
+ volatile __u16 temp16;
+ volatile __u64 temp64;
+ struct perf_event_attr attr;
+ int break_fd;
+ unsigned long long breaks;
+ int fail = 0;
+ size_t res;
+
+ target = malloc(8);
+ if (!target) {
+ perror("malloc failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* setup counters */
+ memset(&attr, 0, sizeof(attr));
+ attr.disabled = 1;
+ attr.type = PERF_TYPE_BREAKPOINT;
+ attr.exclude_kernel = 1;
+ attr.exclude_hv = 1;
+ attr.exclude_guest = 1;
+ attr.bp_type = HW_BREAKPOINT_RW;
+ /* watch middle half of target array */
+ attr.bp_addr = (__u64)(target + 2);
+ attr.bp_len = 4;
+ break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ if (break_fd < 0) {
+ free(target);
+ perror("sys_perf_event_open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Shouldn't hit. */
+ ioctl(break_fd, PERF_EVENT_IOC_RESET);
+ ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
+ temp16 = *((__u16 *)target);
+ *((__u16 *)target) = temp16;
+ ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
+ res = read(break_fd, &breaks, sizeof(unsigned long long));
+ assert(res == sizeof(unsigned long long));
+ if (breaks == 0) {
+ printf("TESTED: No overlap\n");
+ } else {
+ printf("FAILED: No overlap: %lld != 0\n", breaks);
+ fail = 1;
+ }
+
+ /* Hit */
+ ioctl(break_fd, PERF_EVENT_IOC_RESET);
+ ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
+ temp16 = *((__u16 *)(target + 1));
+ *((__u16 *)(target + 1)) = temp16;
+ ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
+ res = read(break_fd, &breaks, sizeof(unsigned long long));
+ assert(res == sizeof(unsigned long long));
+ if (breaks == 2) {
+ printf("TESTED: Partial overlap\n");
+ } else {
+ printf("FAILED: Partial overlap: %lld != 2\n", breaks);
+ fail = 1;
+ }
+
+ /* Hit */
+ ioctl(break_fd, PERF_EVENT_IOC_RESET);
+ ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
+ temp16 = *((__u16 *)(target + 5));
+ *((__u16 *)(target + 5)) = temp16;
+ ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
+ res = read(break_fd, &breaks, sizeof(unsigned long long));
+ assert(res == sizeof(unsigned long long));
+ if (breaks == 2) {
+ printf("TESTED: Partial overlap\n");
+ } else {
+ printf("FAILED: Partial overlap: %lld != 2\n", breaks);
+ fail = 1;
+ }
+
+ /* Shouldn't Hit */
+ ioctl(break_fd, PERF_EVENT_IOC_RESET);
+ ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
+ temp16 = *((__u16 *)(target + 6));
+ *((__u16 *)(target + 6)) = temp16;
+ ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
+ res = read(break_fd, &breaks, sizeof(unsigned long long));
+ assert(res == sizeof(unsigned long long));
+ if (breaks == 0) {
+ printf("TESTED: No overlap\n");
+ } else {
+ printf("FAILED: No overlap: %lld != 0\n", breaks);
+ fail = 1;
+ }
+
+ /* Hit */
+ ioctl(break_fd, PERF_EVENT_IOC_RESET);
+ ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
+ temp64 = *((__u64 *)target);
+ *((__u64 *)target) = temp64;
+ ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
+ res = read(break_fd, &breaks, sizeof(unsigned long long));
+ assert(res == sizeof(unsigned long long));
+ if (breaks == 2) {
+ printf("TESTED: Full overlap\n");
+ } else {
+ printf("FAILED: Full overlap: %lld != 2\n", breaks);
+ fail = 1;
+ }
+
+ free(target);
+ close(break_fd);
+ return fail;
+}
+
+static int runtest(void)
+{
+ int rwflag;
+ int exclude_user;
+ int ret;
+
+ /*
+ * perf defines rwflag as two bits read and write and at least
+ * one must be set. So range 1-3.
+ */
+ for (rwflag = 1 ; rwflag < 4; rwflag++) {
+ for (exclude_user = 0 ; exclude_user < 2; exclude_user++) {
+ ret = runtestsingle(rwflag, exclude_user, 0);
+ if (ret)
+ return ret;
+
+ /* if we have the dawr, we can do an array test */
+ if (!dawr_supported())
+ continue;
+ ret = runtestsingle(rwflag, exclude_user, 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = runtest_dar_outside();
+ return ret;
+}
+
+
+static int perf_hwbreak(void)
+{
+ srand ( time(NULL) );
+
+ SKIP_IF(!perf_breakpoint_supported());
+
+ return runtest();
+}
+
+int main(int argc, char *argv[], char **envp)
+{
+ return test_harness(perf_hwbreak, "perf_hwbreak");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
new file mode 100644
index 000000000..17cd480c8
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for GPR/FPR registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "ptrace-gpr.h"
+#include "reg.h"
+
+/* Tracer and Tracee Shared Data */
+int shm_id;
+int *cptr, *pptr;
+
+float a = FPR_1;
+float b = FPR_2;
+float c = FPR_3;
+
+void gpr(void)
+{
+ unsigned long gpr_buf[18];
+ float fpr_buf[32];
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+
+ asm __volatile__(
+ ASM_LOAD_GPR_IMMED(gpr_1)
+ ASM_LOAD_FPR_SINGLE_PRECISION(flt_1)
+ :
+ : [gpr_1]"i"(GPR_1), [flt_1] "b" (&a)
+ : "memory", "r6", "r7", "r8", "r9", "r10",
+ "r11", "r12", "r13", "r14", "r15", "r16", "r17",
+ "r18", "r19", "r20", "r21", "r22", "r23", "r24",
+ "r25", "r26", "r27", "r28", "r29", "r30", "r31"
+ );
+
+ cptr[1] = 1;
+
+ while (!cptr[0])
+ asm volatile("" : : : "memory");
+
+ shmdt((void *)cptr);
+ store_gpr(gpr_buf);
+ store_fpr_single_precision(fpr_buf);
+
+ if (validate_gpr(gpr_buf, GPR_3))
+ exit(1);
+
+ if (validate_fpr_float(fpr_buf, c))
+ exit(1);
+
+ exit(0);
+}
+
+int trace_gpr(pid_t child)
+{
+ unsigned long gpr[18];
+ unsigned long fpr[32];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_gpr(child, gpr));
+ FAIL_IF(validate_gpr(gpr, GPR_1));
+ FAIL_IF(show_fpr(child, fpr));
+ FAIL_IF(validate_fpr(fpr, FPR_1_REP));
+ FAIL_IF(write_gpr(child, GPR_3));
+ FAIL_IF(write_fpr(child, FPR_3_REP));
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+int ptrace_gpr(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+ if (pid == 0)
+ gpr();
+
+ if (pid) {
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ while (!pptr[1])
+ asm volatile("" : : : "memory");
+
+ ret = trace_gpr(pid);
+ if (ret) {
+ kill(pid, SIGTERM);
+ shmdt((void *)pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ pptr[0] = 1;
+ shmdt((void *)pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_gpr, "ptrace_gpr");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h
new file mode 100644
index 000000000..c5cd53181
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#define GPR_1 1
+#define GPR_2 2
+#define GPR_3 3
+#define GPR_4 4
+
+#define FPR_1 0.001
+#define FPR_2 0.002
+#define FPR_3 0.003
+#define FPR_4 0.004
+
+#define FPR_1_REP 0x3f50624de0000000
+#define FPR_2_REP 0x3f60624de0000000
+#define FPR_3_REP 0x3f689374c0000000
+#define FPR_4_REP 0x3f70624de0000000
+
+/* Buffer must have 18 elements */
+int validate_gpr(unsigned long *gpr, unsigned long val)
+{
+ int i, found = 1;
+
+ for (i = 0; i < 18; i++) {
+ if (gpr[i] != val) {
+ printf("GPR[%d]: %lx Expected: %lx\n",
+ i+14, gpr[i], val);
+ found = 0;
+ }
+ }
+
+ if (!found)
+ return TEST_FAIL;
+ return TEST_PASS;
+}
+
+/* Buffer must have 32 elements */
+int validate_fpr(unsigned long *fpr, unsigned long val)
+{
+ int i, found = 1;
+
+ for (i = 0; i < 32; i++) {
+ if (fpr[i] != val) {
+ printf("FPR[%d]: %lx Expected: %lx\n", i, fpr[i], val);
+ found = 0;
+ }
+ }
+
+ if (!found)
+ return TEST_FAIL;
+ return TEST_PASS;
+}
+
+/* Buffer must have 32 elements */
+int validate_fpr_float(float *fpr, float val)
+{
+ int i, found = 1;
+
+ for (i = 0; i < 32; i++) {
+ if (fpr[i] != val) {
+ printf("FPR[%d]: %f Expected: %f\n", i, fpr[i], val);
+ found = 0;
+ }
+ }
+
+ if (!found)
+ return TEST_FAIL;
+ return TEST_PASS;
+}
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 000000000..2e0d86e06
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Ptrace test for hw breakpoints
+ *
+ * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
+ *
+ * This test forks and the parent then traces the child doing various
+ * types of ptrace enabled breakpoints
+ *
+ * Copyright (C) 2018 Michael Neuling, IBM Corporation.
+ */
+
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/user.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <linux/limits.h>
+#include "ptrace.h"
+
+#define SPRN_PVR 0x11F
+#define PVR_8xx 0x00500000
+
+bool is_8xx;
+
+/*
+ * Use volatile on all global var so that compiler doesn't
+ * optimise their load/stores. Otherwise selftest can fail.
+ */
+static volatile __u64 glvar;
+
+#define DAWR_MAX_LEN 512
+static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
+
+#define A_LEN 6
+#define B_LEN 6
+struct gstruct {
+ __u8 a[A_LEN]; /* double word aligned */
+ __u8 b[B_LEN]; /* double word unaligned */
+};
+static volatile struct gstruct gstruct __attribute__((aligned(512)));
+
+static volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
+
+static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
+{
+ if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
+ perror("Can't get breakpoint info");
+ exit(-1);
+ }
+}
+
+static bool dawr_present(struct ppc_debug_info *dbginfo)
+{
+ return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
+}
+
+static void write_var(int len)
+{
+ __u8 *pcvar;
+ __u16 *psvar;
+ __u32 *pivar;
+ __u64 *plvar;
+
+ switch (len) {
+ case 1:
+ pcvar = (__u8 *)&glvar;
+ *pcvar = 0xff;
+ break;
+ case 2:
+ psvar = (__u16 *)&glvar;
+ *psvar = 0xffff;
+ break;
+ case 4:
+ pivar = (__u32 *)&glvar;
+ *pivar = 0xffffffff;
+ break;
+ case 8:
+ plvar = (__u64 *)&glvar;
+ *plvar = 0xffffffffffffffffLL;
+ break;
+ }
+}
+
+static void read_var(int len)
+{
+ __u8 cvar __attribute__((unused));
+ __u16 svar __attribute__((unused));
+ __u32 ivar __attribute__((unused));
+ __u64 lvar __attribute__((unused));
+
+ switch (len) {
+ case 1:
+ cvar = (__u8)glvar;
+ break;
+ case 2:
+ svar = (__u16)glvar;
+ break;
+ case 4:
+ ivar = (__u32)glvar;
+ break;
+ case 8:
+ lvar = (__u64)glvar;
+ break;
+ }
+}
+
+static void test_workload(void)
+{
+ __u8 cvar __attribute__((unused));
+ __u32 ivar __attribute__((unused));
+ int len = 0;
+
+ if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
+ perror("Child can't be traced?");
+ exit(-1);
+ }
+
+ /* Wake up father so that it sets up the first test */
+ kill(getpid(), SIGUSR1);
+
+ /* PTRACE_SET_DEBUGREG, WO test */
+ for (len = 1; len <= sizeof(glvar); len <<= 1)
+ write_var(len);
+
+ /* PTRACE_SET_DEBUGREG, RO test */
+ for (len = 1; len <= sizeof(glvar); len <<= 1)
+ read_var(len);
+
+ /* PTRACE_SET_DEBUGREG, RW test */
+ for (len = 1; len <= sizeof(glvar); len <<= 1) {
+ if (rand() % 2)
+ read_var(len);
+ else
+ write_var(len);
+ }
+
+ /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
+ syscall(__NR_getcwd, &cwd, PATH_MAX);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
+ write_var(1);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
+ read_var(1);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
+ if (rand() % 2)
+ write_var(1);
+ else
+ read_var(1);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
+ syscall(__NR_getcwd, &cwd, PATH_MAX);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
+ gstruct.a[rand() % A_LEN] = 'a';
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
+ cvar = gstruct.a[rand() % A_LEN];
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
+ if (rand() % 2)
+ gstruct.a[rand() % A_LEN] = 'a';
+ else
+ cvar = gstruct.a[rand() % A_LEN];
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
+ gstruct.b[rand() % B_LEN] = 'b';
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
+ cvar = gstruct.b[rand() % B_LEN];
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
+ if (rand() % 2)
+ gstruct.b[rand() % B_LEN] = 'b';
+ else
+ cvar = gstruct.b[rand() % B_LEN];
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
+ if (rand() % 2)
+ *((int *)(gstruct.a + 4)) = 10;
+ else
+ ivar = *((int *)(gstruct.a + 4));
+
+ /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
+ if (rand() % 2)
+ big_var[rand() % DAWR_MAX_LEN] = 'a';
+ else
+ cvar = big_var[rand() % DAWR_MAX_LEN];
+}
+
+static void check_success(pid_t child_pid, const char *name, const char *type,
+ unsigned long saddr, int len)
+{
+ int status;
+ siginfo_t siginfo;
+ unsigned long eaddr = (saddr + len - 1) | 0x7;
+
+ saddr &= ~0x7;
+
+ /* Wait for the child to SIGTRAP */
+ wait(&status);
+
+ ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
+ (unsigned long)siginfo.si_addr < saddr ||
+ (unsigned long)siginfo.si_addr > eaddr) {
+ printf("%s, %s, len: %d: Fail\n", name, type, len);
+ exit(-1);
+ }
+
+ printf("%s, %s, len: %d: Ok\n", name, type, len);
+
+ if (!is_8xx) {
+ /*
+ * For ptrace registered watchpoint, signal is generated
+ * before executing load/store. Singlestep the instruction
+ * and then continue the test.
+ */
+ ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
+ wait(NULL);
+ }
+}
+
+static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
+{
+ if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
+ perror("PTRACE_SET_DEBUGREG failed");
+ exit(-1);
+ }
+}
+
+static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
+{
+ int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
+
+ if (wh <= 0) {
+ perror("PPC_PTRACE_SETHWDEBUG failed");
+ exit(-1);
+ }
+ return wh;
+}
+
+static void ptrace_delhwdebug(pid_t child_pid, int wh)
+{
+ if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
+ perror("PPC_PTRACE_DELHWDEBUG failed");
+ exit(-1);
+ }
+}
+
+#define DABR_READ_SHIFT 0
+#define DABR_WRITE_SHIFT 1
+#define DABR_TRANSLATION_SHIFT 2
+
+static int test_set_debugreg(pid_t child_pid)
+{
+ unsigned long wp_addr = (unsigned long)&glvar;
+ char *name = "PTRACE_SET_DEBUGREG";
+ int len;
+
+ /* PTRACE_SET_DEBUGREG, WO test*/
+ wp_addr &= ~0x7UL;
+ wp_addr |= (1UL << DABR_WRITE_SHIFT);
+ wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+ for (len = 1; len <= sizeof(glvar); len <<= 1) {
+ ptrace_set_debugreg(child_pid, wp_addr);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "WO", wp_addr, len);
+ }
+
+ /* PTRACE_SET_DEBUGREG, RO test */
+ wp_addr &= ~0x7UL;
+ wp_addr |= (1UL << DABR_READ_SHIFT);
+ wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+ for (len = 1; len <= sizeof(glvar); len <<= 1) {
+ ptrace_set_debugreg(child_pid, wp_addr);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RO", wp_addr, len);
+ }
+
+ /* PTRACE_SET_DEBUGREG, RW test */
+ wp_addr &= ~0x7UL;
+ wp_addr |= (1Ul << DABR_READ_SHIFT);
+ wp_addr |= (1UL << DABR_WRITE_SHIFT);
+ wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+ for (len = 1; len <= sizeof(glvar); len <<= 1) {
+ ptrace_set_debugreg(child_pid, wp_addr);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RW", wp_addr, len);
+ }
+
+ ptrace_set_debugreg(child_pid, 0);
+ return 0;
+}
+
+static int test_set_debugreg_kernel_userspace(pid_t child_pid)
+{
+ unsigned long wp_addr = (unsigned long)cwd;
+ char *name = "PTRACE_SET_DEBUGREG";
+
+ /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
+ wp_addr &= ~0x7UL;
+ wp_addr |= (1Ul << DABR_READ_SHIFT);
+ wp_addr |= (1UL << DABR_WRITE_SHIFT);
+ wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+ ptrace_set_debugreg(child_pid, wp_addr);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8);
+
+ ptrace_set_debugreg(child_pid, 0);
+ return 0;
+}
+
+static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
+ unsigned long addr, int len)
+{
+ info->version = 1;
+ info->trigger_type = type;
+ info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ info->addr = (__u64)addr;
+ info->addr2 = (__u64)addr + len;
+ info->condition_value = 0;
+ if (!len)
+ info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ else
+ info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+}
+
+static void test_sethwdebug_exact(pid_t child_pid)
+{
+ struct ppc_hw_breakpoint info;
+ unsigned long wp_addr = (unsigned long)&glvar;
+ char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
+ int len = 1; /* hardcoded in kernel */
+ int wh;
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "WO", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RO", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RW", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+}
+
+static void test_sethwdebug_exact_kernel_userspace(pid_t child_pid)
+{
+ struct ppc_hw_breakpoint info;
+ unsigned long wp_addr = (unsigned long)&cwd;
+ char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
+ int len = 1; /* hardcoded in kernel */
+ int wh;
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+}
+
+static void test_sethwdebug_range_aligned(pid_t child_pid)
+{
+ struct ppc_hw_breakpoint info;
+ unsigned long wp_addr;
+ char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
+ int len;
+ int wh;
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
+ wp_addr = (unsigned long)&gstruct.a;
+ len = A_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "WO", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
+ wp_addr = (unsigned long)&gstruct.a;
+ len = A_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RO", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
+ wp_addr = (unsigned long)&gstruct.a;
+ len = A_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RW", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+}
+
+static void test_sethwdebug_range_unaligned(pid_t child_pid)
+{
+ struct ppc_hw_breakpoint info;
+ unsigned long wp_addr;
+ char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
+ int len;
+ int wh;
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
+ wp_addr = (unsigned long)&gstruct.b;
+ len = B_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "WO", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
+ wp_addr = (unsigned long)&gstruct.b;
+ len = B_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RO", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
+ wp_addr = (unsigned long)&gstruct.b;
+ len = B_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RW", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+
+}
+
+static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
+{
+ struct ppc_hw_breakpoint info;
+ unsigned long wp_addr;
+ char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
+ int len;
+ int wh;
+
+ /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
+ wp_addr = (unsigned long)&gstruct.b;
+ len = B_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RW", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+}
+
+static void test_sethwdebug_dawr_max_range(pid_t child_pid)
+{
+ struct ppc_hw_breakpoint info;
+ unsigned long wp_addr;
+ char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
+ int len;
+ int wh;
+
+ /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
+ wp_addr = (unsigned long)big_var;
+ len = DAWR_MAX_LEN;
+ get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
+ wh = ptrace_sethwdebug(child_pid, &info);
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ check_success(child_pid, name, "RW", wp_addr, len);
+ ptrace_delhwdebug(child_pid, wh);
+}
+
+/* Set the breakpoints and check the child successfully trigger them */
+static void
+run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
+{
+ test_set_debugreg(child_pid);
+ test_set_debugreg_kernel_userspace(child_pid);
+ test_sethwdebug_exact(child_pid);
+ test_sethwdebug_exact_kernel_userspace(child_pid);
+ if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
+ test_sethwdebug_range_aligned(child_pid);
+ if (dawr || is_8xx) {
+ test_sethwdebug_range_unaligned(child_pid);
+ test_sethwdebug_range_unaligned_dar(child_pid);
+ test_sethwdebug_dawr_max_range(child_pid);
+ }
+ }
+}
+
+static int ptrace_hwbreak(void)
+{
+ pid_t child_pid;
+ struct ppc_debug_info dbginfo;
+ bool dawr;
+
+ child_pid = fork();
+ if (!child_pid) {
+ test_workload();
+ return 0;
+ }
+
+ wait(NULL);
+
+ get_dbginfo(child_pid, &dbginfo);
+ SKIP_IF(dbginfo.num_data_bps == 0);
+
+ dawr = dawr_present(&dbginfo);
+ run_tests(child_pid, &dbginfo, dawr);
+
+ /* Let the child exit first. */
+ ptrace(PTRACE_CONT, child_pid, NULL, 0);
+ wait(NULL);
+
+ /*
+ * Testcases exits immediately with -1 on any failure. If
+ * it has reached here, it means all tests were successful.
+ */
+ return TEST_PASS;
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ int pvr = 0;
+ asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
+ if (pvr == PVR_8xx)
+ is_8xx = true;
+
+ return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
+}
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 000000000..bc454f899
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ptrace test for Memory Protection Key registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ * Copyright (C) 2018 IBM Corporation.
+ */
+#include "ptrace.h"
+#include "child.h"
+
+#ifndef __NR_pkey_alloc
+#define __NR_pkey_alloc 384
+#endif
+
+#ifndef __NR_pkey_free
+#define __NR_pkey_free 385
+#endif
+
+#ifndef NT_PPC_PKEY
+#define NT_PPC_PKEY 0x110
+#endif
+
+#ifndef PKEY_DISABLE_EXECUTE
+#define PKEY_DISABLE_EXECUTE 0x4
+#endif
+
+#define AMR_BITS_PER_PKEY 2
+#define PKEY_REG_BITS (sizeof(u64) * 8)
+#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
+
+static const char user_read[] = "[User Read (Running)]";
+static const char user_write[] = "[User Write (Running)]";
+static const char ptrace_read_running[] = "[Ptrace Read (Running)]";
+static const char ptrace_write_running[] = "[Ptrace Write (Running)]";
+
+/* Information shared between the parent and the child. */
+struct shared_info {
+ struct child_sync child_sync;
+
+ /* AMR value the parent expects to read from the child. */
+ unsigned long amr1;
+
+ /* AMR value the parent is expected to write to the child. */
+ unsigned long amr2;
+
+ /* AMR value that ptrace should refuse to write to the child. */
+ unsigned long invalid_amr;
+
+ /* IAMR value the parent expects to read from the child. */
+ unsigned long expected_iamr;
+
+ /* UAMOR value the parent expects to read from the child. */
+ unsigned long expected_uamor;
+
+ /*
+ * IAMR and UAMOR values that ptrace should refuse to write to the child
+ * (even though they're valid ones) because userspace doesn't have
+ * access to those registers.
+ */
+ unsigned long invalid_iamr;
+ unsigned long invalid_uamor;
+};
+
+static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
+{
+ return syscall(__NR_pkey_alloc, flags, init_access_rights);
+}
+
+static int child(struct shared_info *info)
+{
+ unsigned long reg;
+ bool disable_execute = true;
+ int pkey1, pkey2, pkey3;
+ int ret;
+
+ /* Wait until parent fills out the initial register values. */
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Get some pkeys so that we can change their bits in the AMR. */
+ pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
+ if (pkey1 < 0) {
+ pkey1 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
+
+ disable_execute = false;
+ }
+
+ pkey2 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
+
+ pkey3 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);
+
+ info->amr1 |= 3ul << pkeyshift(pkey1);
+ info->amr2 |= 3ul << pkeyshift(pkey2);
+ /*
+ * invalid amr value where we try to force write
+ * things which are deined by a uamor setting.
+ */
+ info->invalid_amr = info->amr2 | (~0x0UL & ~info->expected_uamor);
+
+ /*
+ * if PKEY_DISABLE_EXECUTE succeeded we should update the expected_iamr
+ */
+ if (disable_execute)
+ info->expected_iamr |= 1ul << pkeyshift(pkey1);
+ else
+ info->expected_iamr &= ~(1ul << pkeyshift(pkey1));
+
+ /*
+ * We allocated pkey2 and pkey 3 above. Clear the IAMR bits.
+ */
+ info->expected_iamr &= ~(1ul << pkeyshift(pkey2));
+ info->expected_iamr &= ~(1ul << pkeyshift(pkey3));
+
+ /*
+ * Create an IAMR value different from expected value.
+ * Kernel will reject an IAMR and UAMOR change.
+ */
+ info->invalid_iamr = info->expected_iamr | (1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2));
+ info->invalid_uamor = info->expected_uamor & ~(0x3ul << pkeyshift(pkey1));
+
+ printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
+ user_write, info->amr1, pkey1, pkey2, pkey3);
+
+ set_amr(info->amr1);
+
+ /* Wait for parent to read our AMR value and write a new one. */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /*
+ * Wait for parent to try to write an invalid AMR value.
+ */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /*
+ * Wait for parent to try to write an IAMR and a UAMOR value. We can't
+ * verify them, but we can verify that the AMR didn't change.
+ */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /* Now let parent now that we are finished. */
+
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ return TEST_PASS;
+}
+
+static int parent(struct shared_info *info, pid_t pid)
+{
+ unsigned long regs[3];
+ int ret, status;
+
+ /*
+ * Get the initial values for AMR, IAMR and UAMOR and communicate them
+ * to the child.
+ */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ info->amr1 = info->amr2 = regs[0];
+ info->expected_iamr = regs[1];
+ info->expected_uamor = regs[2];
+
+ /* Wake up child so that it can set itself up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Verify that we can read the pkey registers from the child. */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);
+ PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
+ PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
+
+ /* Write valid AMR value in child. */
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);
+
+ /* Wake up child so that it can verify it changed. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Write invalid AMR value in child. */
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->invalid_amr, 1);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx\n", ptrace_write_running, info->invalid_amr);
+
+ /* Wake up child so that it can verify it didn't change. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Try to write to IAMR. */
+ regs[0] = info->amr1;
+ regs[1] = info->invalid_iamr;
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
+ PARENT_FAIL_IF(!ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx\n",
+ ptrace_write_running, regs[0], regs[1]);
+
+ /* Try to write to IAMR and UAMOR. */
+ regs[2] = info->invalid_uamor;
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(!ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_write_running, regs[0], regs[1], regs[2]);
+
+ /* Verify that all registers still have their expected values. */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);
+ PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
+ PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
+
+ /* Wake up child so that it can verify AMR didn't change and wrap up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait(&status);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ ret = TEST_PASS;
+ } else if (!WIFEXITED(status)) {
+ printf("Child exited abnormally\n");
+ ret = TEST_FAIL;
+ } else
+ ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;
+
+ return ret;
+}
+
+static int ptrace_pkey(void)
+{
+ struct shared_info *info;
+ int shm_id;
+ int ret;
+ pid_t pid;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
+ info = shmat(shm_id, NULL, 0);
+
+ ret = init_child_sync(&info->child_sync);
+ if (ret)
+ return ret;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ ret = TEST_FAIL;
+ } else if (pid == 0)
+ ret = child(info);
+ else
+ ret = parent(info, pid);
+
+ shmdt(info);
+
+ if (pid) {
+ destroy_child_sync(&info->child_sync);
+ shmctl(shm_id, IPC_RMID, NULL);
+ }
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_pkey, "ptrace_pkey");
+}
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 000000000..3353210dc
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and
+ * PTRACE_GETREG. This test basically create a child process that executes
+ * syscalls and the parent process check if it is being traced appropriated.
+ *
+ * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c
+ * test, and it was adapted to run on Powerpc by
+ * Breno Leitao <leitao@debian.org>
+ */
+#define _GNU_SOURCE
+
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <sys/user.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <sys/auxv.h>
+#include "utils.h"
+
+/* Bitness-agnostic defines for user_regs_struct fields. */
+#define user_syscall_nr gpr[0]
+#define user_arg0 gpr[3]
+#define user_arg1 gpr[4]
+#define user_arg2 gpr[5]
+#define user_arg3 gpr[6]
+#define user_arg4 gpr[7]
+#define user_arg5 gpr[8]
+#define user_ip nip
+
+#define PTRACE_SYSEMU 0x1d
+
+static int nerrs;
+
+static void wait_trap(pid_t chld)
+{
+ siginfo_t si;
+
+ if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
+ err(1, "waitid");
+ if (si.si_pid != chld)
+ errx(1, "got unexpected pid in event\n");
+ if (si.si_code != CLD_TRAPPED)
+ errx(1, "got unexpected event type %d\n", si.si_code);
+}
+
+static void test_ptrace_syscall_restart(void)
+{
+ int status;
+ struct pt_regs regs;
+ pid_t chld;
+
+ printf("[RUN]\tptrace-induced syscall restart\n");
+
+ chld = fork();
+ if (chld < 0)
+ err(1, "fork");
+
+ /*
+ * Child process is running 4 syscalls after ptrace.
+ *
+ * 1) getpid()
+ * 2) gettid()
+ * 3) tgkill() -> Send SIGSTOP
+ * 4) gettid() -> Where the tests will happen essentially
+ */
+ if (chld == 0) {
+ if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
+ err(1, "PTRACE_TRACEME");
+
+ pid_t pid = getpid(), tid = syscall(SYS_gettid);
+
+ printf("\tChild will make one syscall\n");
+ syscall(SYS_tgkill, pid, tid, SIGSTOP);
+
+ syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
+ _exit(0);
+ }
+ /* Parent process below */
+
+ /* Wait for SIGSTOP sent by tgkill above. */
+ if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
+ err(1, "waitpid");
+
+ printf("[RUN]\tSYSEMU\n");
+ if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
+ err(1, "PTRACE_SYSEMU");
+ wait_trap(chld);
+
+ if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
+ err(1, "PTRACE_GETREGS");
+
+ /*
+ * Ptrace trapped prior to executing the syscall, thus r3 still has
+ * the syscall number instead of the sys_gettid() result
+ */
+ if (regs.user_syscall_nr != SYS_gettid ||
+ regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
+ regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
+ regs.user_arg4 != 14 || regs.user_arg5 != 15) {
+ printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
+ (unsigned long)regs.user_syscall_nr,
+ (unsigned long)regs.user_arg0,
+ (unsigned long)regs.user_arg1,
+ (unsigned long)regs.user_arg2,
+ (unsigned long)regs.user_arg3,
+ (unsigned long)regs.user_arg4,
+ (unsigned long)regs.user_arg5);
+ nerrs++;
+ } else {
+ printf("[OK]\tInitial nr and args are correct\n"); }
+
+ printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
+ (unsigned long)regs.user_ip);
+
+ /*
+ * Rewind to retry the same syscall again. This will basically test
+ * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS.
+ */
+ regs.user_ip -= 4;
+ if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
+ err(1, "PTRACE_SETREGS");
+
+ if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
+ err(1, "PTRACE_SYSEMU");
+ wait_trap(chld);
+
+ if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
+ err(1, "PTRACE_GETREGS");
+
+ if (regs.user_syscall_nr != SYS_gettid ||
+ regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
+ regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
+ regs.user_arg4 != 14 || regs.user_arg5 != 15) {
+ printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
+ (unsigned long)regs.user_syscall_nr,
+ (unsigned long)regs.user_arg0,
+ (unsigned long)regs.user_arg1,
+ (unsigned long)regs.user_arg2,
+ (unsigned long)regs.user_arg3,
+ (unsigned long)regs.user_arg4,
+ (unsigned long)regs.user_arg5);
+ nerrs++;
+ } else {
+ printf("[OK]\tRestarted nr and args are correct\n");
+ }
+
+ printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
+ (unsigned long)regs.user_ip);
+
+ /*
+ * Inject a new syscall (getpid) in the same place the previous
+ * syscall (gettid), rewind and re-execute.
+ */
+ regs.user_syscall_nr = SYS_getpid;
+ regs.user_arg0 = 20;
+ regs.user_arg1 = 21;
+ regs.user_arg2 = 22;
+ regs.user_arg3 = 23;
+ regs.user_arg4 = 24;
+ regs.user_arg5 = 25;
+ regs.user_ip -= 4;
+
+ if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
+ err(1, "PTRACE_SETREGS");
+
+ if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
+ err(1, "PTRACE_SYSEMU");
+ wait_trap(chld);
+
+ if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
+ err(1, "PTRACE_GETREGS");
+
+ /* Check that ptrace stopped at the new syscall that was
+ * injected, and guarantee that it haven't executed, i.e, user_args
+ * contain the arguments and not the syscall return value, for
+ * instance.
+ */
+ if (regs.user_syscall_nr != SYS_getpid
+ || regs.user_arg0 != 20 || regs.user_arg1 != 21
+ || regs.user_arg2 != 22 || regs.user_arg3 != 23
+ || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
+
+ printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
+ (unsigned long)regs.user_syscall_nr,
+ (unsigned long)regs.user_arg0,
+ (unsigned long)regs.user_arg1,
+ (unsigned long)regs.user_arg2,
+ (unsigned long)regs.user_arg3,
+ (unsigned long)regs.user_arg4,
+ (unsigned long)regs.user_arg5);
+ nerrs++;
+ } else {
+ printf("[OK]\tReplacement nr and args are correct\n");
+ }
+
+ if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
+ err(1, "PTRACE_CONT");
+
+ if (waitpid(chld, &status, 0) != chld)
+ err(1, "waitpid");
+
+ /* Guarantee that the process executed properly, returning 0 */
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ printf("[FAIL]\tChild failed\n");
+ nerrs++;
+ } else {
+ printf("[OK]\tChild exited cleanly\n");
+ }
+}
+
+int ptrace_syscall(void)
+{
+ test_ptrace_syscall_restart();
+
+ return nerrs;
+}
+
+int main(void)
+{
+ return test_harness(ptrace_syscall, "ptrace_syscall");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c
new file mode 100644
index 000000000..4436ca9d3
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for TAR, PPR, DSCR registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "ptrace-tar.h"
+
+/* Tracer and Tracee Shared Data */
+int shm_id;
+int *cptr;
+int *pptr;
+
+void tar(void)
+{
+ unsigned long reg[3];
+ int ret;
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+ printf("%-30s TAR: %u PPR: %lx DSCR: %u\n",
+ user_write, TAR_1, PPR_1, DSCR_1);
+
+ mtspr(SPRN_TAR, TAR_1);
+ mtspr(SPRN_PPR, PPR_1);
+ mtspr(SPRN_DSCR, DSCR_1);
+
+ cptr[2] = 1;
+
+ /* Wait on parent */
+ while (!cptr[0])
+ asm volatile("" : : : "memory");
+
+ reg[0] = mfspr(SPRN_TAR);
+ reg[1] = mfspr(SPRN_PPR);
+ reg[2] = mfspr(SPRN_DSCR);
+
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ user_read, reg[0], reg[1], reg[2]);
+
+ /* Unblock the parent now */
+ cptr[1] = 1;
+ shmdt((int *)cptr);
+
+ ret = validate_tar_registers(reg, TAR_2, PPR_2, DSCR_2);
+ if (ret)
+ exit(1);
+ exit(0);
+}
+
+int trace_tar(pid_t child)
+{
+ unsigned long reg[3];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_tar_registers(child, reg));
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ ptrace_read_running, reg[0], reg[1], reg[2]);
+
+ FAIL_IF(validate_tar_registers(reg, TAR_1, PPR_1, DSCR_1));
+ FAIL_IF(stop_trace(child));
+ return TEST_PASS;
+}
+
+int trace_tar_write(pid_t child)
+{
+ FAIL_IF(start_trace(child));
+ FAIL_IF(write_tar_registers(child, TAR_2, PPR_2, DSCR_2));
+ printf("%-30s TAR: %u PPR: %lx DSCR: %u\n",
+ ptrace_write_running, TAR_2, PPR_2, DSCR_2);
+
+ FAIL_IF(stop_trace(child));
+ return TEST_PASS;
+}
+
+int ptrace_tar(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ // TAR was added in v2.07
+ SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ tar();
+
+ if (pid) {
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ pptr[0] = 0;
+ pptr[1] = 0;
+
+ while (!pptr[2])
+ asm volatile("" : : : "memory");
+ ret = trace_tar(pid);
+ if (ret)
+ return ret;
+
+ ret = trace_tar_write(pid);
+ if (ret)
+ return ret;
+
+ /* Unblock the child now */
+ pptr[0] = 1;
+
+ /* Wait on child */
+ while (!pptr[1])
+ asm volatile("" : : : "memory");
+
+ shmdt((int *)pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_PASS;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tar, "ptrace_tar");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h
new file mode 100644
index 000000000..d6a4c0aab
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#define TAR_1 10
+#define TAR_2 20
+#define TAR_3 30
+#define TAR_4 40
+#define TAR_5 50
+
+#define DSCR_1 100
+#define DSCR_2 200
+#define DSCR_3 300
+#define DSCR_4 400
+#define DSCR_5 500
+
+#define PPR_1 0x4000000000000 /* or 31,31,31*/
+#define PPR_2 0x8000000000000 /* or 1,1,1 */
+#define PPR_3 0xc000000000000 /* or 6,6,6 */
+#define PPR_4 0x10000000000000 /* or 2,2,2 */
+
+char *user_read = "[User Read (Running)]";
+char *user_write = "[User Write (Running)]";
+char *ptrace_read_running = "[Ptrace Read (Running)]";
+char *ptrace_write_running = "[Ptrace Write (Running)]";
+char *ptrace_read_ckpt = "[Ptrace Read (Checkpointed)]";
+char *ptrace_write_ckpt = "[Ptrace Write (Checkpointed)]";
+
+int validate_tar_registers(unsigned long *reg, unsigned long tar,
+ unsigned long ppr, unsigned long dscr)
+{
+ int match = 1;
+
+ if (reg[0] != tar)
+ match = 0;
+
+ if (reg[1] != ppr)
+ match = 0;
+
+ if (reg[2] != dscr)
+ match = 0;
+
+ if (!match)
+ return TEST_FAIL;
+ return TEST_PASS;
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
new file mode 100644
index 000000000..82f7bdc2e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for GPR/FPR registers in TM context
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "ptrace-gpr.h"
+#include "tm.h"
+
+/* Tracer and Tracee Shared Data */
+int shm_id;
+unsigned long *cptr, *pptr;
+
+float a = FPR_1;
+float b = FPR_2;
+float c = FPR_3;
+
+void tm_gpr(void)
+{
+ unsigned long gpr_buf[18];
+ unsigned long result, texasr;
+ float fpr_buf[32];
+
+ printf("Starting the child\n");
+ cptr = (unsigned long *)shmat(shm_id, NULL, 0);
+
+trans:
+ cptr[1] = 0;
+ asm __volatile__(
+ ASM_LOAD_GPR_IMMED(gpr_1)
+ ASM_LOAD_FPR_SINGLE_PRECISION(flt_1)
+ "1: ;"
+ "tbegin.;"
+ "beq 2f;"
+ ASM_LOAD_GPR_IMMED(gpr_2)
+ ASM_LOAD_FPR_SINGLE_PRECISION(flt_2)
+ "tsuspend.;"
+ "li 7, 1;"
+ "stw 7, 0(%[cptr1]);"
+ "tresume.;"
+ "b .;"
+
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ /* Transaction abort handler */
+ "2: ;"
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+ : [res] "=r" (result), [texasr] "=r" (texasr)
+ : [gpr_1]"i"(GPR_1), [gpr_2]"i"(GPR_2),
+ [sprn_texasr] "i" (SPRN_TEXASR), [flt_1] "b" (&a),
+ [flt_2] "b" (&b), [cptr1] "b" (&cptr[1])
+ : "memory", "r7", "r8", "r9", "r10",
+ "r11", "r12", "r13", "r14", "r15", "r16",
+ "r17", "r18", "r19", "r20", "r21", "r22",
+ "r23", "r24", "r25", "r26", "r27", "r28",
+ "r29", "r30", "r31"
+ );
+
+ if (result) {
+ if (!cptr[0])
+ goto trans;
+
+ shmdt((void *)cptr);
+ store_gpr(gpr_buf);
+ store_fpr_single_precision(fpr_buf);
+
+ if (validate_gpr(gpr_buf, GPR_3))
+ exit(1);
+
+ if (validate_fpr_float(fpr_buf, c))
+ exit(1);
+
+ exit(0);
+ }
+ shmdt((void *)cptr);
+ exit(1);
+}
+
+int trace_tm_gpr(pid_t child)
+{
+ unsigned long gpr[18];
+ unsigned long fpr[32];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_gpr(child, gpr));
+ FAIL_IF(validate_gpr(gpr, GPR_2));
+ FAIL_IF(show_fpr(child, fpr));
+ FAIL_IF(validate_fpr(fpr, FPR_2_REP));
+ FAIL_IF(show_ckpt_fpr(child, fpr));
+ FAIL_IF(validate_fpr(fpr, FPR_1_REP));
+ FAIL_IF(show_ckpt_gpr(child, gpr));
+ FAIL_IF(validate_gpr(gpr, GPR_1));
+ FAIL_IF(write_ckpt_gpr(child, GPR_3));
+ FAIL_IF(write_ckpt_fpr(child, FPR_3_REP));
+
+ pptr[0] = 1;
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+int ptrace_tm_gpr(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+ if (pid == 0)
+ tm_gpr();
+
+ if (pid) {
+ pptr = (unsigned long *)shmat(shm_id, NULL, 0);
+
+ while (!pptr[1])
+ asm volatile("" : : : "memory");
+ ret = trace_tm_gpr(pid);
+ if (ret) {
+ kill(pid, SIGTERM);
+ return TEST_FAIL;
+ }
+
+ shmdt((void *)pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_gpr, "ptrace_tm_gpr");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c
new file mode 100644
index 000000000..ad65be6e8
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for GPR/FPR registers in TM Suspend context
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "ptrace-gpr.h"
+#include "tm.h"
+
+/* Tracer and Tracee Shared Data */
+int shm_id;
+int *cptr, *pptr;
+
+float a = FPR_1;
+float b = FPR_2;
+float c = FPR_3;
+float d = FPR_4;
+
+__attribute__((used)) void wait_parent(void)
+{
+ cptr[2] = 1;
+ while (!cptr[1])
+ asm volatile("" : : : "memory");
+}
+
+void tm_spd_gpr(void)
+{
+ unsigned long gpr_buf[18];
+ unsigned long result, texasr;
+ float fpr_buf[32];
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+
+trans:
+ cptr[2] = 0;
+ asm __volatile__(
+ ASM_LOAD_GPR_IMMED(gpr_1)
+ ASM_LOAD_FPR_SINGLE_PRECISION(flt_1)
+
+ "1: ;"
+ "tbegin.;"
+ "beq 2f;"
+
+ ASM_LOAD_GPR_IMMED(gpr_2)
+ "tsuspend.;"
+ ASM_LOAD_GPR_IMMED(gpr_4)
+ ASM_LOAD_FPR_SINGLE_PRECISION(flt_4)
+
+ "bl wait_parent;"
+ "tresume.;"
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ /* Transaction abort handler */
+ "2: ;"
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+ : [res] "=r" (result), [texasr] "=r" (texasr)
+ : [gpr_1]"i"(GPR_1), [gpr_2]"i"(GPR_2), [gpr_4]"i"(GPR_4),
+ [sprn_texasr] "i" (SPRN_TEXASR), [flt_1] "b" (&a),
+ [flt_4] "b" (&d)
+ : "memory", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
+ );
+
+ if (result) {
+ if (!cptr[0])
+ goto trans;
+
+ shmdt((void *)cptr);
+ store_gpr(gpr_buf);
+ store_fpr_single_precision(fpr_buf);
+
+ if (validate_gpr(gpr_buf, GPR_3))
+ exit(1);
+
+ if (validate_fpr_float(fpr_buf, c))
+ exit(1);
+ exit(0);
+ }
+ shmdt((void *)cptr);
+ exit(1);
+}
+
+int trace_tm_spd_gpr(pid_t child)
+{
+ unsigned long gpr[18];
+ unsigned long fpr[32];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_gpr(child, gpr));
+ FAIL_IF(validate_gpr(gpr, GPR_4));
+ FAIL_IF(show_fpr(child, fpr));
+ FAIL_IF(validate_fpr(fpr, FPR_4_REP));
+ FAIL_IF(show_ckpt_fpr(child, fpr));
+ FAIL_IF(validate_fpr(fpr, FPR_1_REP));
+ FAIL_IF(show_ckpt_gpr(child, gpr));
+ FAIL_IF(validate_gpr(gpr, GPR_1));
+ FAIL_IF(write_ckpt_gpr(child, GPR_3));
+ FAIL_IF(write_ckpt_fpr(child, FPR_3_REP));
+
+ pptr[0] = 1;
+ pptr[1] = 1;
+ FAIL_IF(stop_trace(child));
+ return TEST_PASS;
+}
+
+int ptrace_tm_spd_gpr(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ tm_spd_gpr();
+
+ if (pid) {
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ pptr[0] = 0;
+ pptr[1] = 0;
+
+ while (!pptr[2])
+ asm volatile("" : : : "memory");
+ ret = trace_tm_spd_gpr(pid);
+ if (ret) {
+ kill(pid, SIGTERM);
+ shmdt((void *)pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ shmdt((void *)pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_spd_gpr, "ptrace_tm_spd_gpr");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
new file mode 100644
index 000000000..2ecfa1158
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for TAR, PPR, DSCR registers in the TM Suspend context
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "tm.h"
+#include "ptrace-tar.h"
+
+int shm_id;
+int *cptr, *pptr;
+
+__attribute__((used)) void wait_parent(void)
+{
+ cptr[2] = 1;
+ while (!cptr[1])
+ asm volatile("" : : : "memory");
+}
+
+void tm_spd_tar(void)
+{
+ unsigned long result, texasr;
+ unsigned long regs[3];
+ int ret;
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+
+trans:
+ cptr[2] = 0;
+ asm __volatile__(
+ "li 4, %[tar_1];"
+ "mtspr %[sprn_tar], 4;" /* TAR_1 */
+ "li 4, %[dscr_1];"
+ "mtspr %[sprn_dscr], 4;" /* DSCR_1 */
+ "or 31,31,31;" /* PPR_1*/
+
+ "1: ;"
+ "tbegin.;"
+ "beq 2f;"
+
+ "li 4, %[tar_2];"
+ "mtspr %[sprn_tar], 4;" /* TAR_2 */
+ "li 4, %[dscr_2];"
+ "mtspr %[sprn_dscr], 4;" /* DSCR_2 */
+ "or 1,1,1;" /* PPR_2 */
+
+ "tsuspend.;"
+ "li 4, %[tar_3];"
+ "mtspr %[sprn_tar], 4;" /* TAR_3 */
+ "li 4, %[dscr_3];"
+ "mtspr %[sprn_dscr], 4;" /* DSCR_3 */
+ "or 6,6,6;" /* PPR_3 */
+ "bl wait_parent;"
+ "tresume.;"
+
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ /* Transaction abort handler */
+ "2: ;"
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+
+ : [res] "=r" (result), [texasr] "=r" (texasr)
+ : [sprn_dscr]"i"(SPRN_DSCR),
+ [sprn_tar]"i"(SPRN_TAR), [sprn_ppr]"i"(SPRN_PPR),
+ [sprn_texasr]"i"(SPRN_TEXASR), [tar_1]"i"(TAR_1),
+ [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), [dscr_2]"i"(DSCR_2),
+ [tar_3]"i"(TAR_3), [dscr_3]"i"(DSCR_3)
+ : "memory", "r0", "r3", "r4", "r5", "r6", "lr"
+ );
+
+ /* TM failed, analyse */
+ if (result) {
+ if (!cptr[0])
+ goto trans;
+
+ regs[0] = mfspr(SPRN_TAR);
+ regs[1] = mfspr(SPRN_PPR);
+ regs[2] = mfspr(SPRN_DSCR);
+
+ shmdt(&cptr);
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ user_read, regs[0], regs[1], regs[2]);
+
+ ret = validate_tar_registers(regs, TAR_4, PPR_4, DSCR_4);
+ if (ret)
+ exit(1);
+ exit(0);
+ }
+ shmdt(&cptr);
+ exit(1);
+}
+
+int trace_tm_spd_tar(pid_t child)
+{
+ unsigned long regs[3];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_tar_registers(child, regs));
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ FAIL_IF(validate_tar_registers(regs, TAR_3, PPR_3, DSCR_3));
+ FAIL_IF(show_tm_checkpointed_state(child, regs));
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ ptrace_read_ckpt, regs[0], regs[1], regs[2]);
+
+ FAIL_IF(validate_tar_registers(regs, TAR_1, PPR_1, DSCR_1));
+ FAIL_IF(write_ckpt_tar_registers(child, TAR_4, PPR_4, DSCR_4));
+ printf("%-30s TAR: %u PPR: %lx DSCR: %u\n",
+ ptrace_write_ckpt, TAR_4, PPR_4, DSCR_4);
+
+ pptr[0] = 1;
+ pptr[1] = 1;
+ FAIL_IF(stop_trace(child));
+ return TEST_PASS;
+}
+
+int ptrace_tm_spd_tar(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid == 0)
+ tm_spd_tar();
+
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ pptr[0] = 0;
+ pptr[1] = 0;
+
+ if (pid) {
+ while (!pptr[2])
+ asm volatile("" : : : "memory");
+ ret = trace_tm_spd_tar(pid);
+ if (ret) {
+ kill(pid, SIGTERM);
+ shmdt(&pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ shmdt(&pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_spd_tar, "ptrace_tm_spd_tar");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
new file mode 100644
index 000000000..6f7fb51f0
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for VMX/VSX registers in the TM Suspend context
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "tm.h"
+#include "ptrace-vsx.h"
+
+int shm_id;
+int *cptr, *pptr;
+
+unsigned long fp_load[VEC_MAX];
+unsigned long fp_load_new[VEC_MAX];
+unsigned long fp_store[VEC_MAX];
+unsigned long fp_load_ckpt[VEC_MAX];
+unsigned long fp_load_ckpt_new[VEC_MAX];
+
+__attribute__((used)) void load_vsx(void)
+{
+ loadvsx(fp_load, 0);
+}
+
+__attribute__((used)) void load_vsx_new(void)
+{
+ loadvsx(fp_load_new, 0);
+}
+
+__attribute__((used)) void load_vsx_ckpt(void)
+{
+ loadvsx(fp_load_ckpt, 0);
+}
+
+__attribute__((used)) void wait_parent(void)
+{
+ cptr[2] = 1;
+ while (!cptr[1])
+ asm volatile("" : : : "memory");
+}
+
+void tm_spd_vsx(void)
+{
+ unsigned long result, texasr;
+ int ret;
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+
+trans:
+ cptr[2] = 0;
+ asm __volatile__(
+ "bl load_vsx_ckpt;"
+
+ "1: ;"
+ "tbegin.;"
+ "beq 2f;"
+
+ "bl load_vsx_new;"
+ "tsuspend.;"
+ "bl load_vsx;"
+ "bl wait_parent;"
+ "tresume.;"
+
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ "2: ;"
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+ : [res] "=r" (result), [texasr] "=r" (texasr)
+ : [sprn_texasr] "i" (SPRN_TEXASR)
+ : "memory", "r0", "r3", "r4",
+ "r7", "r8", "r9", "r10", "r11", "lr"
+ );
+
+ if (result) {
+ if (!cptr[0])
+ goto trans;
+ shmdt((void *)cptr);
+
+ storevsx(fp_store, 0);
+ ret = compare_vsx_vmx(fp_store, fp_load_ckpt_new);
+ if (ret)
+ exit(1);
+ exit(0);
+ }
+ shmdt((void *)cptr);
+ exit(1);
+}
+
+int trace_tm_spd_vsx(pid_t child)
+{
+ unsigned long vsx[VSX_MAX];
+ unsigned long vmx[VMX_MAX + 2][2];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_vsx(child, vsx));
+ FAIL_IF(validate_vsx(vsx, fp_load));
+ FAIL_IF(show_vmx(child, vmx));
+ FAIL_IF(validate_vmx(vmx, fp_load));
+ FAIL_IF(show_vsx_ckpt(child, vsx));
+ FAIL_IF(validate_vsx(vsx, fp_load_ckpt));
+ FAIL_IF(show_vmx_ckpt(child, vmx));
+ FAIL_IF(validate_vmx(vmx, fp_load_ckpt));
+
+ memset(vsx, 0, sizeof(vsx));
+ memset(vmx, 0, sizeof(vmx));
+
+ load_vsx_vmx(fp_load_ckpt_new, vsx, vmx);
+
+ FAIL_IF(write_vsx_ckpt(child, vsx));
+ FAIL_IF(write_vmx_ckpt(child, vmx));
+
+ pptr[0] = 1;
+ pptr[1] = 1;
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+int ptrace_tm_spd_vsx(void)
+{
+ pid_t pid;
+ int ret, status, i;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT);
+
+ for (i = 0; i < 128; i++) {
+ fp_load[i] = 1 + rand();
+ fp_load_new[i] = 1 + 2 * rand();
+ fp_load_ckpt[i] = 1 + 3 * rand();
+ fp_load_ckpt_new[i] = 1 + 4 * rand();
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ tm_spd_vsx();
+
+ if (pid) {
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ while (!pptr[2])
+ asm volatile("" : : : "memory");
+
+ ret = trace_tm_spd_vsx(pid);
+ if (ret) {
+ kill(pid, SIGKILL);
+ shmdt((void *)pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ shmdt((void *)pptr);
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_spd_vsx, "ptrace_tm_spd_vsx");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
new file mode 100644
index 000000000..068bfed2e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test TM SPR registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "tm.h"
+
+/* Tracee and tracer shared data */
+struct shared {
+ int flag;
+ struct tm_spr_regs regs;
+};
+unsigned long tfhar;
+
+int shm_id;
+struct shared *cptr, *pptr;
+
+int shm_id1;
+int *cptr1, *pptr1;
+
+#define TM_KVM_SCHED 0xe0000001ac000001
+int validate_tm_spr(struct tm_spr_regs *regs)
+{
+ FAIL_IF(regs->tm_tfhar != tfhar);
+ FAIL_IF((regs->tm_texasr == TM_KVM_SCHED) && (regs->tm_tfiar != 0));
+
+ return TEST_PASS;
+}
+
+void tm_spr(void)
+{
+ unsigned long result, texasr;
+ int ret;
+
+ cptr = (struct shared *)shmat(shm_id, NULL, 0);
+ cptr1 = (int *)shmat(shm_id1, NULL, 0);
+
+trans:
+ cptr1[0] = 0;
+ asm __volatile__(
+ "1: ;"
+ /* TM failover handler should follow "tbegin.;" */
+ "mflr 31;"
+ "bl 4f;" /* $ = TFHAR - 12 */
+ "4: ;"
+ "mflr %[tfhar];"
+ "mtlr 31;"
+
+ "tbegin.;"
+ "beq 2f;"
+
+ "tsuspend.;"
+ "li 8, 1;"
+ "sth 8, 0(%[cptr1]);"
+ "tresume.;"
+ "b .;"
+
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ "2: ;"
+
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+ : [tfhar] "=r" (tfhar), [res] "=r" (result),
+ [texasr] "=r" (texasr), [cptr1] "=b" (cptr1)
+ : [sprn_texasr] "i" (SPRN_TEXASR)
+ : "memory", "r0", "r8", "r31"
+ );
+
+ /* There are 2 32bit instructions before tbegin. */
+ tfhar += 12;
+
+ if (result) {
+ if (!cptr->flag)
+ goto trans;
+
+ ret = validate_tm_spr((struct tm_spr_regs *)&cptr->regs);
+ shmdt((void *)cptr);
+ shmdt((void *)cptr1);
+ if (ret)
+ exit(1);
+ exit(0);
+ }
+ shmdt((void *)cptr);
+ shmdt((void *)cptr1);
+ exit(1);
+}
+
+int trace_tm_spr(pid_t child)
+{
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_tm_spr(child, (struct tm_spr_regs *)&pptr->regs));
+
+ printf("TFHAR: %lx TEXASR: %lx TFIAR: %lx\n", pptr->regs.tm_tfhar,
+ pptr->regs.tm_texasr, pptr->regs.tm_tfiar);
+
+ pptr->flag = 1;
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+int ptrace_tm_spr(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(struct shared), 0777|IPC_CREAT);
+ shm_id1 = shmget(IPC_PRIVATE, sizeof(int), 0777|IPC_CREAT);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ tm_spr();
+
+ if (pid) {
+ pptr = (struct shared *)shmat(shm_id, NULL, 0);
+ pptr1 = (int *)shmat(shm_id1, NULL, 0);
+
+ while (!pptr1[0])
+ asm volatile("" : : : "memory");
+ ret = trace_tm_spr(pid);
+ if (ret) {
+ kill(pid, SIGKILL);
+ shmdt((void *)pptr);
+ shmdt((void *)pptr1);
+ shmctl(shm_id, IPC_RMID, NULL);
+ shmctl(shm_id1, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ shmdt((void *)pptr);
+ shmdt((void *)pptr1);
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ shmctl(shm_id1, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_spr, "ptrace_tm_spr");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
new file mode 100644
index 000000000..46ef378a1
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for TAR, PPR, DSCR registers in the TM context
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "tm.h"
+#include "ptrace-tar.h"
+
+int shm_id;
+unsigned long *cptr, *pptr;
+
+
+void tm_tar(void)
+{
+ unsigned long result, texasr;
+ unsigned long regs[3];
+ int ret;
+
+ cptr = (unsigned long *)shmat(shm_id, NULL, 0);
+
+trans:
+ cptr[1] = 0;
+ asm __volatile__(
+ "li 4, %[tar_1];"
+ "mtspr %[sprn_tar], 4;" /* TAR_1 */
+ "li 4, %[dscr_1];"
+ "mtspr %[sprn_dscr], 4;" /* DSCR_1 */
+ "or 31,31,31;" /* PPR_1*/
+
+ "1: ;"
+ "tbegin.;"
+ "beq 2f;"
+
+ "li 4, %[tar_2];"
+ "mtspr %[sprn_tar], 4;" /* TAR_2 */
+ "li 4, %[dscr_2];"
+ "mtspr %[sprn_dscr], 4;" /* DSCR_2 */
+ "or 1,1,1;" /* PPR_2 */
+ "tsuspend.;"
+ "li 0, 1;"
+ "stw 0, 0(%[cptr1]);"
+ "tresume.;"
+ "b .;"
+
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ /* Transaction abort handler */
+ "2: ;"
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+
+ : [res] "=r" (result), [texasr] "=r" (texasr)
+ : [sprn_dscr]"i"(SPRN_DSCR), [sprn_tar]"i"(SPRN_TAR),
+ [sprn_ppr]"i"(SPRN_PPR), [sprn_texasr]"i"(SPRN_TEXASR),
+ [tar_1]"i"(TAR_1), [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2),
+ [dscr_2]"i"(DSCR_2), [cptr1] "b" (&cptr[1])
+ : "memory", "r0", "r3", "r4", "r5", "r6"
+ );
+
+ /* TM failed, analyse */
+ if (result) {
+ if (!cptr[0])
+ goto trans;
+
+ regs[0] = mfspr(SPRN_TAR);
+ regs[1] = mfspr(SPRN_PPR);
+ regs[2] = mfspr(SPRN_DSCR);
+
+ shmdt(&cptr);
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ user_read, regs[0], regs[1], regs[2]);
+
+ ret = validate_tar_registers(regs, TAR_4, PPR_4, DSCR_4);
+ if (ret)
+ exit(1);
+ exit(0);
+ }
+ shmdt(&cptr);
+ exit(1);
+}
+
+int trace_tm_tar(pid_t child)
+{
+ unsigned long regs[3];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_tar_registers(child, regs));
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ FAIL_IF(validate_tar_registers(regs, TAR_2, PPR_2, DSCR_2));
+ FAIL_IF(show_tm_checkpointed_state(child, regs));
+ printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n",
+ ptrace_read_ckpt, regs[0], regs[1], regs[2]);
+
+ FAIL_IF(validate_tar_registers(regs, TAR_1, PPR_1, DSCR_1));
+ FAIL_IF(write_ckpt_tar_registers(child, TAR_4, PPR_4, DSCR_4));
+ printf("%-30s TAR: %u PPR: %lx DSCR: %u\n",
+ ptrace_write_ckpt, TAR_4, PPR_4, DSCR_4);
+
+ pptr[0] = 1;
+ FAIL_IF(stop_trace(child));
+ return TEST_PASS;
+}
+
+int ptrace_tm_tar(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid == 0)
+ tm_tar();
+
+ pptr = (unsigned long *)shmat(shm_id, NULL, 0);
+ pptr[0] = 0;
+
+ if (pid) {
+ while (!pptr[1])
+ asm volatile("" : : : "memory");
+ ret = trace_tm_tar(pid);
+ if (ret) {
+ kill(pid, SIGTERM);
+ shmdt(&pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+ shmdt(&pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_tar, "ptrace_tm_tar");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
new file mode 100644
index 000000000..70ca01234
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for VMX/VSX registers in the TM context
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "tm.h"
+#include "ptrace-vsx.h"
+
+int shm_id;
+unsigned long *cptr, *pptr;
+
+unsigned long fp_load[VEC_MAX];
+unsigned long fp_store[VEC_MAX];
+unsigned long fp_load_ckpt[VEC_MAX];
+unsigned long fp_load_ckpt_new[VEC_MAX];
+
+__attribute__((used)) void load_vsx(void)
+{
+ loadvsx(fp_load, 0);
+}
+
+__attribute__((used)) void load_vsx_ckpt(void)
+{
+ loadvsx(fp_load_ckpt, 0);
+}
+
+void tm_vsx(void)
+{
+ unsigned long result, texasr;
+ int ret;
+
+ cptr = (unsigned long *)shmat(shm_id, NULL, 0);
+
+trans:
+ cptr[1] = 0;
+ asm __volatile__(
+ "bl load_vsx_ckpt;"
+
+ "1: ;"
+ "tbegin.;"
+ "beq 2f;"
+
+ "bl load_vsx;"
+ "tsuspend.;"
+ "li 7, 1;"
+ "stw 7, 0(%[cptr1]);"
+ "tresume.;"
+ "b .;"
+
+ "tend.;"
+ "li 0, 0;"
+ "ori %[res], 0, 0;"
+ "b 3f;"
+
+ "2: ;"
+ "li 0, 1;"
+ "ori %[res], 0, 0;"
+ "mfspr %[texasr], %[sprn_texasr];"
+
+ "3: ;"
+ : [res] "=r" (result), [texasr] "=r" (texasr)
+ : [sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "b" (&cptr[1])
+ : "memory", "r0", "r3", "r4",
+ "r7", "r8", "r9", "r10", "r11", "lr"
+ );
+
+ if (result) {
+ if (!cptr[0])
+ goto trans;
+
+ shmdt((void *)cptr);
+ storevsx(fp_store, 0);
+ ret = compare_vsx_vmx(fp_store, fp_load_ckpt_new);
+ if (ret)
+ exit(1);
+ exit(0);
+ }
+ shmdt((void *)cptr);
+ exit(1);
+}
+
+int trace_tm_vsx(pid_t child)
+{
+ unsigned long vsx[VSX_MAX];
+ unsigned long vmx[VMX_MAX + 2][2];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_vsx(child, vsx));
+ FAIL_IF(validate_vsx(vsx, fp_load));
+ FAIL_IF(show_vmx(child, vmx));
+ FAIL_IF(validate_vmx(vmx, fp_load));
+ FAIL_IF(show_vsx_ckpt(child, vsx));
+ FAIL_IF(validate_vsx(vsx, fp_load_ckpt));
+ FAIL_IF(show_vmx_ckpt(child, vmx));
+ FAIL_IF(validate_vmx(vmx, fp_load_ckpt));
+ memset(vsx, 0, sizeof(vsx));
+ memset(vmx, 0, sizeof(vmx));
+
+ load_vsx_vmx(fp_load_ckpt_new, vsx, vmx);
+
+ FAIL_IF(write_vsx_ckpt(child, vsx));
+ FAIL_IF(write_vmx_ckpt(child, vmx));
+ pptr[0] = 1;
+ FAIL_IF(stop_trace(child));
+ return TEST_PASS;
+}
+
+int ptrace_tm_vsx(void)
+{
+ pid_t pid;
+ int ret, status, i;
+
+ SKIP_IF(!have_htm());
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
+
+ for (i = 0; i < 128; i++) {
+ fp_load[i] = 1 + rand();
+ fp_load_ckpt[i] = 1 + 2 * rand();
+ fp_load_ckpt_new[i] = 1 + 3 * rand();
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ tm_vsx();
+
+ if (pid) {
+ pptr = (unsigned long *)shmat(shm_id, NULL, 0);
+ while (!pptr[1])
+ asm volatile("" : : : "memory");
+
+ ret = trace_tm_vsx(pid);
+ if (ret) {
+ kill(pid, SIGKILL);
+ shmdt((void *)pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ shmdt((void *)pptr);
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_tm_vsx, "ptrace_tm_vsx");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c
new file mode 100644
index 000000000..cb9875f76
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ptrace test for VMX/VSX registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include "ptrace.h"
+#include "ptrace-vsx.h"
+
+/* Tracer and Tracee Shared Data */
+int shm_id;
+int *cptr, *pptr;
+
+unsigned long fp_load[VEC_MAX];
+unsigned long fp_load_new[VEC_MAX];
+unsigned long fp_store[VEC_MAX];
+
+void vsx(void)
+{
+ int ret;
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+ loadvsx(fp_load, 0);
+ cptr[1] = 1;
+
+ while (!cptr[0])
+ asm volatile("" : : : "memory");
+ shmdt((void *) cptr);
+
+ storevsx(fp_store, 0);
+ ret = compare_vsx_vmx(fp_store, fp_load_new);
+ if (ret)
+ exit(1);
+ exit(0);
+}
+
+int trace_vsx(pid_t child)
+{
+ unsigned long vsx[VSX_MAX];
+ unsigned long vmx[VMX_MAX + 2][2];
+
+ FAIL_IF(start_trace(child));
+ FAIL_IF(show_vsx(child, vsx));
+ FAIL_IF(validate_vsx(vsx, fp_load));
+ FAIL_IF(show_vmx(child, vmx));
+ FAIL_IF(validate_vmx(vmx, fp_load));
+
+ memset(vsx, 0, sizeof(vsx));
+ memset(vmx, 0, sizeof(vmx));
+ load_vsx_vmx(fp_load_new, vsx, vmx);
+
+ FAIL_IF(write_vsx(child, vsx));
+ FAIL_IF(write_vmx(child, vmx));
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+int ptrace_vsx(void)
+{
+ pid_t pid;
+ int ret, status, i;
+
+ SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_VSX));
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
+
+ for (i = 0; i < VEC_MAX; i++)
+ fp_load[i] = i + rand();
+
+ for (i = 0; i < VEC_MAX; i++)
+ fp_load_new[i] = i + 2 * rand();
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ vsx();
+
+ if (pid) {
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ while (!pptr[1])
+ asm volatile("" : : : "memory");
+
+ ret = trace_vsx(pid);
+ if (ret) {
+ kill(pid, SIGTERM);
+ shmdt((void *)pptr);
+ shmctl(shm_id, IPC_RMID, NULL);
+ return TEST_FAIL;
+ }
+
+ pptr[0] = 1;
+ shmdt((void *)pptr);
+
+ ret = wait(&status);
+ shmctl(shm_id, IPC_RMID, NULL);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
+ TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_vsx, "ptrace_vsx");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h
new file mode 100644
index 000000000..663348521
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#define VEC_MAX 128
+#define VSX_MAX 32
+#define VMX_MAX 32
+
+/*
+ * unsigned long vsx[32]
+ * unsigned long load[128]
+ */
+int validate_vsx(unsigned long *vsx, unsigned long *load)
+{
+ int i;
+
+ for (i = 0; i < VSX_MAX; i++) {
+ if (vsx[i] != load[2 * i + 1]) {
+ printf("vsx[%d]: %lx load[%d] %lx\n",
+ i, vsx[i], 2 * i + 1, load[2 * i + 1]);
+ return TEST_FAIL;
+ }
+ }
+ return TEST_PASS;
+}
+
+/*
+ * unsigned long vmx[32][2]
+ * unsigned long load[128]
+ */
+int validate_vmx(unsigned long vmx[][2], unsigned long *load)
+{
+ int i;
+
+ for (i = 0; i < VMX_MAX; i++) {
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ if ((vmx[i][0] != load[64 + 2 * i]) ||
+ (vmx[i][1] != load[65 + 2 * i])) {
+ printf("vmx[%d][0]: %lx load[%d] %lx\n",
+ i, vmx[i][0], 64 + 2 * i,
+ load[64 + 2 * i]);
+ printf("vmx[%d][1]: %lx load[%d] %lx\n",
+ i, vmx[i][1], 65 + 2 * i,
+ load[65 + 2 * i]);
+ return TEST_FAIL;
+ }
+ #else /*
+ * In LE each value pair is stored in an
+ * alternate manner.
+ */
+ if ((vmx[i][0] != load[65 + 2 * i]) ||
+ (vmx[i][1] != load[64 + 2 * i])) {
+ printf("vmx[%d][0]: %lx load[%d] %lx\n",
+ i, vmx[i][0], 65 + 2 * i,
+ load[65 + 2 * i]);
+ printf("vmx[%d][1]: %lx load[%d] %lx\n",
+ i, vmx[i][1], 64 + 2 * i,
+ load[64 + 2 * i]);
+ return TEST_FAIL;
+ }
+ #endif
+ }
+ return TEST_PASS;
+}
+
+/*
+ * unsigned long store[128]
+ * unsigned long load[128]
+ */
+int compare_vsx_vmx(unsigned long *store, unsigned long *load)
+{
+ int i;
+
+ for (i = 0; i < VSX_MAX; i++) {
+ if (store[1 + 2 * i] != load[1 + 2 * i]) {
+ printf("store[%d]: %lx load[%d] %lx\n",
+ 1 + 2 * i, store[i],
+ 1 + 2 * i, load[i]);
+ return TEST_FAIL;
+ }
+ }
+
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ for (i = 64; i < VEC_MAX; i++) {
+ if (store[i] != load[i]) {
+ printf("store[%d]: %lx load[%d] %lx\n",
+ i, store[i], i, load[i]);
+ return TEST_FAIL;
+ }
+ }
+ #else /* In LE each value pair is stored in an alternate manner */
+ for (i = 64; i < VEC_MAX; i++) {
+ if (!(i % 2) && (store[i] != load[i+1])) {
+ printf("store[%d]: %lx load[%d] %lx\n",
+ i, store[i], i+1, load[i+1]);
+ return TEST_FAIL;
+ }
+ if ((i % 2) && (store[i] != load[i-1])) {
+ printf("here store[%d]: %lx load[%d] %lx\n",
+ i, store[i], i-1, load[i-1]);
+ return TEST_FAIL;
+ }
+ }
+ #endif
+ return TEST_PASS;
+}
+
+void load_vsx_vmx(unsigned long *load, unsigned long *vsx,
+ unsigned long vmx[][2])
+{
+ int i;
+
+ for (i = 0; i < VSX_MAX; i++)
+ vsx[i] = load[1 + 2 * i];
+
+ for (i = 0; i < VMX_MAX; i++) {
+ vmx[i][0] = load[64 + 2 * i];
+ vmx[i][1] = load[65 + 2 * i];
+ }
+}
+
+void loadvsx(void *p, int tmp);
+void storevsx(void *p, int tmp);
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
new file mode 100644
index 000000000..5181ad9b4
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -0,0 +1,745 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Ptrace interface test helper functions
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ */
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/ptrace.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/user.h>
+#include <linux/elf.h>
+#include <linux/types.h>
+#include <linux/auxvec.h>
+#include "reg.h"
+#include "utils.h"
+
+#define TEST_PASS 0
+#define TEST_FAIL 1
+
+struct fpr_regs {
+ unsigned long fpr[32];
+ unsigned long fpscr;
+};
+
+struct tm_spr_regs {
+ unsigned long tm_tfhar;
+ unsigned long tm_texasr;
+ unsigned long tm_tfiar;
+};
+
+#ifndef NT_PPC_TAR
+#define NT_PPC_TAR 0x103
+#define NT_PPC_PPR 0x104
+#define NT_PPC_DSCR 0x105
+#define NT_PPC_EBB 0x106
+#define NT_PPC_PMU 0x107
+#define NT_PPC_TM_CGPR 0x108
+#define NT_PPC_TM_CFPR 0x109
+#define NT_PPC_TM_CVMX 0x10a
+#define NT_PPC_TM_CVSX 0x10b
+#define NT_PPC_TM_SPR 0x10c
+#define NT_PPC_TM_CTAR 0x10d
+#define NT_PPC_TM_CPPR 0x10e
+#define NT_PPC_TM_CDSCR 0x10f
+#endif
+
+/* Basic ptrace operations */
+int start_trace(pid_t child)
+{
+ int ret;
+
+ ret = ptrace(PTRACE_ATTACH, child, NULL, NULL);
+ if (ret) {
+ perror("ptrace(PTRACE_ATTACH) failed");
+ return TEST_FAIL;
+ }
+ ret = waitpid(child, NULL, 0);
+ if (ret != child) {
+ perror("waitpid() failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int stop_trace(pid_t child)
+{
+ int ret;
+
+ ret = ptrace(PTRACE_DETACH, child, NULL, NULL);
+ if (ret) {
+ perror("ptrace(PTRACE_DETACH) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int cont_trace(pid_t child)
+{
+ int ret;
+
+ ret = ptrace(PTRACE_CONT, child, NULL, NULL);
+ if (ret) {
+ perror("ptrace(PTRACE_CONT) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
+ int n)
+{
+ struct iovec iov;
+ long ret;
+
+ FAIL_IF(start_trace(child));
+
+ iov.iov_base = regs;
+ iov.iov_len = n * sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
+ if (ret)
+ return ret;
+
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[],
+ int n)
+{
+ struct iovec iov;
+ long ret;
+
+ FAIL_IF(start_trace(child));
+
+ iov.iov_base = regs;
+ iov.iov_len = n * sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_SETREGSET, child, type, &iov);
+
+ FAIL_IF(stop_trace(child));
+
+ return ret;
+}
+
+/* TAR, PPR, DSCR */
+int show_tar_registers(pid_t child, unsigned long *out)
+{
+ struct iovec iov;
+ unsigned long *reg;
+ int ret;
+
+ reg = malloc(sizeof(unsigned long));
+ if (!reg) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+ iov.iov_base = (u64 *) reg;
+ iov.iov_len = sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TAR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+ if (out)
+ out[0] = *reg;
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_PPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+ if (out)
+ out[1] = *reg;
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_DSCR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+ if (out)
+ out[2] = *reg;
+
+ free(reg);
+ return TEST_PASS;
+fail:
+ free(reg);
+ return TEST_FAIL;
+}
+
+int write_tar_registers(pid_t child, unsigned long tar,
+ unsigned long ppr, unsigned long dscr)
+{
+ struct iovec iov;
+ unsigned long *reg;
+ int ret;
+
+ reg = malloc(sizeof(unsigned long));
+ if (!reg) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (u64 *) reg;
+ iov.iov_len = sizeof(unsigned long);
+
+ *reg = tar;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TAR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_SETREGSET) failed");
+ goto fail;
+ }
+
+ *reg = ppr;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_PPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_SETREGSET) failed");
+ goto fail;
+ }
+
+ *reg = dscr;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_DSCR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_SETREGSET) failed");
+ goto fail;
+ }
+
+ free(reg);
+ return TEST_PASS;
+fail:
+ free(reg);
+ return TEST_FAIL;
+}
+
+int show_tm_checkpointed_state(pid_t child, unsigned long *out)
+{
+ struct iovec iov;
+ unsigned long *reg;
+ int ret;
+
+ reg = malloc(sizeof(unsigned long));
+ if (!reg) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (u64 *) reg;
+ iov.iov_len = sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CTAR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+ if (out)
+ out[0] = *reg;
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CPPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+ if (out)
+ out[1] = *reg;
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CDSCR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+ if (out)
+ out[2] = *reg;
+
+ free(reg);
+ return TEST_PASS;
+
+fail:
+ free(reg);
+ return TEST_FAIL;
+}
+
+int write_ckpt_tar_registers(pid_t child, unsigned long tar,
+ unsigned long ppr, unsigned long dscr)
+{
+ struct iovec iov;
+ unsigned long *reg;
+ int ret;
+
+ reg = malloc(sizeof(unsigned long));
+ if (!reg) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (u64 *) reg;
+ iov.iov_len = sizeof(unsigned long);
+
+ *reg = tar;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CTAR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+
+ *reg = ppr;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CPPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+
+ *reg = dscr;
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CDSCR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+
+ free(reg);
+ return TEST_PASS;
+fail:
+ free(reg);
+ return TEST_FAIL;
+}
+
+/* FPR */
+int show_fpr(pid_t child, unsigned long *fpr)
+{
+ struct fpr_regs *regs;
+ int ret, i;
+
+ regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs));
+ ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ if (fpr) {
+ for (i = 0; i < 32; i++)
+ fpr[i] = regs->fpr[i];
+ }
+ return TEST_PASS;
+}
+
+int write_fpr(pid_t child, unsigned long val)
+{
+ struct fpr_regs *regs;
+ int ret, i;
+
+ regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs));
+ ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ for (i = 0; i < 32; i++)
+ regs->fpr[i] = val;
+
+ ret = ptrace(PTRACE_SETFPREGS, child, NULL, regs);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int show_ckpt_fpr(pid_t child, unsigned long *fpr)
+{
+ struct fpr_regs *regs;
+ struct iovec iov;
+ int ret, i;
+
+ regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs));
+ iov.iov_base = regs;
+ iov.iov_len = sizeof(struct fpr_regs);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ if (fpr) {
+ for (i = 0; i < 32; i++)
+ fpr[i] = regs->fpr[i];
+ }
+
+ return TEST_PASS;
+}
+
+int write_ckpt_fpr(pid_t child, unsigned long val)
+{
+ struct fpr_regs *regs;
+ struct iovec iov;
+ int ret, i;
+
+ regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs));
+ iov.iov_base = regs;
+ iov.iov_len = sizeof(struct fpr_regs);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ for (i = 0; i < 32; i++)
+ regs->fpr[i] = val;
+
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CFPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+/* GPR */
+int show_gpr(pid_t child, unsigned long *gpr)
+{
+ struct pt_regs *regs;
+ int ret, i;
+
+ regs = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ if (!regs) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ ret = ptrace(PTRACE_GETREGS, child, NULL, regs);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ if (gpr) {
+ for (i = 14; i < 32; i++)
+ gpr[i-14] = regs->gpr[i];
+ }
+
+ return TEST_PASS;
+}
+
+int write_gpr(pid_t child, unsigned long val)
+{
+ struct pt_regs *regs;
+ int i, ret;
+
+ regs = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ if (!regs) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ ret = ptrace(PTRACE_GETREGS, child, NULL, regs);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ for (i = 14; i < 32; i++)
+ regs->gpr[i] = val;
+
+ ret = ptrace(PTRACE_SETREGS, child, NULL, regs);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int show_ckpt_gpr(pid_t child, unsigned long *gpr)
+{
+ struct pt_regs *regs;
+ struct iovec iov;
+ int ret, i;
+
+ regs = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ if (!regs) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(struct pt_regs);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ if (gpr) {
+ for (i = 14; i < 32; i++)
+ gpr[i-14] = regs->gpr[i];
+ }
+
+ return TEST_PASS;
+}
+
+int write_ckpt_gpr(pid_t child, unsigned long val)
+{
+ struct pt_regs *regs;
+ struct iovec iov;
+ int ret, i;
+
+ regs = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ if (!regs) {
+ perror("malloc() failed\n");
+ return TEST_FAIL;
+ }
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(struct pt_regs);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ for (i = 14; i < 32; i++)
+ regs->gpr[i] = val;
+
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CGPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+/* VMX */
+int show_vmx(pid_t child, unsigned long vmx[][2])
+{
+ int ret;
+
+ ret = ptrace(PTRACE_GETVRREGS, child, 0, vmx);
+ if (ret) {
+ perror("ptrace(PTRACE_GETVRREGS) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int show_vmx_ckpt(pid_t child, unsigned long vmx[][2])
+{
+ unsigned long regs[34][2];
+ struct iovec iov;
+ int ret;
+
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CVMX, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET, NT_PPC_TM_CVMX) failed");
+ return TEST_FAIL;
+ }
+ memcpy(vmx, regs, sizeof(regs));
+ return TEST_PASS;
+}
+
+
+int write_vmx(pid_t child, unsigned long vmx[][2])
+{
+ int ret;
+
+ ret = ptrace(PTRACE_SETVRREGS, child, 0, vmx);
+ if (ret) {
+ perror("ptrace(PTRACE_SETVRREGS) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int write_vmx_ckpt(pid_t child, unsigned long vmx[][2])
+{
+ unsigned long regs[34][2];
+ struct iovec iov;
+ int ret;
+
+ memcpy(regs, vmx, sizeof(regs));
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(regs);
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CVMX, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_SETREGSET, NT_PPC_TM_CVMX) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+/* VSX */
+int show_vsx(pid_t child, unsigned long *vsx)
+{
+ int ret;
+
+ ret = ptrace(PTRACE_GETVSRREGS, child, 0, vsx);
+ if (ret) {
+ perror("ptrace(PTRACE_GETVSRREGS) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int show_vsx_ckpt(pid_t child, unsigned long *vsx)
+{
+ unsigned long regs[32];
+ struct iovec iov;
+ int ret;
+
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CVSX, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET, NT_PPC_TM_CVSX) failed");
+ return TEST_FAIL;
+ }
+ memcpy(vsx, regs, sizeof(regs));
+ return TEST_PASS;
+}
+
+int write_vsx(pid_t child, unsigned long *vsx)
+{
+ int ret;
+
+ ret = ptrace(PTRACE_SETVSRREGS, child, 0, vsx);
+ if (ret) {
+ perror("ptrace(PTRACE_SETVSRREGS) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+int write_vsx_ckpt(pid_t child, unsigned long *vsx)
+{
+ unsigned long regs[32];
+ struct iovec iov;
+ int ret;
+
+ memcpy(regs, vsx, sizeof(regs));
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(regs);
+ ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CVSX, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_SETREGSET, NT_PPC_TM_CVSX) failed");
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+}
+
+/* TM SPR */
+int show_tm_spr(pid_t child, struct tm_spr_regs *out)
+{
+ struct tm_spr_regs *regs;
+ struct iovec iov;
+ int ret;
+
+ regs = (struct tm_spr_regs *) malloc(sizeof(struct tm_spr_regs));
+ if (!regs) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (u64 *) regs;
+ iov.iov_len = sizeof(struct tm_spr_regs);
+
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_SPR, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ return TEST_FAIL;
+ }
+
+ if (out)
+ memcpy(out, regs, sizeof(struct tm_spr_regs));
+
+ return TEST_PASS;
+}
+
+
+
+/* Analyse TEXASR after TM failure */
+inline unsigned long get_tfiar(void)
+{
+ unsigned long ret;
+
+ asm volatile("mfspr %0,%1" : "=r" (ret) : "i" (SPRN_TFIAR));
+ return ret;
+}
+
+void analyse_texasr(unsigned long texasr)
+{
+ printf("TEXASR: %16lx\t", texasr);
+
+ if (texasr & TEXASR_FP)
+ printf("TEXASR_FP ");
+
+ if (texasr & TEXASR_DA)
+ printf("TEXASR_DA ");
+
+ if (texasr & TEXASR_NO)
+ printf("TEXASR_NO ");
+
+ if (texasr & TEXASR_FO)
+ printf("TEXASR_FO ");
+
+ if (texasr & TEXASR_SIC)
+ printf("TEXASR_SIC ");
+
+ if (texasr & TEXASR_NTC)
+ printf("TEXASR_NTC ");
+
+ if (texasr & TEXASR_TC)
+ printf("TEXASR_TC ");
+
+ if (texasr & TEXASR_TIC)
+ printf("TEXASR_TIC ");
+
+ if (texasr & TEXASR_IC)
+ printf("TEXASR_IC ");
+
+ if (texasr & TEXASR_IFC)
+ printf("TEXASR_IFC ");
+
+ if (texasr & TEXASR_ABT)
+ printf("TEXASR_ABT ");
+
+ if (texasr & TEXASR_SPD)
+ printf("TEXASR_SPD ");
+
+ if (texasr & TEXASR_HV)
+ printf("TEXASR_HV ");
+
+ if (texasr & TEXASR_PR)
+ printf("TEXASR_PR ");
+
+ if (texasr & TEXASR_FS)
+ printf("TEXASR_FS ");
+
+ if (texasr & TEXASR_TE)
+ printf("TEXASR_TE ");
+
+ if (texasr & TEXASR_ROT)
+ printf("TEXASR_ROT ");
+
+ printf("TFIAR :%lx\n", get_tfiar());
+}
+
+void store_gpr(unsigned long *addr);
+void store_fpr(float *addr);