From f666ad413db6f7fbfaeec6b11ed6f8e4b60dda38 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Fri, 30 Sep 2016 10:32:52 +0800 Subject: selftests/powerpc: Add ptrace tests for GPR/FPR registers This patch adds ptrace interface test for GPR/FPR registers. This adds ptrace interface based helper functions related to GPR/FPR access and some assembly helper functions related to GPR/FPR registers. Signed-off-by: Anshuman Khandual Signed-off-by: Simon Guo [mpe: Add #defines for the new note types when headers don't define them] Signed-off-by: Michael Ellerman --- tools/testing/selftests/powerpc/ptrace/.gitignore | 1 + tools/testing/selftests/powerpc/ptrace/Makefile | 12 + .../testing/selftests/powerpc/ptrace/ptrace-gpr.c | 123 +++++++ .../testing/selftests/powerpc/ptrace/ptrace-gpr.h | 74 ++++ tools/testing/selftests/powerpc/ptrace/ptrace.h | 376 +++++++++++++++++++++ 5 files changed, 586 insertions(+) create mode 100644 tools/testing/selftests/powerpc/ptrace/.gitignore create mode 100644 tools/testing/selftests/powerpc/ptrace/Makefile create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace.h (limited to 'tools/testing/selftests/powerpc/ptrace') diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore new file mode 100644 index 000000000000..6e060041d7c8 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/.gitignore @@ -0,0 +1 @@ +ptrace-gpr \ No newline at end of file diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile new file mode 100644 index 000000000000..110377268f6d --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/Makefile @@ -0,0 +1,12 @@ +TEST_PROGS := ptrace-gpr + +include ../../lib.mk + +all: $(TEST_PROGS) + +CFLAGS += -m64 -I../../../../../usr/include + +$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h + +clean: + rm -f $(TEST_PROGS) *.o diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c new file mode 100644 index 000000000000..0b4ebcc2f485 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c @@ -0,0 +1,123 @@ +/* + * Ptrace test for GPR/FPR registers + * + * Copyright (C) 2015 Anshuman Khandual, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "ptrace.h" +#include "ptrace-gpr.h" +#include "reg.h" + +/* Tracer and Tracee Shared Data */ +int shm_id; +int *cptr, *pptr; + +float a = FPR_1; +float b = FPR_2; +float c = FPR_3; + +void gpr(void) +{ + unsigned long gpr_buf[18]; + float fpr_buf[32]; + + cptr = (int *)shmat(shm_id, NULL, 0); + + asm __volatile__( + ASM_LOAD_GPR_IMMED(gpr_1) + ASM_LOAD_FPR_SINGLE_PRECISION(flt_1) + : + : [gpr_1]"i"(GPR_1), [flt_1] "r" (&a) + : "memory", "r6", "r7", "r8", "r9", "r10", + "r11", "r12", "r13", "r14", "r15", "r16", "r17", + "r18", "r19", "r20", "r21", "r22", "r23", "r24", + "r25", "r26", "r27", "r28", "r29", "r30", "r31" + ); + + cptr[1] = 1; + + while (!cptr[0]) + asm volatile("" : : : "memory"); + + shmdt((void *)cptr); + store_gpr(gpr_buf); + store_fpr_single_precision(fpr_buf); + + if (validate_gpr(gpr_buf, GPR_3)) + exit(1); + + if (validate_fpr_float(fpr_buf, c)) + exit(1); + + exit(0); +} + +int trace_gpr(pid_t child) +{ + unsigned long gpr[18]; + unsigned long fpr[32]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_gpr(child, gpr)); + FAIL_IF(validate_gpr(gpr, GPR_1)); + FAIL_IF(show_fpr(child, fpr)); + FAIL_IF(validate_fpr(fpr, FPR_1_REP)); + FAIL_IF(write_gpr(child, GPR_3)); + FAIL_IF(write_fpr(child, FPR_3_REP)); + FAIL_IF(stop_trace(child)); + + return TEST_PASS; +} + +int ptrace_gpr(void) +{ + pid_t pid; + int ret, status; + + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + if (pid == 0) + gpr(); + + if (pid) { + pptr = (int *)shmat(shm_id, NULL, 0); + while (!pptr[1]) + asm volatile("" : : : "memory"); + + ret = trace_gpr(pid); + if (ret) { + kill(pid, SIGTERM); + shmdt((void *)pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + pptr[0] = 1; + shmdt((void *)pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_gpr, "ptrace_gpr"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h new file mode 100644 index 000000000000..e30fef63824c --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Anshuman Khandual, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define GPR_1 1 +#define GPR_2 2 +#define GPR_3 3 +#define GPR_4 4 + +#define FPR_1 0.001 +#define FPR_2 0.002 +#define FPR_3 0.003 +#define FPR_4 0.004 + +#define FPR_1_REP 0x3f50624de0000000 +#define FPR_2_REP 0x3f60624de0000000 +#define FPR_3_REP 0x3f689374c0000000 +#define FPR_4_REP 0x3f70624de0000000 + +/* Buffer must have 18 elements */ +int validate_gpr(unsigned long *gpr, unsigned long val) +{ + int i, found = 1; + + for (i = 0; i < 18; i++) { + if (gpr[i] != val) { + printf("GPR[%d]: %lx Expected: %lx\n", + i+14, gpr[i], val); + found = 0; + } + } + + if (!found) + return TEST_FAIL; + return TEST_PASS; +} + +/* Buffer must have 32 elements */ +int validate_fpr(unsigned long *fpr, unsigned long val) +{ + int i, found = 1; + + for (i = 0; i < 32; i++) { + if (fpr[i] != val) { + printf("FPR[%d]: %lx Expected: %lx\n", i, fpr[i], val); + found = 0; + } + } + + if (!found) + return TEST_FAIL; + return TEST_PASS; +} + +/* Buffer must have 32 elements */ +int validate_fpr_float(float *fpr, float val) +{ + int i, found = 1; + + for (i = 0; i < 32; i++) { + if (fpr[i] != val) { + printf("FPR[%d]: %f Expected: %f\n", i, fpr[i], val); + found = 0; + } + } + + if (!found) + return TEST_FAIL; + return TEST_PASS; +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h new file mode 100644 index 000000000000..9ee89eb06df9 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h @@ -0,0 +1,376 @@ +/* + * Ptrace interface test helper functions + * + * Copyright (C) 2015 Anshuman Khandual, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "reg.h" +#include "utils.h" + +#define TEST_PASS 0 +#define TEST_FAIL 1 + +struct fpr_regs { + unsigned long fpr[32]; + unsigned long fpscr; +}; + + +#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; +} + +/* FPR */ +int show_fpr(pid_t child, unsigned long *fpr) +{ + struct fpr_regs *regs; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (fpr) { + for (i = 0; i < 32; i++) + fpr[i] = regs->fpr[i]; + } + return TEST_PASS; +} + +int write_fpr(pid_t child, unsigned long val) +{ + struct fpr_regs *regs; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 0; i < 32; i++) + regs->fpr[i] = val; + + ret = ptrace(PTRACE_SETFPREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int show_ckpt_fpr(pid_t child, unsigned long *fpr) +{ + struct fpr_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + iov.iov_base = regs; + iov.iov_len = sizeof(struct fpr_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (fpr) { + for (i = 0; i < 32; i++) + fpr[i] = regs->fpr[i]; + } + + return TEST_PASS; +} + +int write_ckpt_fpr(pid_t child, unsigned long val) +{ + struct fpr_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + iov.iov_base = regs; + iov.iov_len = sizeof(struct fpr_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 0; i < 32; i++) + regs->fpr[i] = val; + + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CFPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* GPR */ +int show_gpr(pid_t child, unsigned long *gpr) +{ + struct pt_regs *regs; + int ret, i; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + ret = ptrace(PTRACE_GETREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (gpr) { + for (i = 14; i < 32; i++) + gpr[i-14] = regs->gpr[i]; + } + + return TEST_PASS; +} + +int write_gpr(pid_t child, unsigned long val) +{ + struct pt_regs *regs; + int i, ret; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + ret = ptrace(PTRACE_GETREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 14; i < 32; i++) + regs->gpr[i] = val; + + ret = ptrace(PTRACE_SETREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int show_ckpt_gpr(pid_t child, unsigned long *gpr) +{ + struct pt_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(struct pt_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (gpr) { + for (i = 14; i < 32; i++) + gpr[i-14] = regs->gpr[i]; + } + + return TEST_PASS; +} + +int write_ckpt_gpr(pid_t child, unsigned long val) +{ + struct pt_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed\n"); + return TEST_FAIL; + } + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(struct pt_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 14; i < 32; i++) + regs->gpr[i] = val; + + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CGPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* Analyse TEXASR after TM failure */ +inline unsigned long get_tfiar(void) +{ + unsigned long ret; + + asm volatile("mfspr %0,%1" : "=r" (ret) : "i" (SPRN_TFIAR)); + return ret; +} + +void analyse_texasr(unsigned long texasr) +{ + printf("TEXASR: %16lx\t", texasr); + + if (texasr & TEXASR_FP) + printf("TEXASR_FP "); + + if (texasr & TEXASR_DA) + printf("TEXASR_DA "); + + if (texasr & TEXASR_NO) + printf("TEXASR_NO "); + + if (texasr & TEXASR_FO) + printf("TEXASR_FO "); + + if (texasr & TEXASR_SIC) + printf("TEXASR_SIC "); + + if (texasr & TEXASR_NTC) + printf("TEXASR_NTC "); + + if (texasr & TEXASR_TC) + printf("TEXASR_TC "); + + if (texasr & TEXASR_TIC) + printf("TEXASR_TIC "); + + if (texasr & TEXASR_IC) + printf("TEXASR_IC "); + + if (texasr & TEXASR_IFC) + printf("TEXASR_IFC "); + + if (texasr & TEXASR_ABT) + printf("TEXASR_ABT "); + + if (texasr & TEXASR_SPD) + printf("TEXASR_SPD "); + + if (texasr & TEXASR_HV) + printf("TEXASR_HV "); + + if (texasr & TEXASR_PR) + printf("TEXASR_PR "); + + if (texasr & TEXASR_FS) + printf("TEXASR_FS "); + + if (texasr & TEXASR_TE) + printf("TEXASR_TE "); + + if (texasr & TEXASR_ROT) + printf("TEXASR_ROT "); + + printf("TFIAR :%lx\n", get_tfiar()); +} + +void store_gpr(unsigned long *addr); +void store_fpr(float *addr); -- cgit v1.2.3-59-g8ed1b