[PATCH V10 17/28] selftests, powerpc: Add ptrace tests for EBB

From: Anshuman Khandual
Date: Tue Feb 16 2016 - 04:03:00 EST


This patch adds ptrace interface test for EBB specific
registers. This also adds some generic ptrace interface
based helper functions to be used by other patches later
on in the series.

Signed-off-by: Anshuman Khandual <khandual@xxxxxxxxxxxxxxxxxx>
---
tools/testing/selftests/powerpc/Makefile | 3 +-
tools/testing/selftests/powerpc/ptrace/Makefile | 7 +
.../testing/selftests/powerpc/ptrace/ptrace-ebb.c | 150 ++++++++++++++
.../testing/selftests/powerpc/ptrace/ptrace-ebb.h | 103 ++++++++++
tools/testing/selftests/powerpc/ptrace/ptrace.h | 225 +++++++++++++++++++++
5 files changed, 487 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/ptrace/Makefile
create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-ebb.c
create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-ebb.h
create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace.h

diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index 0c2706b..5b3c62c 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -22,7 +22,8 @@ SUB_DIRS = benchmarks \
switch_endian \
syscalls \
tm \
- vphn
+ vphn \
+ ptrace

endif

diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
new file mode 100644
index 0000000..8666ac0
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -0,0 +1,7 @@
+TEST_PROGS := ptrace-ebb
+all: $(TEST_PROGS)
+
+$(TEST_PROGS): ../harness.c ptrace.S ../utils.c
+ptrace-ebb: ../pmu/event.c ../pmu/lib.c ../pmu/ebb/ebb_handler.S ../pmu/ebb/busy_loop.S
+clean:
+ rm -f $(TEST_PROGS) *.o
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.c b/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.c
new file mode 100644
index 0000000..e1ca608
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.c
@@ -0,0 +1,150 @@
+/*
+ * Ptrace interface test for EBB
+ *
+ * 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 "../pmu/ebb/ebb.h"
+#include "ptrace.h"
+#include "ptrace-ebb.h"
+
+void ebb(void)
+{
+ struct event event;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event.attr.config |= (1ull << 63);
+ event.attr.exclusive = 1;
+ event.attr.pinned = 1;
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ if (event_open(&event)) {
+ perror("event_open() failed");
+ exit(1);
+ }
+
+ setup_ebb_handler(standard_ebb_callee);
+ mtspr(SPRN_BESCR, 0x8000000100000000ull);
+
+ mb();
+
+ if (ebb_event_enable(&event)) {
+ perror("ebb_event_handler() failed");
+ exit(1);
+ }
+
+ mtspr(SPRN_PMC1, pmc_sample_period(SAMPLE_PERIOD));
+ while(1)
+ core_busy_loop();
+ exit(0);
+}
+
+int validate_ebb(struct ebb_regs *regs)
+{
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ struct opd *opd = (struct opd *) ebb_handler;
+ #endif
+
+ printf("EBBRR: %lx\n", regs->ebbrr);
+ printf("EBBHR: %lx\n", regs->ebbhr);
+ printf("BESCR: %lx\n", regs->bescr);
+ printf("SIAR: %lx\n", regs->siar);
+ printf("SDAR: %lx\n", regs->sdar);
+ printf("SIER: %lx\n", regs->sier);
+ printf("MMCR2: %lx\n", regs->mmcr2);
+ printf("MMCR0: %lx\n", regs->mmcr0);
+
+ /* Validate EBBHR */
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ if (regs->ebbhr != opd->entry)
+ return TEST_FAIL;
+ #else
+ if (regs->ebbhr != (unsigned long) ebb_handler)
+ return TEST_FAIL;
+ #endif
+
+ /* Validate SIER */
+ if (regs->sier != SIER_EXP)
+ return TEST_FAIL;
+
+ /* Validate MMCR2 */
+ if (regs->mmcr2 != MMCR2_EXP)
+ return TEST_FAIL;
+
+ /* Validate MMCR0 */
+ if (regs->mmcr0 != MMCR0_EXP)
+ return TEST_FAIL;
+
+ return TEST_PASS;
+}
+
+int trace_ebb(pid_t child)
+{
+ struct ebb_regs regs;
+ int ret;
+
+ sleep(2);
+ ret = start_trace(child);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = show_ebb_registers(child, &regs);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = validate_ebb(&regs);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = stop_trace(child);
+ if (ret)
+ return TEST_FAIL;
+
+ return TEST_PASS;
+}
+
+int ptrace_ebb(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ ebb();
+
+ if (pid) {
+ ret = trace_ebb(pid);
+ if (ret)
+ return TEST_FAIL;
+
+ kill(pid, SIGKILL);
+ ret = wait(&status);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ }
+
+ if (WIFEXITED(status)) {
+ if(WEXITSTATUS(status))
+ return TEST_FAIL;
+ }
+ return TEST_PASS;
+ }
+ return TEST_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_ebb, "ptrace_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.h b/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.h
new file mode 100644
index 0000000..9b38edc
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.h
@@ -0,0 +1,103 @@
+/*
+ * Inspired mostly from the EBB selftest
+ *
+ * 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 SAMPLE_PERIOD 100 /* EBB event sample persiod */
+
+/* Standard expected values */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define MMCR0_EXP 0x8000008000000001
+#else
+#define MMCR0_EXP 0x180000080
+#endif
+
+#define MMCR2_EXP 0
+#define SIER_EXP 0x2000000
+
+struct opd
+{
+ u64 entry;
+ u64 toc;
+};
+
+void (*ebb_user_func)(void);
+extern void ebb_handler(void); /* Defined in ebb_handle.S */
+
+void ebb_hook(void) /* Called by ebb_handler */
+{
+ if (ebb_user_func)
+ ebb_user_func();
+}
+
+void setup_ebb_handler(void (*callee)(void))
+{
+ u64 entry;
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+ entry = (u64)ebb_handler;
+#else
+ struct opd *opd;
+
+ opd = (struct opd *)ebb_handler;
+ entry = opd->entry;
+#endif
+ ebb_user_func = callee;
+
+ /* Ensure ebb_user_func is set before we set the handler */
+ mb();
+ mtspr(SPRN_EBBHR, entry);
+
+ /* Make sure the handler is set before we return */
+ mb();
+}
+
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask)
+{
+ u64 val;
+
+ /* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */
+ /* 3) set MMCR0[PMAE] - docs say BESCR[PME] should do this */
+ val = mfspr(SPRN_MMCR0);
+ mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE);
+
+ /* 4) clear BESCR[PMEO] */
+ mtspr(SPRN_BESCRR, BESCR_PMEO);
+
+ /* 5) set BESCR[PME] */
+ mtspr(SPRN_BESCRS, BESCR_PME);
+
+ /* 6) rfebb 1 - done in our caller */
+}
+
+void standard_ebb_callee(void)
+{
+ u64 val;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO))
+ printf("Spurious interrupt\n");
+
+ mtspr(SPRN_PMC1, pmc_sample_period(SAMPLE_PERIOD));
+ reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC);
+}
+
+int ebb_event_enable(struct event *e)
+{
+ int rc;
+
+ mb();
+
+ rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+ if (rc)
+ return rc;
+ rc = event_read(e);
+
+ mb();
+ return rc;
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
new file mode 100644
index 0000000..44256d2
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -0,0 +1,225 @@
+/*
+ * 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 <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/ptrace.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/user.h>
+#include <linux/elf.h>
+#include <linux/types.h>
+#include <linux/auxvec.h>
+#include "../reg.h"
+#include "utils.h"
+
+/* ELF core note sections */
+#define NT_PPC_TAR 0x103 /* Target Address Register */
+#define NT_PPC_PPR 0x104 /* Program Priority Register */
+#define NT_PPC_DSCR 0x105 /* Data Stream Control Register */
+#define NT_PPC_EBB 0x106 /* Event Based Branch Registers */
+#define NT_PPC_TM_CGPR 0x107 /* TM checkpointed GPR Registers */
+#define NT_PPC_TM_CFPR 0x108 /* TM checkpointed FPR Registers */
+#define NT_PPC_TM_CVMX 0x109 /* TM checkpointed VMX Registers */
+#define NT_PPC_TM_CVSX 0x10a /* TM checkpointed VSX Registers */
+#define NT_PPC_TM_SPR 0x10b /* TM Special Purpose Registers */
+#define NT_PPC_TM_CTAR 0x10c /* TM checkpointed Target Address Register */
+#define NT_PPC_TM_CPPR 0x10d /* TM checkpointed Program Priority Register */
+#define NT_PPC_TM_CDSCR 0x10e /* TM checkpointed Data Stream Control Register */
+
+/* TEXASR register bits */
+#define TEXASR_FC 0xFE00000000000000
+#define TEXASR_FP 0x0100000000000000
+#define TEXASR_DA 0x0080000000000000
+#define TEXASR_NO 0x0040000000000000
+#define TEXASR_FO 0x0020000000000000
+#define TEXASR_SIC 0x0010000000000000
+#define TEXASR_NTC 0x0008000000000000
+#define TEXASR_TC 0x0004000000000000
+#define TEXASR_TIC 0x0002000000000000
+#define TEXASR_IC 0x0001000000000000
+#define TEXASR_IFC 0x0000800000000000
+#define TEXASR_ABT 0x0000000100000000
+#define TEXASR_SPD 0x0000000080000000
+#define TEXASR_HV 0x0000000020000000
+#define TEXASR_PR 0x0000000010000000
+#define TEXASR_FS 0x0000000008000000
+#define TEXASR_TE 0x0000000004000000
+#define TEXASR_ROT 0x0000000002000000
+
+#define TEST_PASS 0
+#define TEST_FAIL 1
+
+struct ebb_regs {
+ unsigned long ebbrr;
+ unsigned long ebbhr;
+ unsigned long bescr;
+ unsigned long siar;
+ unsigned long sdar;
+ unsigned long sier;
+ unsigned long mmcr2;
+ unsigned long mmcr0;
+};
+
+struct fpr_regs {
+ unsigned long fpr[32];
+ unsigned long fpscr;
+};
+
+
+/* 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;
+}
+
+/* EBB */
+int show_ebb_registers(pid_t child, struct ebb_regs *regs)
+{
+ struct ebb_regs *ebb;
+ struct iovec iov;
+ int ret;
+
+ ebb = malloc(sizeof(struct ebb_regs));
+ if (!ebb) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (struct ebb_regs *) ebb;
+ iov.iov_len = sizeof(struct ebb_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_EBB, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+
+ if (regs)
+ memcpy(regs, ebb, sizeof(struct ebb_regs));
+
+ free(ebb);
+ return TEST_PASS;
+fail:
+ free(ebb);
+ return TEST_FAIL;
+}
+
+/* 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());
+}
--
2.1.0