Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,17 @@
# 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
ptrace-perf-hwbreak
@@ -0,0 +1,43 @@
# SPDX-License-Identifier: GPL-2.0
TM_TESTS := ptrace-tm-gpr
TM_TESTS += ptrace-tm-spd-gpr
TM_TESTS += ptrace-tm-spd-tar
TM_TESTS += ptrace-tm-spd-vsx
TM_TESTS += ptrace-tm-spr
TM_TESTS += ptrace-tm-tar
TM_TESTS += ptrace-tm-vsx
TESTS_64 := $(TM_TESTS)
TESTS_64 += core-pkey
TESTS_64 += perf-hwbreak
TESTS_64 += ptrace-hwbreak
TESTS_64 += ptrace-perf-hwbreak
TESTS_64 += ptrace-pkey
TESTS_64 += ptrace-syscall
TESTS_64 += ptrace-tar
TESTS_64 += ptrace-vsx
TESTS += ptrace-gpr
TEST_GEN_PROGS := $(TESTS) $(TESTS_64)
LOCAL_HDRS += $(patsubst %,$(selfdir)/powerpc/ptrace/%,$(wildcard *.h))
top_srcdir = ../../../../..
include ../../lib.mk
include ../flags.mk
TM_TESTS := $(patsubst %,$(OUTPUT)/%,$(TM_TESTS))
TESTS_64 := $(patsubst %,$(OUTPUT)/%,$(TESTS_64))
$(TESTS_64): CFLAGS += -m64
$(TM_TESTS): CFLAGS += -I../tm -mhtm
CFLAGS += $(KHDR_INCLUDES) -fno-pie
$(OUTPUT)/ptrace-gpr: ptrace-gpr.S
$(OUTPUT)/ptrace-perf-hwbreak: ptrace-perf-asm.S
$(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: LDLIBS += -pthread
$(TEST_GEN_PROGS): ../harness.c ../utils.c ../lib/reg.S
@@ -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, msg) \
do { \
if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \
(sync)->parent_gave_up = true; \
prod_child(sync); \
SKIP_IF_MSG(1, msg); \
} \
} 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;
}
@@ -0,0 +1,423 @@
// 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"
#include "pkeys.h"
#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 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, PKEY_UNRESTRICTED);
FAIL_IF(pkey1 < 0);
disable_execute = false;
}
pkey2 = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
FAIL_IF(pkey2 < 0);
pkey3 = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
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, "PKEYs not supported");
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 mmapping 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)
{
int err;
err = write_file(core_pattern_file, core_pattern, strlen(core_pattern));
if (err) {
SKIP_IF_MSG(err == -EPERM, "Try with root privileges");
perror("Error writing to core_pattern file");
return TEST_FAIL;
}
return TEST_PASS;
}
static int setup_core_pattern(char **core_pattern_, bool *changed_)
{
char *core_pattern;
size_t len;
int ret;
core_pattern = malloc(PATH_MAX);
if (!core_pattern) {
perror("Error allocating memory");
return TEST_FAIL;
}
ret = read_file(core_pattern_file, core_pattern, PATH_MAX - 1, &len);
if (ret) {
perror("Error reading core_pattern file");
ret = TEST_FAIL;
goto out;
}
core_pattern[len] = '\0';
/* 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");
}
@@ -0,0 +1,895 @@
// 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.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <assert.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/sysinfo.h>
#include <asm/ptrace.h>
#include <elf.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include "utils.h"
#ifndef PPC_DEBUG_FEATURE_DATA_BP_ARCH_31
#define PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 0x20
#endif
#define MAX_LOOPS 10000
#define DAWR_LENGTH_MAX ((0x3f + 1) * 8)
int nprocs;
static volatile int a = 10;
static volatile int b = 10;
static volatile char c[512 + 8] __attribute__((aligned(512)));
static void perf_event_attr_set(struct perf_event_attr *attr,
__u32 type, __u64 addr, __u64 len,
bool exclude_user)
{
memset(attr, 0, sizeof(struct perf_event_attr));
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(struct perf_event_attr);
attr->bp_type = type;
attr->bp_addr = addr;
attr->bp_len = len;
attr->exclude_kernel = 1;
attr->exclude_hv = 1;
attr->exclude_guest = 1;
attr->exclude_user = exclude_user;
attr->disabled = 1;
}
static int
perf_process_event_open_exclude_user(__u32 type, __u64 addr, __u64 len, bool exclude_user)
{
struct perf_event_attr attr;
perf_event_attr_set(&attr, type, addr, len, exclude_user);
return syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
}
static int perf_process_event_open(__u32 type, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_event_attr_set(&attr, type, addr, len, 0);
return syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
}
static int perf_cpu_event_open(long cpu, __u32 type, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_event_attr_set(&attr, type, addr, len, 0);
return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
}
static void close_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
close(fd[i]);
}
static unsigned long read_fds(int *fd, int n)
{
int i;
unsigned long c = 0;
unsigned long count = 0;
size_t res;
for (i = 0; i < n; i++) {
res = read(fd[i], &c, sizeof(c));
assert(res == sizeof(unsigned long long));
count += c;
}
return count;
}
static void reset_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
ioctl(fd[i], PERF_EVENT_IOC_RESET);
}
static void enable_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
ioctl(fd[i], PERF_EVENT_IOC_ENABLE);
}
static void disable_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
ioctl(fd[i], PERF_EVENT_IOC_DISABLE);
}
static int perf_systemwide_event_open(int *fd, __u32 type, __u64 addr, __u64 len)
{
int i, ncpus, cpu, ret = 0;
struct rlimit rlim;
cpu_set_t *mask;
size_t size;
if (getrlimit(RLIMIT_NOFILE, &rlim)) {
perror("getrlimit");
return -1;
}
rlim.rlim_cur = 65536;
if (setrlimit(RLIMIT_NOFILE, &rlim)) {
perror("setrlimit");
return -1;
}
ncpus = get_nprocs_conf();
size = CPU_ALLOC_SIZE(ncpus);
mask = CPU_ALLOC(ncpus);
if (!mask) {
perror("malloc");
return -1;
}
CPU_ZERO_S(size, mask);
if (sched_getaffinity(0, size, mask)) {
perror("sched_getaffinity");
ret = -1;
goto done;
}
for (i = 0, cpu = 0; i < nprocs && cpu < ncpus; cpu++) {
if (!CPU_ISSET_S(cpu, size, mask))
continue;
fd[i] = perf_cpu_event_open(cpu, type, addr, len);
if (fd[i] < 0) {
perror("perf_systemwide_event_open");
close_fds(fd, i);
ret = fd[i];
goto done;
}
i++;
}
if (i < nprocs) {
printf("Error: Number of online cpus reduced since start of test: %d < %d\n", i, nprocs);
close_fds(fd, i);
ret = -1;
}
done:
CPU_FREE(mask);
return ret;
}
static inline bool breakpoint_test(int len)
{
int fd;
/* bp_addr can point anywhere but needs to be aligned */
fd = perf_process_event_open(HW_BREAKPOINT_R, (__u64)(&fd) & 0xfffffffffffff800, len);
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;
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;
__u64 len;
/* align to 0x400 boundary as required by DAWR */
readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) &
0xfffffffffffff800);
ptr = &readint;
if (arraytest)
ptr = &readintalign[0];
len = arraytest ? DAWR_LENGTH_MAX : sizeof(int);
break_fd = perf_process_event_open_exclude_user(readwriteflag, (__u64)ptr,
len, exclude_user);
if (break_fd < 0) {
perror("perf_process_event_open_exclude_user");
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;
int break_fd;
unsigned long long breaks;
int fail = 0;
size_t res;
target = malloc(8);
if (!target) {
perror("malloc failed");
exit(EXIT_FAILURE);
}
/* watch middle half of target array */
break_fd = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)(target + 2), 4);
if (break_fd < 0) {
free(target);
perror("perf_process_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 void multi_dawr_workload(void)
{
a += 10;
b += 10;
c[512 + 1] += 'a';
}
static int test_process_multi_diff_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, diff addr";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&b, (__u64)sizeof(b));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_process_multi_same_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, same addr";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_process_multi_diff_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, diff addr, one is RO, other is WO";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_R, (__u64)&b, (__u64)sizeof(b));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_process_multi_same_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, same addr, one is RO, other is WO";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_R, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_diff_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, diff addr";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (ret)
exit(EXIT_FAILURE);
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_RW, (__u64)&b, (__u64)sizeof(b));
if (ret) {
close_fds(fd1, nprocs);
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_same_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, same addr";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (ret)
exit(EXIT_FAILURE);
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (ret) {
close_fds(fd1, nprocs);
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_diff_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, diff addr, one is RO, other is WO";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (ret)
exit(EXIT_FAILURE);
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_R, (__u64)&b, (__u64)sizeof(b));
if (ret) {
close_fds(fd1, nprocs);
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_same_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, same addr, one is RO, other is WO";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (ret)
exit(EXIT_FAILURE);
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_R, (__u64)&a, (__u64)sizeof(a));
if (ret) {
close_fds(fd1, nprocs);
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int runtest_multi_dawr(void)
{
int ret = 0;
ret |= test_process_multi_diff_addr();
ret |= test_process_multi_same_addr();
ret |= test_process_multi_diff_addr_ro_wo();
ret |= test_process_multi_same_addr_ro_wo();
ret |= test_syswide_multi_diff_addr();
ret |= test_syswide_multi_same_addr();
ret |= test_syswide_multi_diff_addr_ro_wo();
ret |= test_syswide_multi_same_addr_ro_wo();
return ret;
}
static int runtest_unaligned_512bytes(void)
{
unsigned long long breaks = 0;
int fd;
char *desc = "Process specific, 512 bytes, unaligned";
__u64 addr = (__u64)&c + 8;
size_t res;
fd = perf_process_event_open(HW_BREAKPOINT_RW, addr, 512);
if (fd < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd, PERF_EVENT_IOC_RESET);
ioctl(fd, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd, PERF_EVENT_IOC_DISABLE);
res = read(fd, &breaks, sizeof(breaks));
assert(res == sizeof(unsigned long long));
close(fd);
if (breaks != 2) {
printf("FAILED: %s: %lld != 2\n", desc, breaks);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
/* There is no perf api to find number of available watchpoints. Use ptrace. */
static int get_nr_wps(bool *arch_31)
{
struct ppc_debug_info dbginfo;
int child_pid;
child_pid = fork();
if (!child_pid) {
int ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
if (ret) {
perror("PTRACE_TRACEME failed\n");
exit(EXIT_FAILURE);
}
kill(getpid(), SIGUSR1);
sleep(1);
exit(EXIT_SUCCESS);
}
wait(NULL);
if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo)) {
perror("Can't get breakpoint info");
exit(EXIT_FAILURE);
}
*arch_31 = !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_ARCH_31);
return dbginfo.num_data_bps;
}
static int runtest(void)
{
int rwflag;
int exclude_user;
int ret;
bool dawr = dawr_supported();
bool arch_31 = false;
int nr_wps = get_nr_wps(&arch_31);
/*
* 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)
continue;
ret = runtestsingle(rwflag, exclude_user, 1);
if (ret)
return ret;
}
}
ret = runtest_dar_outside();
if (ret)
return ret;
if (dawr && nr_wps > 1) {
nprocs = get_nprocs();
ret = runtest_multi_dawr();
if (ret)
return ret;
}
if (dawr && arch_31)
ret = runtest_unaligned_512bytes();
return ret;
}
static int perf_hwbreak(void)
{
srand ( time(NULL) );
SKIP_IF_MSG(!perf_breakpoint_supported(), "Perf breakpoints not supported");
return runtest();
}
int main(int argc, char *argv[], char **envp)
{
return test_harness(perf_hwbreak, "perf_hwbreak");
}
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* test helper assembly functions
*
* Copyright (C) 2016 Simon Guo, IBM Corporation.
* Copyright 2022 Michael Ellerman, IBM Corporation.
*/
#include "basic_asm.h"
#define GPR_SIZE __SIZEOF_LONG__
#define FIRST_GPR 14
#define NUM_GPRS (32 - FIRST_GPR)
#define STACK_SIZE (NUM_GPRS * GPR_SIZE)
// gpr_child_loop(int *read_flag, int *write_flag,
// unsigned long *gpr_buf, double *fpr_buf);
FUNC_START(gpr_child_loop)
// r3 = read_flag
// r4 = write_flag
// r5 = gpr_buf
// r6 = fpr_buf
PUSH_BASIC_STACK(STACK_SIZE)
// Save non-volatile GPRs
OP_REGS PPC_STL, GPR_SIZE, FIRST_GPR, 31, %r1, STACK_FRAME_LOCAL(0, 0), FIRST_GPR
// Load GPRs with expected values
OP_REGS PPC_LL, GPR_SIZE, FIRST_GPR, 31, r5, 0, FIRST_GPR
// Load FPRs with expected values
OP_REGS lfd, 8, 0, 31, r6
// Signal to parent that we're ready
li r0, 1
stw r0, 0(r4)
// Wait for parent to finish
1: lwz r0, 0(r3)
cmpwi r0, 0
beq 1b // Loop while flag is zero
// Save GPRs back to caller buffer
OP_REGS PPC_STL, GPR_SIZE, FIRST_GPR, 31, r5, 0, FIRST_GPR
// Save FPRs
OP_REGS stfd, 8, 0, 31, r6
// Reload non-volatile GPRs
OP_REGS PPC_LL, GPR_SIZE, FIRST_GPR, 31, %r1, STACK_FRAME_LOCAL(0, 0), FIRST_GPR
POP_BASIC_STACK(STACK_SIZE)
blr
@@ -0,0 +1,174 @@
// 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"
#include <time.h>
/* Tracer and Tracee Shared Data */
int shm_id;
int *cptr, *pptr;
extern void gpr_child_loop(int *read_flag, int *write_flag,
unsigned long *gpr_buf, double *fpr_buf);
unsigned long child_gpr_val, parent_gpr_val;
double child_fpr_val, parent_fpr_val;
static int child(void)
{
unsigned long gpr_buf[32];
double fpr_buf[32];
int i;
cptr = (int *)shmat(shm_id, NULL, 0);
memset(gpr_buf, 0, sizeof(gpr_buf));
memset(fpr_buf, 0, sizeof(fpr_buf));
for (i = 0; i < 32; i++) {
gpr_buf[i] = child_gpr_val;
fpr_buf[i] = child_fpr_val;
}
gpr_child_loop(&cptr[0], &cptr[1], gpr_buf, fpr_buf);
shmdt((void *)cptr);
FAIL_IF(validate_gpr(gpr_buf, parent_gpr_val));
FAIL_IF(validate_fpr_double(fpr_buf, parent_fpr_val));
return 0;
}
int trace_gpr(pid_t child)
{
__u64 tmp, fpr[32], *peeked_fprs;
unsigned long gpr[18];
FAIL_IF(start_trace(child));
// Check child GPRs match what we expect using GETREGS
FAIL_IF(show_gpr(child, gpr));
FAIL_IF(validate_gpr(gpr, child_gpr_val));
// Check child FPRs match what we expect using GETFPREGS
FAIL_IF(show_fpr(child, fpr));
memcpy(&tmp, &child_fpr_val, sizeof(tmp));
FAIL_IF(validate_fpr(fpr, tmp));
// Check child FPRs match what we expect using PEEKUSR
peeked_fprs = peek_fprs(child);
FAIL_IF(!peeked_fprs);
FAIL_IF(validate_fpr(peeked_fprs, tmp));
free(peeked_fprs);
// Write child GPRs using SETREGS
FAIL_IF(write_gpr(child, parent_gpr_val));
// Write child FPRs using SETFPREGS
memcpy(&tmp, &parent_fpr_val, sizeof(tmp));
FAIL_IF(write_fpr(child, tmp));
// Check child FPRs match what we just set, using PEEKUSR
peeked_fprs = peek_fprs(child);
FAIL_IF(!peeked_fprs);
FAIL_IF(validate_fpr(peeked_fprs, tmp));
// Write child FPRs using POKEUSR
FAIL_IF(poke_fprs(child, (unsigned long *)peeked_fprs));
// Child will check its FPRs match before exiting
FAIL_IF(stop_trace(child));
return TEST_PASS;
}
#ifndef __LONG_WIDTH__
#define __LONG_WIDTH__ (sizeof(long) * 8)
#endif
static uint64_t rand_reg(void)
{
uint64_t result;
long r;
r = random();
// Small values are typical
result = r & 0xffff;
if (r & 0x10000)
return result;
// Pointers tend to have high bits set
result |= random() << (__LONG_WIDTH__ - 31);
if (r & 0x100000)
return result;
// And sometimes we want a full 64-bit value
result ^= random() << 16;
return result;
}
int ptrace_gpr(void)
{
unsigned long seed;
int ret, status;
pid_t pid;
seed = getpid() ^ time(NULL);
printf("srand(%lu)\n", seed);
srand(seed);
child_gpr_val = rand_reg();
child_fpr_val = rand_reg();
parent_gpr_val = rand_reg();
parent_fpr_val = rand_reg();
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)
exit(child());
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");
}
@@ -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 0x3f50624dd2f1a9fcull
#define FPR_2_REP 0x3f60624dd2f1a9fcull
#define FPR_3_REP 0x3f689374bc6a7efaull
#define FPR_4_REP 0x3f70624dd2f1a9fcull
/* 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(__u64 *fpr, __u64 val)
{
int i, found = 1;
for (i = 0; i < 32; i++) {
if (fpr[i] != val) {
printf("FPR[%d]: %llx Expected: %llx\n", i, fpr[i], val);
found = 0;
}
}
if (!found)
return TEST_FAIL;
return TEST_PASS;
}
/* Buffer must have 32 elements */
int validate_fpr_double(double *fpr, double 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;
}
@@ -0,0 +1,627 @@
// 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"
#include "reg.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)
{
volatile __u8 *pcvar;
volatile __u16 *psvar;
volatile __u32 *pivar;
volatile __u64 *plvar;
switch (len) {
case 1:
pcvar = (volatile __u8 *)&glvar;
*pcvar = 0xff;
break;
case 2:
psvar = (volatile __u16 *)&glvar;
*psvar = 0xffff;
break;
case 4:
pivar = (volatile __u32 *)&glvar;
*pivar = 0xffffffff;
break;
case 8:
plvar = (volatile __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 = (volatile __u8)glvar;
break;
case 2:
svar = (volatile __u16)glvar;
break;
case 4:
ivar = (volatile __u32)glvar;
break;
case 8:
lvar = (volatile __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];
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
gstruct.a[rand() % A_LEN] = 'a';
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
cvar = gstruct.b[rand() % B_LEN];
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
gstruct.a[rand() % A_LEN] = 'a';
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
cvar = gstruct.a[rand() % A_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_multi_sethwdebug_range(pid_t child_pid)
{
struct ppc_hw_breakpoint info1, info2;
unsigned long wp_addr1, wp_addr2;
char *name1 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED";
char *name2 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED";
int len1, len2;
int wh1, wh2;
wp_addr1 = (unsigned long)&gstruct.a;
wp_addr2 = (unsigned long)&gstruct.b;
len1 = A_LEN;
len2 = B_LEN;
get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
wh1 = ptrace_sethwdebug(child_pid, &info1);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
wh2 = ptrace_sethwdebug(child_pid, &info2);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name1, "WO", wp_addr1, len1);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name2, "RO", wp_addr2, len2);
ptrace_delhwdebug(child_pid, wh1);
ptrace_delhwdebug(child_pid, wh2);
}
static void test_multi_sethwdebug_range_dawr_overlap(pid_t child_pid)
{
struct ppc_hw_breakpoint info1, info2;
unsigned long wp_addr1, wp_addr2;
char *name = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap";
int len1, len2;
int wh1, wh2;
wp_addr1 = (unsigned long)&gstruct.a;
wp_addr2 = (unsigned long)&gstruct.a;
len1 = A_LEN;
len2 = A_LEN;
get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
wh1 = ptrace_sethwdebug(child_pid, &info1);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
wh2 = ptrace_sethwdebug(child_pid, &info2);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "WO", wp_addr1, len1);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RO", wp_addr2, len2);
ptrace_delhwdebug(child_pid, wh1);
ptrace_delhwdebug(child_pid, wh2);
}
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);
if (dbginfo->num_data_bps > 1) {
test_multi_sethwdebug_range(child_pid);
test_multi_sethwdebug_range_dawr_overlap(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_MSG(dbginfo.num_data_bps == 0, "No data breakpoints present");
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)
{
is_8xx = mfspr(SPRN_PVR) == PVR_8xx;
return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
}
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <ppc-asm.h>
.global same_watch_addr_load
.global same_watch_addr_trap
FUNC_START(same_watch_addr_child)
nop
same_watch_addr_load:
ld 0,0(3)
nop
same_watch_addr_trap:
trap
blr
FUNC_END(same_watch_addr_child)
.global perf_then_ptrace_load1
.global perf_then_ptrace_load2
.global perf_then_ptrace_trap
FUNC_START(perf_then_ptrace_child)
nop
perf_then_ptrace_load1:
ld 0,0(3)
perf_then_ptrace_load2:
ld 0,0(4)
nop
perf_then_ptrace_trap:
trap
blr
FUNC_END(perf_then_ptrace_child)
@@ -0,0 +1,445 @@
// SPDX-License-Identifier: GPL-2.0+
#include <asm/unistd.h>
#include <linux/hw_breakpoint.h>
#include <linux/ptrace.h>
#include <memory.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "utils.h"
/*
* Child subroutine that performs a load on the address, then traps
*/
void same_watch_addr_child(unsigned long *addr);
/* Address of the ld instruction in same_watch_addr_child() */
extern char same_watch_addr_load[];
/* Address of the end trap instruction in same_watch_addr_child() */
extern char same_watch_addr_trap[];
/*
* Child subroutine that performs a load on the first address, then a load on
* the second address (with no instructions separating this from the first
* load), then traps.
*/
void perf_then_ptrace_child(unsigned long *first_addr, unsigned long *second_addr);
/* Address of the first ld instruction in perf_then_ptrace_child() */
extern char perf_then_ptrace_load1[];
/* Address of the second ld instruction in perf_then_ptrace_child() */
extern char perf_then_ptrace_load2[];
/* Address of the end trap instruction in perf_then_ptrace_child() */
extern char perf_then_ptrace_trap[];
static inline long sys_ptrace(long request, pid_t pid, unsigned long addr, unsigned long data)
{
return syscall(__NR_ptrace, request, pid, addr, data);
}
static long ptrace_traceme(void)
{
return sys_ptrace(PTRACE_TRACEME, 0, 0, 0);
}
static long ptrace_getregs(pid_t pid, struct pt_regs *result)
{
return sys_ptrace(PTRACE_GETREGS, pid, 0, (unsigned long)result);
}
static long ptrace_setregs(pid_t pid, struct pt_regs *result)
{
return sys_ptrace(PTRACE_SETREGS, pid, 0, (unsigned long)result);
}
static long ptrace_cont(pid_t pid, long signal)
{
return sys_ptrace(PTRACE_CONT, pid, 0, signal);
}
static long ptrace_singlestep(pid_t pid, long signal)
{
return sys_ptrace(PTRACE_SINGLESTEP, pid, 0, signal);
}
static long ppc_ptrace_gethwdbginfo(pid_t pid, struct ppc_debug_info *dbginfo)
{
return sys_ptrace(PPC_PTRACE_GETHWDBGINFO, pid, 0, (unsigned long)dbginfo);
}
static long ppc_ptrace_sethwdbg(pid_t pid, struct ppc_hw_breakpoint *bp_info)
{
return sys_ptrace(PPC_PTRACE_SETHWDEBUG, pid, 0, (unsigned long)bp_info);
}
static long ppc_ptrace_delhwdbg(pid_t pid, int bp_id)
{
return sys_ptrace(PPC_PTRACE_DELHWDEBUG, pid, 0L, bp_id);
}
static long ptrace_getreg_pc(pid_t pid, void **pc)
{
struct pt_regs regs;
long err;
err = ptrace_getregs(pid, &regs);
if (err)
return err;
*pc = (void *)regs.nip;
return 0;
}
static long ptrace_setreg_pc(pid_t pid, void *pc)
{
struct pt_regs regs;
long err;
err = ptrace_getregs(pid, &regs);
if (err)
return err;
regs.nip = (unsigned long)pc;
err = ptrace_setregs(pid, &regs);
if (err)
return err;
return 0;
}
static int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
}
static void perf_user_event_attr_set(struct perf_event_attr *attr, void *addr, u64 len)
{
memset(attr, 0, sizeof(struct perf_event_attr));
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(struct perf_event_attr);
attr->bp_type = HW_BREAKPOINT_R;
attr->bp_addr = (u64)addr;
attr->bp_len = len;
attr->exclude_kernel = 1;
attr->exclude_hv = 1;
}
static int perf_watchpoint_open(pid_t child_pid, void *addr, u64 len)
{
struct perf_event_attr attr;
perf_user_event_attr_set(&attr, addr, len);
return perf_event_open(&attr, child_pid, -1, -1, 0);
}
static int perf_read_counter(int perf_fd, u64 *count)
{
/*
* A perf counter is retrieved by the read() syscall. It contains
* the current count as 8 bytes that are interpreted as a u64
*/
ssize_t len = read(perf_fd, count, sizeof(*count));
if (len != sizeof(*count))
return -1;
return 0;
}
static void ppc_ptrace_init_breakpoint(struct ppc_hw_breakpoint *info,
int type, void *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;
}
/*
* Checks if we can place at least 2 watchpoints on the child process
*/
static int check_watchpoints(pid_t pid)
{
struct ppc_debug_info dbginfo;
FAIL_IF_MSG(ppc_ptrace_gethwdbginfo(pid, &dbginfo), "PPC_PTRACE_GETHWDBGINFO failed");
SKIP_IF_MSG(dbginfo.num_data_bps <= 1, "Not enough data watchpoints (need at least 2)");
return 0;
}
/*
* Wrapper around a plain fork() call that sets up the child for
* ptrace-ing. Both the parent and child return from this, though
* the child is stopped until ptrace_cont(pid) is run by the parent.
*/
static int ptrace_fork_child(pid_t *pid)
{
int status;
*pid = fork();
if (*pid < 0)
FAIL_IF_MSG(1, "Failed to fork child");
if (!*pid) {
FAIL_IF_EXIT_MSG(ptrace_traceme(), "PTRACE_TRACEME failed");
FAIL_IF_EXIT_MSG(raise(SIGSTOP), "Child failed to raise SIGSTOP");
} else {
/* Synchronise on child SIGSTOP */
FAIL_IF_MSG(waitpid(*pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
}
return 0;
}
/*
* Tests the interaction between ptrace and perf watching the same data.
*
* We expect ptrace to take 'priority', as it is has before-execute
* semantics.
*
* The perf counter should not be incremented yet because perf has after-execute
* semantics. E.g., if ptrace changes the child PC, we don't even execute the
* instruction at all.
*
* When the child is stopped for ptrace, we test both continue and single step.
* Both should increment the perf counter. We also test changing the PC somewhere
* different and stepping, which should not increment the perf counter.
*/
int same_watch_addr_test(void)
{
struct ppc_hw_breakpoint bp_info; /* ptrace breakpoint info */
int bp_id; /* Breakpoint handle of ptrace watchpoint */
int perf_fd; /* File descriptor of perf performance counter */
u64 perf_count; /* Most recently fetched perf performance counter value */
pid_t pid; /* PID of child process */
void *pc; /* Most recently fetched child PC value */
int status; /* Stop status of child after waitpid */
unsigned long value; /* Dummy value to be read/written to by child */
int err;
err = ptrace_fork_child(&pid);
if (err)
return err;
if (!pid) {
same_watch_addr_child(&value);
exit(1);
}
err = check_watchpoints(pid);
if (err)
return err;
/* Place a perf watchpoint counter on value */
perf_fd = perf_watchpoint_open(pid, &value, sizeof(value));
FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter");
/* Place a ptrace watchpoint on value */
ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, &value, sizeof(value));
bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
/* Let the child run. It should stop on the ptrace watchpoint */
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction");
/*
* We stopped before executing the load, so perf should not have
* recorded any events yet
*/
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 0, "perf recorded unexpected event");
/* Single stepping over the load should increment the perf counter */
FAIL_IF_MSG(ptrace_singlestep(pid, 0), "Failed to single step child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_load + 4, "Failed to single step load instruction");
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 1, "perf counter did not increment");
/*
* Set up a ptrace watchpoint on the value again and trigger it.
* The perf counter should not have incremented because we do not
* execute the load yet.
*/
FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint");
bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load trap");
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 1, "perf counter should not have changed");
/* Continuing over the load should increment the perf counter */
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 2, "perf counter did not increment");
/*
* If we set the child PC back to the load instruction, then continue,
* we should reach the end trap (because ptrace is one-shot) and have
* another perf event.
*/
FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 3, "perf counter did not increment");
/*
* If we set the child PC back to the load instruction, set a ptrace
* watchpoint on the load, then continue, we should immediately get
* the ptrace trap without incrementing the perf counter
*/
FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint");
bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction");
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed");
/*
* If we change the PC while stopped on the load instruction, we should
* not increment the perf counter (because ptrace is before-execute,
* perf is after-execute).
*/
FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load + 4), "Failed to set child PC");
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed");
/* Clean up child */
FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child");
return 0;
}
/*
* Tests the interaction between ptrace and perf when:
* 1. perf watches a value
* 2. ptrace watches a different value
* 3. The perf value is read, then the ptrace value is read immediately after
*
* A breakpoint implementation may accidentally misattribute/skip one of
* the ptrace or perf handlers, as interrupt based work is done after perf
* and before ptrace.
*
* We expect the perf counter to increment before the ptrace watchpoint
* triggers.
*/
int perf_then_ptrace_test(void)
{
struct ppc_hw_breakpoint bp_info; /* ptrace breakpoint info */
int bp_id; /* Breakpoint handle of ptrace watchpoint */
int perf_fd; /* File descriptor of perf performance counter */
u64 perf_count; /* Most recently fetched perf performance counter value */
pid_t pid; /* PID of child process */
void *pc; /* Most recently fetched child PC value */
int status; /* Stop status of child after waitpid */
unsigned long perf_value; /* Dummy value to be watched by perf */
unsigned long ptrace_value; /* Dummy value to be watched by ptrace */
int err;
err = ptrace_fork_child(&pid);
if (err)
return err;
/*
* If we are the child, run a subroutine that reads the perf value,
* then reads the ptrace value with consecutive load instructions
*/
if (!pid) {
perf_then_ptrace_child(&perf_value, &ptrace_value);
exit(0);
}
err = check_watchpoints(pid);
if (err)
return err;
/* Place a perf watchpoint counter */
perf_fd = perf_watchpoint_open(pid, &perf_value, sizeof(perf_value));
FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter");
/* Place a ptrace watchpoint */
ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ,
&ptrace_value, sizeof(ptrace_value));
bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
/* Let the child run. It should stop on the ptrace watchpoint */
FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
FAIL_IF_MSG(pc != perf_then_ptrace_load2, "Child did not stop on ptrace load");
/* perf should have recorded the first load */
FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
FAIL_IF_MSG(perf_count != 1, "perf counter did not increment");
/* Clean up child */
FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child");
return 0;
}
int main(int argc, char *argv[])
{
int err = 0;
err |= test_harness(same_watch_addr_test, "same_watch_addr");
err |= test_harness(perf_then_ptrace_test, "perf_then_ptrace");
return err;
}
@@ -0,0 +1,306 @@
// 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"
#include "pkeys.h"
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 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, PKEY_UNRESTRICTED);
CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
disable_execute = false;
}
pkey2 = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
pkey3 = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
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, "PKEYs not supported");
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");
}
@@ -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");
}
@@ -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_MSG(!have_hwcap2(PPC_FEATURE2_ARCH_2_07), "TAR requires ISA 2.07 compatible hardware");
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");
}
@@ -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;
}
@@ -0,0 +1,155 @@
// 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;
double a = FPR_1;
double b = FPR_2;
double c = FPR_3;
void tm_gpr(void)
{
unsigned long gpr_buf[18];
unsigned long result, texasr;
double 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(flt_1)
"1: ;"
"tbegin.;"
"beq 2f;"
ASM_LOAD_GPR_IMMED(gpr_2)
ASM_LOAD_FPR(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", "r0", "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(fpr_buf);
if (validate_gpr(gpr_buf, GPR_3))
exit(1);
if (validate_fpr_double(fpr_buf, c))
exit(1);
exit(0);
}
shmdt((void *)cptr);
exit(1);
}
int trace_tm_gpr(pid_t child)
{
unsigned long gpr[18];
__u64 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -0,0 +1,166 @@
// 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;
double a = FPR_1;
double b = FPR_2;
double c = FPR_3;
double 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;
double 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(flt_1)
"1: ;"
"tbegin.;"
"beq 2f;"
ASM_LOAD_GPR_IMMED(gpr_2)
"tsuspend.;"
ASM_LOAD_GPR_IMMED(gpr_4)
ASM_LOAD_FPR(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", "r0", "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(fpr_buf);
if (validate_gpr(gpr_buf, GPR_3))
exit(1);
if (validate_fpr_double(fpr_buf, c))
exit(1);
exit(0);
}
shmdt((void *)cptr);
exit(1);
}
int trace_tm_spd_gpr(pid_t child)
{
unsigned long gpr[18];
__u64 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -0,0 +1,171 @@
// 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -0,0 +1,181 @@
// 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -0,0 +1,164 @@
// 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -0,0 +1,157 @@
// 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -0,0 +1,164 @@
// 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_MSG(!have_htm(), "Don't have transactional memory");
SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic");
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");
}
@@ -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_MSG(!have_hwcap(PPC_FEATURE_HAS_VSX), "Don't have 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");
}
@@ -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);
@@ -0,0 +1,809 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Ptrace interface test helper functions
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
*/
#define __SANE_USERSPACE_TYPES__
#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 <sys/syscall.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 {
__u64 fpr[32];
__u64 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, __u64 *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, __u64 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, __u64 *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;
}
long sys_ptrace(enum __ptrace_request request, pid_t pid, unsigned long addr, unsigned long data)
{
return syscall(__NR_ptrace, request, pid, (void *)addr, data);
}
// 33 because of FPSCR
#define PT_NUM_FPRS (33 * (sizeof(__u64) / sizeof(unsigned long)))
__u64 *peek_fprs(pid_t child)
{
unsigned long *fprs, *p, addr;
long ret;
int i;
fprs = malloc(sizeof(unsigned long) * PT_NUM_FPRS);
if (!fprs) {
perror("malloc() failed");
return NULL;
}
for (i = 0, p = fprs; i < PT_NUM_FPRS; i++, p++) {
addr = sizeof(unsigned long) * (PT_FPR0 + i);
ret = sys_ptrace(PTRACE_PEEKUSER, child, addr, (unsigned long)p);
if (ret) {
perror("ptrace(PTRACE_PEEKUSR) failed");
return NULL;
}
}
addr = sizeof(unsigned long) * (PT_FPR0 + i);
ret = sys_ptrace(PTRACE_PEEKUSER, child, addr, (unsigned long)&addr);
if (!ret) {
printf("ptrace(PTRACE_PEEKUSR) succeeded unexpectedly!\n");
return NULL;
}
return (__u64 *)fprs;
}
int poke_fprs(pid_t child, unsigned long *fprs)
{
unsigned long *p, addr;
long ret;
int i;
for (i = 0, p = fprs; i < PT_NUM_FPRS; i++, p++) {
addr = sizeof(unsigned long) * (PT_FPR0 + i);
ret = sys_ptrace(PTRACE_POKEUSER, child, addr, *p);
if (ret) {
perror("ptrace(PTRACE_POKEUSR) failed");
return -1;
}
}
addr = sizeof(unsigned long) * (PT_FPR0 + i);
ret = sys_ptrace(PTRACE_POKEUSER, child, addr, addr);
if (!ret) {
printf("ptrace(PTRACE_POKEUSR) succeeded unexpectedly!\n");
return -1;
}
return 0;
}
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)
{
return mfspr(SPRN_TFIAR);
}
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);
@@ -0,0 +1 @@
timeout=130