powerpc/ptrace: Enable support for NT_PPC_CVMX
authorAnshuman Khandual <khandual@linux.vnet.ibm.com>
Thu, 28 Jul 2016 02:57:38 +0000 (10:57 +0800)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 1 Aug 2016 01:15:19 +0000 (11:15 +1000)
This patch enables support for TM checkpointed VMX register
set ELF core note NT_PPC_CVMX based ptrace requests through
PTRACE_GETREGSET, PTRACE_SETREGSET calls. This is achieved
through adding a register set REGSET_CVMX in powerpc
corresponding to the ELF core note section added. It
implements the get, set and active functions for this new
register set added.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Simon Guo <wei.guo.simon@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/uapi/asm/elf.h
arch/powerpc/kernel/ptrace.c

index c2d21d1..ecb4e84 100644 (file)
@@ -91,6 +91,7 @@
 
 #define ELF_NGREG      48      /* includes nip, msr, lr, etc. */
 #define ELF_NFPREG     33      /* includes fpscr */
+#define ELF_NVMX       34      /* includes all vector registers */
 
 typedef unsigned long elf_greg_t64;
 typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
index b216c39..6e42137 100644 (file)
@@ -64,6 +64,8 @@ struct pt_regs_offset {
        {.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
 #define REG_OFFSET_END {.name = NULL, .offset = 0}
 
+#define TVSO(f)        (offsetof(struct thread_vr_state, f))
+
 static const struct pt_regs_offset regoffset_table[] = {
        GPR_OFFSET_NAME(0),
        GPR_OFFSET_NAME(1),
@@ -1147,6 +1149,151 @@ static int tm_cfpr_set(struct task_struct *target,
        target->thread.fp_state.fpscr = buf[32];
        return 0;
 }
+
+/**
+ * tm_cvmx_active - get active number of registers in CVMX
+ * @target:    The target task.
+ * @regset:    The user regset structure.
+ *
+ * This function checks for the active number of available
+ * regisers in checkpointed VMX category.
+ */
+static int tm_cvmx_active(struct task_struct *target,
+                               const struct user_regset *regset)
+{
+       if (!cpu_has_feature(CPU_FTR_TM))
+               return -ENODEV;
+
+       if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+               return 0;
+
+       return regset->n;
+}
+
+/**
+ * tm_cvmx_get - get CMVX registers
+ * @target:    The target task.
+ * @regset:    The user regset structure.
+ * @pos:       The buffer position.
+ * @count:     Number of bytes to copy.
+ * @kbuf:      Kernel buffer to copy from.
+ * @ubuf:      User buffer to copy into.
+ *
+ * This function gets in transaction checkpointed VMX registers.
+ *
+ * When the transaction is active 'vr_state' and 'vr_save' hold
+ * the checkpointed values for the current transaction to fall
+ * back on if it aborts in between. The userspace interface buffer
+ * layout is as follows.
+ *
+ * struct data {
+ *     vector128       vr[32];
+ *     vector128       vscr;
+ *     vector128       vrsave;
+ *};
+ */
+static int tm_cvmx_get(struct task_struct *target,
+                       const struct user_regset *regset,
+                       unsigned int pos, unsigned int count,
+                       void *kbuf, void __user *ubuf)
+{
+       int ret;
+
+       BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32]));
+
+       if (!cpu_has_feature(CPU_FTR_TM))
+               return -ENODEV;
+
+       if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+               return -ENODATA;
+
+       /* Flush the state */
+       flush_fp_to_thread(target);
+       flush_altivec_to_thread(target);
+       flush_tmregs_to_thread(target);
+
+       ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                       &target->thread.vr_state, 0,
+                                       33 * sizeof(vector128));
+       if (!ret) {
+               /*
+                * Copy out only the low-order word of vrsave.
+                */
+               union {
+                       elf_vrreg_t reg;
+                       u32 word;
+               } vrsave;
+               memset(&vrsave, 0, sizeof(vrsave));
+               vrsave.word = target->thread.vrsave;
+               ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
+                                               33 * sizeof(vector128), -1);
+       }
+
+       return ret;
+}
+
+/**
+ * tm_cvmx_set - set CMVX registers
+ * @target:    The target task.
+ * @regset:    The user regset structure.
+ * @pos:       The buffer position.
+ * @count:     Number of bytes to copy.
+ * @kbuf:      Kernel buffer to copy into.
+ * @ubuf:      User buffer to copy from.
+ *
+ * This function sets in transaction checkpointed VMX registers.
+ *
+ * When the transaction is active 'vr_state' and 'vr_save' hold
+ * the checkpointed values for the current transaction to fall
+ * back on if it aborts in between. The userspace interface buffer
+ * layout is as follows.
+ *
+ * struct data {
+ *     vector128       vr[32];
+ *     vector128       vscr;
+ *     vector128       vrsave;
+ *};
+ */
+static int tm_cvmx_set(struct task_struct *target,
+                       const struct user_regset *regset,
+                       unsigned int pos, unsigned int count,
+                       const void *kbuf, const void __user *ubuf)
+{
+       int ret;
+
+       BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32]));
+
+       if (!cpu_has_feature(CPU_FTR_TM))
+               return -ENODEV;
+
+       if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+               return -ENODATA;
+
+       flush_fp_to_thread(target);
+       flush_altivec_to_thread(target);
+       flush_tmregs_to_thread(target);
+
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                       &target->thread.vr_state, 0,
+                                       33 * sizeof(vector128));
+       if (!ret && count > 0) {
+               /*
+                * We use only the low-order word of vrsave.
+                */
+               union {
+                       elf_vrreg_t reg;
+                       u32 word;
+               } vrsave;
+               memset(&vrsave, 0, sizeof(vrsave));
+               vrsave.word = target->thread.vrsave;
+               ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave,
+                                               33 * sizeof(vector128), -1);
+               if (!ret)
+                       target->thread.vrsave = vrsave.word;
+       }
+
+       return ret;
+}
 #endif
 
 /*
@@ -1167,6 +1314,7 @@ enum powerpc_regset {
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
        REGSET_TM_CGPR,         /* TM checkpointed GPR registers */
        REGSET_TM_CFPR,         /* TM checkpointed FPR registers */
+       REGSET_TM_CVMX,         /* TM checkpointed VMX registers */
 #endif
 };
 
@@ -1213,6 +1361,11 @@ static const struct user_regset native_regsets[] = {
                .size = sizeof(double), .align = sizeof(double),
                .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set
        },
+       [REGSET_TM_CVMX] = {
+               .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
+               .size = sizeof(vector128), .align = sizeof(vector128),
+               .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
+       },
 #endif
 };
 
@@ -1450,6 +1603,11 @@ static const struct user_regset compat_regsets[] = {
                .size = sizeof(double), .align = sizeof(double),
                .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set
        },
+       [REGSET_TM_CVMX] = {
+               .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
+               .size = sizeof(vector128), .align = sizeof(vector128),
+               .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
+       },
 #endif
 };