--- v11
+++ v12
@@ -1,12 +1,11 @@
From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
-This patch enables support for TM checkpointed GPR register
-set ELF core note NT_PPC_CGPR based ptrace requests through
-PTRACE_GETREGSET, PTRACE_SETREGSET calls. This is achieved
-through adding a register set REGSET_CGPR in powerpc
-corresponding to the ELF core note section added. It
-implements the get, set and active functions for this new
-register set added.
+This patch enables in transaction NT_PPC_VMX ptrace requests. The
+function vr_get which gets the running value of all VMX registers
+and the function vr_set which sets the running value of of all VMX
+registers work on the running set of VMX registers whose location
+will be different if transaction is active. This patch makes these
+functions adapt to situations when the transaction is active.
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
@@ -32,276 +31,159 @@
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
- arch/powerpc/kernel/ptrace.c | 222 +++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 222 insertions(+)
+ arch/powerpc/kernel/ptrace.c | 90 ++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 87 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
-index 38df7f0..0334c23 100644
+index 82db082..3baa57e 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
-@@ -181,6 +181,26 @@ static int set_user_msr(struct task_struct *task, unsigned long msr)
- return 0;
+@@ -518,10 +518,28 @@ static int vr_active(struct task_struct *target,
+ return target->thread.used_vr ? regset->n : 0;
}
++/*
++ * When the transaction is active, 'transact_vr' holds the current running
++ * value of all the VMX registers and 'vr_state' holds the last checkpointed
++ * value of all the VMX registers for the current transaction to fall back
++ * on in case it aborts. When transaction is not active 'vr_state' holds
++ * the current running state of all the VMX registers. So this function which
++ * gets the current running values of all the VMX registers, needs to know
++ * whether any transaction is active or not.
++ *
++ * Userspace interface buffer layout:
++ *
++ * struct data {
++ * vector128 vr[32];
++ * vector128 vscr;
++ * vector128 vrsave;
++ * };
++ */
+ static int vr_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+ {
++ struct thread_vr_state *addr;
+ int ret;
+
+ flush_altivec_to_thread(target);
+@@ -529,8 +547,19 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
+ BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
+ offsetof(struct thread_vr_state, vr[32]));
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-+static unsigned long get_user_ckpt_msr(struct task_struct *task)
-+{
-+ return task->thread.ckpt_regs.msr | task->thread.fpexc_mode;
-+}
++ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
++ flush_fp_to_thread(target);
++ flush_tmregs_to_thread(target);
++ addr = &target->thread.transact_vr;
++ } else {
++ addr = &target->thread.vr_state;
++ }
++#else
++ addr = &target->thread.vr_state;
++#endif
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+- &target->thread.vr_state, 0,
++ addr, 0,
+ 33 * sizeof(vector128));
+ if (!ret) {
+ /*
+@@ -541,7 +570,16 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
+ u32 word;
+ } vrsave;
+ memset(&vrsave, 0, sizeof(vrsave));
+
-+static int set_user_ckpt_msr(struct task_struct *task, unsigned long msr)
-+{
-+ task->thread.ckpt_regs.msr &= ~MSR_DEBUGCHANGE;
-+ task->thread.ckpt_regs.msr |= msr & MSR_DEBUGCHANGE;
-+ return 0;
-+}
-+
-+static int set_user_ckpt_trap(struct task_struct *task, unsigned long trap)
-+{
-+ task->thread.ckpt_regs.trap = trap & 0xfff0;
-+ return 0;
-+}
++#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
++ if (MSR_TM_ACTIVE(target->thread.regs->msr))
++ vrsave.word = target->thread.transact_vrsave;
++ else
++ vrsave.word = target->thread.vrsave;
++#else
+ vrsave.word = target->thread.vrsave;
+#endif
+
- #ifdef CONFIG_PPC64
- static int get_user_dscr(struct task_struct *task, unsigned long *data)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
+ 33 * sizeof(vector128), -1);
+ }
+@@ -549,10 +587,28 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
+ return ret;
+ }
+
++/*
++ * When the transaction is active, 'transact_vr' holds the current running
++ * value of all the VMX registers and 'vr_state' holds the last checkpointed
++ * value of all the VMX registers for the current transaction to fall back
++ * on in case it aborts. When transaction is not active 'vr_state' holds
++ * the current running state of all the VMX registers. So this function which
++ * sets the current running values of all the VMX registers, needs to know
++ * whether any transaction is active or not.
++ *
++ * Userspace interface buffer layout:
++ *
++ * struct data {
++ * vector128 vr[32];
++ * vector128 vscr;
++ * vector128 vrsave;
++ * };
++ */
+ static int vr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
{
-@@ -614,6 +634,172 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
- }
- #endif /* CONFIG_SPE */
++ struct thread_vr_state *addr;
+ int ret;
+
+ flush_altivec_to_thread(target);
+@@ -560,8 +616,19 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
+ BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) !=
+ offsetof(struct thread_vr_state, vr[32]));
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-+/**
-+ * tm_cgpr_active - get active number of registers in CGPR
-+ * @target: The target task.
-+ * @regset: The user regset structure.
-+ *
-+ * This function checks for the active number of available
-+ * regisers in transaction checkpointed GPR category.
-+ */
-+static int tm_cgpr_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)) {
++ flush_fp_to_thread(target);
++ flush_tmregs_to_thread(target);
++ addr = &target->thread.transact_vr;
++ } else {
++ addr = &target->thread.vr_state;
++ }
++#else
++ addr = &target->thread.vr_state;
++#endif
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+- &target->thread.vr_state, 0,
++ addr, 0,
+ 33 * sizeof(vector128));
+ if (!ret && count > 0) {
+ /*
+@@ -572,11 +639,28 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
+ u32 word;
+ } vrsave;
+ memset(&vrsave, 0, sizeof(vrsave));
+
-+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
-+ return 0;
++#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
++ if (MSR_TM_ACTIVE(target->thread.regs->msr))
++ vrsave.word = target->thread.transact_vrsave;
++ else
++ vrsave.word = target->thread.vrsave;
++#else
+ vrsave.word = target->thread.vrsave;
++#endif
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave,
+ 33 * sizeof(vector128), -1);
+- if (!ret)
++ if (!ret) {
+
-+ return regset->n;
-+}
-+
-+/**
-+ * tm_cgpr_get - get CGPR 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 transaction checkpointed GPR registers.
-+ *
-+ * When the transaction is active, 'ckpt_regs' holds all the checkpointed
-+ * GPR register values for the current transaction to fall back on if it
-+ * aborts in between. This function gets those checkpointed GPR registers.
-+ * The userspace interface buffer layout is as follows.
-+ *
-+ * struct data {
-+ * struct pt_regs ckpt_regs;
-+ * };
-+ */
-+static int tm_cgpr_get(struct task_struct *target,
-+ const struct user_regset *regset,
-+ unsigned int pos, unsigned int count,
-+ void *kbuf, void __user *ubuf)
-+{
-+ int ret;
-+
-+ 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_copyout(&pos, &count, &kbuf, &ubuf,
-+ &target->thread.ckpt_regs,
-+ 0, offsetof(struct pt_regs, msr));
-+ if (!ret) {
-+ unsigned long msr = get_user_ckpt_msr(target);
-+
-+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr,
-+ offsetof(struct pt_regs, msr),
-+ offsetof(struct pt_regs, msr) +
-+ sizeof(msr));
-+ }
-+
-+ BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) !=
-+ offsetof(struct pt_regs, msr) + sizeof(long));
-+
-+ if (!ret)
-+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
-+ &target->thread.ckpt_regs.orig_gpr3,
-+ offsetof(struct pt_regs, orig_gpr3),
-+ sizeof(struct pt_regs));
-+ if (!ret)
-+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
-+ sizeof(struct pt_regs), -1);
-+
-+ return ret;
-+}
-+
-+/*
-+ * tm_cgpr_set - set the CGPR 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 GPR registers.
-+ *
-+ * When the transaction is active, 'ckpt_regs' holds the checkpointed
-+ * GPR register values for the current transaction to fall back on if it
-+ * aborts in between. This function sets those checkpointed GPR registers.
-+ * The userspace interface buffer layout is as follows.
-+ *
-+ * struct data {
-+ * struct pt_regs ckpt_regs;
-+ * };
-+ */
-+static int tm_cgpr_set(struct task_struct *target,
-+ const struct user_regset *regset,
-+ unsigned int pos, unsigned int count,
-+ const void *kbuf, const void __user *ubuf)
-+{
-+ unsigned long reg;
-+ int ret;
-+
-+ 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.ckpt_regs,
-+ 0, PT_MSR * sizeof(reg));
-+
-+ if (!ret && count > 0) {
-+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®,
-+ PT_MSR * sizeof(reg),
-+ (PT_MSR + 1) * sizeof(reg));
-+ if (!ret)
-+ ret = set_user_ckpt_msr(target, reg);
-+ }
-+
-+ BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) !=
-+ offsetof(struct pt_regs, msr) + sizeof(long));
-+
-+ if (!ret)
-+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
-+ &target->thread.ckpt_regs.orig_gpr3,
-+ PT_ORIG_R3 * sizeof(reg),
-+ (PT_MAX_PUT_REG + 1) * sizeof(reg));
-+
-+ if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret)
-+ ret = user_regset_copyin_ignore(
-+ &pos, &count, &kbuf, &ubuf,
-+ (PT_MAX_PUT_REG + 1) * sizeof(reg),
-+ PT_TRAP * sizeof(reg));
-+
-+ if (!ret && count > 0) {
-+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®,
-+ PT_TRAP * sizeof(reg),
-+ (PT_TRAP + 1) * sizeof(reg));
-+ if (!ret)
-+ ret = set_user_ckpt_trap(target, reg);
-+ }
-+
-+ if (!ret)
-+ ret = user_regset_copyin_ignore(
-+ &pos, &count, &kbuf, &ubuf,
-+ (PT_TRAP + 1) * sizeof(reg), -1);
-+
-+ return ret;
-+}
++#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
++ if (MSR_TM_ACTIVE(target->thread.regs->msr))
++ target->thread.transact_vrsave = vrsave.word;
++ else
++ target->thread.vrsave = vrsave.word;
++#else
+ target->thread.vrsave = vrsave.word;
+#endif
++ }
+ }
- /*
- * These are our native regset flavors.
-@@ -630,6 +816,9 @@ enum powerpc_regset {
- #ifdef CONFIG_SPE
- REGSET_SPE,
- #endif
-+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-+ REGSET_TM_CGPR, /* TM checkpointed GPR registers */
-+#endif
- };
-
- static const struct user_regset native_regsets[] = {
-@@ -664,6 +853,13 @@ static const struct user_regset native_regsets[] = {
- .active = evr_active, .get = evr_get, .set = evr_set
- },
- #endif
-+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-+ [REGSET_TM_CGPR] = {
-+ .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
-+ .size = sizeof(long), .align = sizeof(long),
-+ .active = tm_cgpr_active, .get = tm_cgpr_get, .set = tm_cgpr_set
-+ },
-+#endif
- };
-
- static const struct user_regset_view user_ppc_native_view = {
-@@ -826,6 +1022,24 @@ static int gpr32_set_common(struct task_struct *target,
- (PT_TRAP + 1) * sizeof(reg), -1);
- }
-
-+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-+static int tm_cgpr32_get(struct task_struct *target,
-+ const struct user_regset *regset,
-+ unsigned int pos, unsigned int count,
-+ void *kbuf, void __user *ubuf)
-+{
-+ return gpr32_get_common(target, regset, pos, count, kbuf, ubuf, 1);
-+}
-+
-+static int tm_cgpr32_set(struct task_struct *target,
-+ const struct user_regset *regset,
-+ unsigned int pos, unsigned int count,
-+ const void *kbuf, const void __user *ubuf)
-+{
-+ return gpr32_set_common(target, regset, pos, count, kbuf, ubuf, 1);
-+}
-+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
-+
- static int gpr32_get(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
-@@ -870,6 +1084,14 @@ static const struct user_regset compat_regsets[] = {
- .active = evr_active, .get = evr_get, .set = evr_set
- },
- #endif
-+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-+ [REGSET_TM_CGPR] = {
-+ .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
-+ .size = sizeof(long), .align = sizeof(long),
-+ .active = tm_cgpr_active,
-+ .get = tm_cgpr32_get, .set = tm_cgpr32_set
-+ },
-+#endif
- };
-
- static const struct user_regset_view user_ppc_compat_view = {
+ return ret;
--
1.8.3.1