[PATCH v15 03/15] selftests/powerpc: Add ptrace tests for EBB

From: wei . guo . simon
Date: Thu Sep 29 2016 - 22:36:22 EST


From: Anshuman Khandual <khandual@xxxxxxxxxxxxxxxxxx>

This patch adds ptrace interface test for EBB/PMU 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>
Signed-off-by: Simon Guo <wei.guo.simon@xxxxxxxxx>
---
tools/testing/selftests/powerpc/Makefile | 3 +-
tools/testing/selftests/powerpc/ptrace/Makefile | 11 +
.../testing/selftests/powerpc/ptrace/ptrace-ebb.c | 187 +++++++++++++++++
.../testing/selftests/powerpc/ptrace/ptrace-ebb.h | 99 +++++++++
tools/testing/selftests/powerpc/ptrace/ptrace.h | 225 +++++++++++++++++++++
5 files changed, 524 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 b6eb817..2fe383c 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -25,7 +25,8 @@ SUB_DIRS = alignment \
syscalls \
tm \
vphn \
- math
+ math \
+ ptrace

endif

diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
new file mode 100644
index 0000000..84c1c01
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -0,0 +1,11 @@
+TEST_PROGS := ptrace-ebb
+
+include ../../lib.mk
+
+all: $(TEST_PROGS)
+CFLAGS += -m64
+$(TEST_PROGS): ../harness.c ../utility/utils.c ptrace.h
+ptrace-ebb: ../pmu/event.c ../pmu/lib.c ../pmu/ebb/ebb_handler.S ../pmu/ebb/busy_loop.S
+ptrace-ebb: CFLAGS += -I../pmu/ebb
+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..1ec4a6b
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.c
@@ -0,0 +1,187 @@
+/*
+ * 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 "ebb.h"
+#include "ptrace.h"
+#include "ptrace-ebb.h"
+
+/* Tracer and Tracee Shared Data */
+int shm_id;
+int *cptr, *pptr;
+
+void ebb(void)
+{
+ struct event event;
+
+ cptr = (int *)shmat(shm_id, NULL, 0);
+
+ 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);
+
+ /*
+ * make sure BESCR has been set before continue
+ */
+ mb();
+
+ if (ebb_event_enable(&event)) {
+ perror("ebb_event_handler() failed");
+ exit(1);
+ }
+
+ mtspr(SPRN_PMC1, pmc_sample_period(SAMPLE_PERIOD));
+ core_busy_loop();
+ cptr[0] = 1;
+ while (1)
+ asm volatile("" : : : "memory");
+
+ 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);
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ printf("EBBHR: %lx; expected: %lx\n",
+ regs->ebbhr, (unsigned long)opd->entry);
+ #else
+ printf("EBBHR: %lx; expected: %lx\n",
+ regs->ebbhr, (unsigned long)ebb_handler);
+ #endif
+ printf("BESCR: %lx\n", regs->bescr);
+
+ #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
+
+ return TEST_PASS;
+}
+
+int validate_pmu(struct pmu_regs *regs)
+{
+ printf("SIAR: %lx\n", regs->siar);
+ printf("SDAR: %lx\n", regs->sdar);
+ printf("SIER: %lx; expected: %lx\n",
+ regs->sier, (unsigned long)SIER_EXP);
+ printf("MMCR2: %lx; expected: %lx\n",
+ regs->mmcr2, (unsigned long)MMCR2_EXP);
+ printf("MMCR0: %lx; expected: %lx\n",
+ regs->mmcr0, (unsigned long)MMCR0_EXP);
+
+ /* 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_pmu(pid_t child)
+{
+ struct ebb_regs ebb_regs;
+ struct pmu_regs pmu_regs;
+ int ret;
+
+ ret = start_trace(child);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = show_ebb_registers(child, &ebb_regs);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = validate_ebb(&ebb_regs);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = show_pmu_registers(child, &pmu_regs);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = validate_pmu(&pmu_regs);
+ if (ret)
+ return TEST_FAIL;
+
+ ret = stop_trace(child);
+ if (ret)
+ return TEST_FAIL;
+
+ return TEST_PASS;
+}
+
+int ptrace_ebb_pmu(void)
+{
+ pid_t pid;
+ int ret, status;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(int) * 1, 0777|IPC_CREAT);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ return TEST_FAIL;
+ }
+
+ if (pid == 0)
+ ebb();
+
+ if (pid) {
+ pptr = (int *)shmat(shm_id, NULL, 0);
+ while (!pptr[0])
+ asm volatile("" : : : "memory");
+
+ ret = trace_ebb_pmu(pid);
+ if (ret)
+ return TEST_FAIL;
+
+ shmctl(shm_id, IPC_RMID, NULL);
+ kill(pid, SIGKILL);
+ ret = wait(&status);
+ 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_ebb_pmu, "ptrace_ebb_pmu");
+}
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..740848a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-ebb.h
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+ rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+ if (rc)
+ return rc;
+ rc = event_read(e);
+
+ 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..96d2179
--- /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"
+
+#define TEST_PASS 0
+#define TEST_FAIL 1
+
+struct ebb_regs {
+ unsigned long ebbrr;
+ unsigned long ebbhr;
+ unsigned long bescr;
+};
+
+struct pmu_regs {
+ 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;
+}
+
+/* PMU */
+int show_pmu_registers(pid_t child, struct pmu_regs *regs)
+{
+ struct pmu_regs *pmu;
+ struct iovec iov;
+ int ret;
+
+ pmu = malloc(sizeof(struct pmu_regs));
+ if (!pmu) {
+ perror("malloc() failed");
+ return TEST_FAIL;
+ }
+
+ iov.iov_base = (struct pmu_regs *) pmu;
+ iov.iov_len = sizeof(struct pmu_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_PMU, &iov);
+ if (ret) {
+ perror("ptrace(PTRACE_GETREGSET) failed");
+ goto fail;
+ }
+
+ if (regs)
+ memcpy(regs, pmu, sizeof(struct pmu_regs));
+
+ free(pmu);
+ return TEST_PASS;
+fail:
+ free(pmu);
+ return TEST_FAIL;
+}
+
+/* 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());
+}
--
1.8.3.1