s390/ptrace: add runtime instrumention register get/set
authorAlice Frosi <alice@linux.vnet.ibm.com>
Thu, 14 Sep 2017 10:36:03 +0000 (12:36 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 28 Sep 2017 05:29:41 +0000 (07:29 +0200)
Add runtime instrumention register get and set which allows to read
and modify the runtime instrumention control block.

Signed-off-by: Alice Frosi <alice@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/kernel/ptrace.c
include/uapi/linux/elf.h

index a5f8f0c8ccf0d6524f99e9049a20ab60a23bbf03..ea711f141bb882d4eadf9a31c27d3546eb48cf73 100644 (file)
@@ -30,6 +30,9 @@
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
 #include <asm/switch_to.h>
+#include <asm/runtime_instr.h>
+#include <asm/facility.h>
+
 #include "entry.h"
 
 #ifdef CONFIG_COMPAT
@@ -1239,6 +1242,96 @@ static int s390_gs_bc_set(struct task_struct *target,
                                  data, 0, sizeof(struct gs_cb));
 }
 
+static bool is_ri_cb_valid(struct runtime_instr_cb *cb)
+{
+       return (cb->rca & 0x1f) == 0 &&
+               (cb->roa & 0xfff) == 0 &&
+               (cb->rla & 0xfff) == 0xfff &&
+               cb->s == 1 &&
+               cb->k == 1 &&
+               cb->h == 0 &&
+               cb->reserved1 == 0 &&
+               cb->ps == 1 &&
+               cb->qs == 0 &&
+               cb->pc == 1 &&
+               cb->qc == 0 &&
+               cb->reserved2 == 0 &&
+               cb->key == PAGE_DEFAULT_KEY &&
+               cb->reserved3 == 0 &&
+               cb->reserved4 == 0 &&
+               cb->reserved5 == 0 &&
+               cb->reserved6 == 0 &&
+               cb->reserved7 == 0 &&
+               cb->reserved8 == 0 &&
+               cb->rla >= cb->roa &&
+               cb->rca >= cb->roa &&
+               cb->rca <= cb->rla+1 &&
+               cb->m < 3;
+}
+
+static int s390_runtime_instr_get(struct task_struct *target,
+                               const struct user_regset *regset,
+                               unsigned int pos, unsigned int count,
+                               void *kbuf, void __user *ubuf)
+{
+       struct runtime_instr_cb *data = target->thread.ri_cb;
+
+       if (!test_facility(64))
+               return -ENODEV;
+       if (!data)
+               return -ENODATA;
+
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                  data, 0, sizeof(struct runtime_instr_cb));
+}
+
+static int s390_runtime_instr_set(struct task_struct *target,
+                                 const struct user_regset *regset,
+                                 unsigned int pos, unsigned int count,
+                                 const void *kbuf, const void __user *ubuf)
+{
+       struct runtime_instr_cb ri_cb = { }, *data = NULL;
+       int rc;
+
+       if (!test_facility(64))
+               return -ENODEV;
+
+       if (!target->thread.ri_cb) {
+               data = kzalloc(sizeof(*data), GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+       }
+
+       if (target->thread.ri_cb) {
+               if (target == current)
+                       store_runtime_instr_cb(&ri_cb);
+               else
+                       ri_cb = *target->thread.ri_cb;
+       }
+
+       rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                               &ri_cb, 0, sizeof(struct runtime_instr_cb));
+       if (rc) {
+               kfree(data);
+               return -EFAULT;
+       }
+
+       if (!is_ri_cb_valid(&ri_cb)) {
+               kfree(data);
+               return -EINVAL;
+       }
+
+       preempt_disable();
+       if (!target->thread.ri_cb)
+               target->thread.ri_cb = data;
+       *target->thread.ri_cb = ri_cb;
+       if (target == current)
+               load_runtime_instr_cb(target->thread.ri_cb);
+       preempt_enable();
+
+       return 0;
+}
+
 static const struct user_regset s390_regsets[] = {
        {
                .core_note_type = NT_PRSTATUS,
@@ -1312,6 +1405,14 @@ static const struct user_regset s390_regsets[] = {
                .get = s390_gs_bc_get,
                .set = s390_gs_bc_set,
        },
+       {
+               .core_note_type = NT_S390_RI_CB,
+               .n = sizeof(struct runtime_instr_cb) / sizeof(__u64),
+               .size = sizeof(__u64),
+               .align = sizeof(__u64),
+               .get = s390_runtime_instr_get,
+               .set = s390_runtime_instr_set,
+       },
 };
 
 static const struct user_regset_view user_s390_view = {
@@ -1548,6 +1649,14 @@ static const struct user_regset s390_compat_regsets[] = {
                .get = s390_gs_cb_get,
                .set = s390_gs_cb_set,
        },
+       {
+               .core_note_type = NT_S390_RI_CB,
+               .n = sizeof(struct runtime_instr_cb) / sizeof(__u64),
+               .size = sizeof(__u64),
+               .align = sizeof(__u64),
+               .get = s390_runtime_instr_get,
+               .set = s390_runtime_instr_set,
+       },
 };
 
 static const struct user_regset_view user_s390_compat_view = {
index b5280db9ef6a8c27f92ee89ffd0432df525e5039..e3739c330c156130f3a9712ca77174e95ab8bef2 100644 (file)
@@ -411,6 +411,7 @@ typedef struct elf64_shdr {
 #define NT_S390_VXRS_HIGH      0x30a   /* s390 vector registers 16-31 */
 #define NT_S390_GS_CB  0x30b           /* s390 guarded storage registers */
 #define NT_S390_GS_BC  0x30c           /* s390 guarded storage broadcast control block */
+#define NT_S390_RI_CB  0x30d           /* s390 runtime instrumentation */
 #define NT_ARM_VFP     0x400           /* ARM VFP/NEON registers */
 #define NT_ARM_TLS     0x401           /* ARM TLS register */
 #define NT_ARM_HW_BREAK        0x402           /* ARM hardware breakpoint registers */