|
@@ -181,6 +181,26 @@ static int set_user_msr(struct task_struct *task, unsigned long msr)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#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;
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
#ifdef CONFIG_PPC64
|
|
|
static int get_user_dscr(struct task_struct *task, unsigned long *data)
|
|
|
{
|
|
@@ -847,6 +867,172 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
}
|
|
|
#endif /* CONFIG_SPE */
|
|
|
|
|
|
+#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))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|
|
|
+#endif
|
|
|
|
|
|
/*
|
|
|
* These are our native regset flavors.
|
|
@@ -863,6 +1049,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[] = {
|
|
@@ -897,6 +1086,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 = {
|
|
@@ -1059,6 +1255,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,
|
|
@@ -1103,6 +1317,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 = {
|