This patch series adds five new ELF core note sections which can be
used with existing ptrace request PTRACE_GETREGSET-SETREGSET for accessing
various transactional memory and miscellaneous debug register sets on PowerPC
platform.
Previous versions:
==================
RFC: https://lkml.org/lkml/2014/4/1/292
V1: https://lkml.org/lkml/2014/4/2/43
V2: https://lkml.org/lkml/2014/5/5/88
V3: https://lkml.org/lkml/2014/5/23/486
Changes in V4:
--------------
- Added one test program into the powerpc selftest bucket in this regard
- Split the 2nd patch in the previous series into four different patches
- Accommodated most of the review comments on the previous patch series
- Added a patch to merge functions __switch_to_tm and tm_reclaim_task
Changes in V3:
--------------
- Added two new error paths in every TM related get/set functions when regset
support is not present on the system (ENODEV) or when the process does not
have any transaction active (ENODATA) in the context
- Installed the active hooks for all the newly added regset core note types
Changes in V2:
--------------
- Removed all the power specific ptrace requests corresponding to new NT_PPC_*
elf core note types. Now all the register sets can be accessed from ptrace
through PTRACE_GETREGSET/PTRACE_SETREGSET using the individual NT_PPC* core
note type instead
- Fixed couple of attribute values for REGSET_TM_CGPR register set
- Renamed flush_tmreg_to_thread as flush_tmregs_to_thread
- Fixed 32 bit checkpointed GPR support
- Changed commit messages accordingly
Test Result
-----------
The patch series has been verified both in 32 bit and 64 bit compiled mode.
Test result for the selftest test (64 bit compiled mode) can be found here.
test: tm_ptrace
tags: git_version:v3.18-rc4-9-g000adb7
=============Testing TM based PTRACE calls==============
-------TM Specific SPR------
TFHAR: 10000cc8
TEXASR: de0000018c000001
TFIAR: c0000000000414f0
TM ORIG_MSR: 50000f032
TM CH DSCR: a (PASSED)
TM CH TAR: 14 (PASSED)
TM CH PPR: 8000000000000 (PASSED)
-------TM Checkpointed GPR-----
TM CH NIP: 10000cc8
TM CH LINK: 10000af8
TM CH CCR: 22000422
TM CH GPR[0]: 0 (PASSED)
TM CH GPR[1]: 1 (PASSED)
TM CH GPR[2]: 2 (PASSED)
TM CH GPR[3]: 3 (PASSED)
TM CH GPR[4]: 4 (PASSED)
TM CH GPR[5]: 5 (PASSED)
TM CH GPR[6]: 6 (PASSED)
TM CH GPR[7]: 7 (PASSED)
TM CH GPR[8]: 8 (PASSED)
TM CH GPR[9]: 9 (PASSED)
TM CH GPR[10]: a (PASSED)
TM CH GPR[11]: b (PASSED)
TM CH GPR[12]: c (PASSED)
TM CH GPR[13]: d (PASSED)
TM CH GPR[14]: e (PASSED)
TM CH GPR[15]: f (PASSED)
TM CH GPR[16]: 0 (PASSED)
TM CH GPR[17]: 1 (PASSED)
TM CH GPR[18]: 2 (PASSED)
TM CH GPR[19]: 3 (PASSED)
TM CH GPR[20]: 4 (PASSED)
TM CH GPR[21]: 5 (PASSED)
TM CH GPR[22]: 6 (PASSED)
TM CH GPR[23]: 7 (PASSED)
TM CH GPR[24]: 8 (PASSED)
TM CH GPR[25]: 9 (PASSED)
TM CH GPR[26]: a (PASSED)
TM CH GPR[27]: b (PASSED)
TM CH GPR[28]: c (PASSED)
TM CH GPR[29]: d (PASSED)
TM CH GPR[30]: e (PASSED)
TM CH GPR[31]: f (PASSED)
-------TM Checkpointed FPR-----
TM CH FPSCR: 0
TM CH FPR[0]: 0 (PASSED)
TM CH FPR[1]: 1 (PASSED)
TM CH FPR[2]: 2 (PASSED)
TM CH FPR[3]: 3 (PASSED)
TM CH FPR[4]: 4 (PASSED)
TM CH FPR[5]: 5 (PASSED)
TM CH FPR[6]: 6 (PASSED)
TM CH FPR[7]: 7 (PASSED)
TM CH FPR[8]: 8 (PASSED)
TM CH FPR[9]: 9 (PASSED)
TM CH FPR[10]: a (PASSED)
TM CH FPR[11]: b (PASSED)
TM CH FPR[12]: c (PASSED)
TM CH FPR[13]: d (PASSED)
TM CH FPR[14]: e (PASSED)
TM CH FPR[15]: f (PASSED)
TM CH FPR[16]: 0 (PASSED)
TM CH FPR[17]: 1 (PASSED)
TM CH FPR[18]: 2 (PASSED)
TM CH FPR[19]: 3 (PASSED)
TM CH FPR[20]: 4 (PASSED)
TM CH FPR[21]: 5 (PASSED)
TM CH FPR[22]: 6 (PASSED)
TM CH FPR[23]: 7 (PASSED)
TM CH FPR[24]: 8 (PASSED)
TM CH FPR[25]: 9 (PASSED)
TM CH FPR[26]: a (PASSED)
TM CH FPR[27]: b (PASSED)
TM CH FPR[28]: c (PASSED)
TM CH FPR[29]: d (PASSED)
TM CH FPR[30]: e (PASSED)
TM CH FPR[31]: f (PASSED)
-------TM Running GPR-----
TM RN NIP: 10000de0
TM RN LINK: 10000af8
TM RN CCR: 2000422
TM RN GPR[0]: f (PASSED)
TM RN GPR[1]: e (PASSED)
TM RN GPR[2]: d (PASSED)
TM RN GPR[3]: c (PASSED)
TM RN GPR[4]: b (PASSED)
TM RN GPR[5]: a (PASSED)
TM RN GPR[6]: 9 (PASSED)
TM RN GPR[7]: 8 (PASSED)
TM RN GPR[8]: 7 (PASSED)
TM RN GPR[9]: 6 (PASSED)
TM RN GPR[10]: 5 (PASSED)
TM RN GPR[11]: 4 (PASSED)
TM RN GPR[12]: 3 (PASSED)
TM RN GPR[13]: 2 (PASSED)
TM RN GPR[14]: 1 (PASSED)
TM RN GPR[15]: 0 (PASSED)
TM RN GPR[16]: f (PASSED)
TM RN GPR[17]: e (PASSED)
TM RN GPR[18]: d (PASSED)
TM RN GPR[19]: c (PASSED)
TM RN GPR[20]: b (PASSED)
TM RN GPR[21]: a (PASSED)
TM RN GPR[22]: 9 (PASSED)
TM RN GPR[23]: 8 (PASSED)
TM RN GPR[24]: 7 (PASSED)
TM RN GPR[25]: 6 (PASSED)
TM RN GPR[26]: 5 (PASSED)
TM RN GPR[27]: 4 (PASSED)
TM RN GPR[28]: 3 (PASSED)
TM RN GPR[29]: 2 (PASSED)
TM RN GPR[30]: 1 (PASSED)
TM RN GPR[31]: 0 (PASSED)
-------TM Running FPR-----
TM RN FPSCR: 0
TM RN FPR[0]: f (PASSED)
TM RN FPR[1]: e (PASSED)
TM RN FPR[2]: d (PASSED)
TM RN FPR[3]: c (PASSED)
TM RN FPR[4]: b (PASSED)
TM RN FPR[5]: a (PASSED)
TM RN FPR[6]: 9 (PASSED)
TM RN FPR[7]: 8 (PASSED)
TM RN FPR[8]: 7 (PASSED)
TM RN FPR[9]: 6 (PASSED)
TM RN FPR[10]: 5 (PASSED)
TM RN FPR[11]: 4 (PASSED)
TM RN FPR[12]: 3 (PASSED)
TM RN FPR[13]: 2 (PASSED)
TM RN FPR[14]: 1 (PASSED)
TM RN FPR[15]: 0 (PASSED)
TM RN FPR[16]: f (PASSED)
TM RN FPR[17]: e (PASSED)
TM RN FPR[18]: d (PASSED)
TM RN FPR[19]: c (PASSED)
TM RN FPR[20]: b (PASSED)
TM RN FPR[21]: a (PASSED)
TM RN FPR[22]: 9 (PASSED)
TM RN FPR[23]: 8 (PASSED)
TM RN FPR[24]: 7 (PASSED)
TM RN FPR[25]: 6 (PASSED)
TM RN FPR[26]: 5 (PASSED)
TM RN FPR[27]: 4 (PASSED)
TM RN FPR[28]: 3 (PASSED)
TM RN FPR[29]: 2 (PASSED)
TM RN FPR[30]: 1 (PASSED)
TM RN FPR[31]: 0 (PASSED)
-------TM Running MISC Registers-------
TM RN DSCR: 32 (PASSED)
TM RN TAR: 3c (PASSED)
TM RN PPR: 4000000000000 (PASSED)
success: tm_ptrace
Anshuman Khandual (8):
elf: Add new PowerPC specifc core note sections
powerpc, process: Add functions flush_tm_state, flush_tmregs_to_thread
powerpc, process: Merge functions __switch_to_tm and tm_reclaim_task
powerpc, ptrace: Enable fpr_(get/set) for transactional memory
powerpc, ptrace: Enable vr_(get/set) for transactional memory
powerpc, ptrace: Enable support for transactional memory register sets
powerpc, ptrace: Enable support for miscellaneous debug registers
selftests, powerpc: Add new test case for TM related ptrace interfaces
arch/powerpc/include/asm/switch_to.h | 8 +
arch/powerpc/include/uapi/asm/elf.h | 3 +
arch/powerpc/kernel/process.c | 65 +-
arch/powerpc/kernel/ptrace.c | 965 ++++++++++++++++++++++++-
include/uapi/linux/elf.h | 5 +
tools/testing/selftests/powerpc/tm/Makefile | 2 +-
tools/testing/selftests/powerpc/tm/tm-ptrace.c | 529 ++++++++++++++
7 files changed, 1533 insertions(+), 44 deletions(-)
create mode 100644 tools/testing/selftests/powerpc/tm/tm-ptrace.c
--
1.9.3
This patch adds four new core note sections for PowerPC transactional
memory and one core note section for general miscellaneous debug registers.
These addition of new elf core note sections extends the existing elf ABI
without affecting it in any manner.
Signed-off-by: Anshuman Khandual <[email protected]>
---
include/uapi/linux/elf.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
index ea9bf25..2260fc0 100644
--- a/include/uapi/linux/elf.h
+++ b/include/uapi/linux/elf.h
@@ -379,6 +379,11 @@ typedef struct elf64_shdr {
#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
+#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
+#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
+#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
+#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
+#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
--
1.9.3
The function tm_reclaim_task is only called from the function
__switch_to_tm. This patch merges these two functions to make
it more readable without changing the functionality in any way.
Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/kernel/process.c | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index c36477b..99b41ed 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -585,6 +585,10 @@ static inline void tm_reclaim_task(struct task_struct *tsk)
*/
struct thread_struct *thr = &tsk->thread;
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return;
+ tm_enable();
+
if (!thr->regs)
return;
@@ -696,14 +700,6 @@ static inline void tm_recheckpoint_new_task(struct task_struct *new)
new->pid, mfmsr());
}
-static inline void __switch_to_tm(struct task_struct *prev)
-{
- if (cpu_has_feature(CPU_FTR_TM)) {
- tm_enable();
- tm_reclaim_task(prev);
- }
-}
-
/*
* This is called if we are on the way out to userspace and the
* TIF_RESTORE_TM flag is set. It checks if we need to reload
@@ -742,13 +738,13 @@ void restore_tm_state(struct pt_regs *regs)
#else
#define tm_recheckpoint_new_task(new)
-#define __switch_to_tm(prev)
+#define tm_reclaim_task(prev)
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
/*
* flush_tm_state
*
- * Flush TM state out so we can copy it. __switch_to_tm() does this
+ * Flush TM state out so we can copy it. tm_reclaim_task() does this
* flush but it removes the checkpointed state from the current CPU and
* transitions the CPU out of TM mode. Hence we need to call
* tm_recheckpoint_new_task() (on the same task) to restore the
@@ -756,7 +752,7 @@ void restore_tm_state(struct pt_regs *regs)
*/
static void flush_tm_state(struct task_struct *src)
{
- __switch_to_tm(src);
+ tm_reclaim_task(src);
tm_recheckpoint_new_task(src);
}
@@ -805,7 +801,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
*/
save_early_sprs(&prev->thread);
- __switch_to_tm(prev);
+ tm_reclaim_task(prev);
#ifdef CONFIG_SMP
/* avoid complexity of lazy save/restore of fpu
--
1.9.3
This patch enables the fpr_get which gets the running value of all
the FPR registers and the fpr_set which sets the running value of
of all the FPR registers to accommodate in transaction ptrace
interface based requests.
Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 103 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 97 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index f21897b..fd36b32 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -357,6 +357,33 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}
+
+/*
+ * fpr_get
+ *
+ * When the transaction is active, 'transact_fp' holds the current running
+ * value of all FPR registers and 'fp_state' holds the last checkpointed
+ * value of all FPR registers for the current transaction. When transaction
+ * is not active 'fp_state' holds the current running state of all the FPR
+ * registers. So this function which returns the current running values of
+ * all the FPR registers, needs to know whether any transaction is active
+ * or not.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ * };
+ *
+ * There are two config options CONFIG_VSX and CONFIG_PPC_TRANSACTIONAL_MEM
+ * which determines the final code in this function. All the combinations of
+ * these two config options are possible except the one below as transactional
+ * memory config pulls in CONFIG_VSX automatically.
+ *
+ * !defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ *
+ */
static int fpr_get(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
@@ -367,22 +394,64 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset,
#endif
flush_fp_to_thread(target);
-#ifdef CONFIG_VSX
+#if defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ /* copy to local buffer then write that out */
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_TRANS_FPR(i);
+ buf[32] = target->thread.transact_fp.fpscr;
+ } else {
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_FPR(i);
+ buf[32] = target->thread.fp_state.fpscr;
+ }
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+#endif
+
+#if defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
/* copy to local buffer then write that out */
for (i = 0; i < 32 ; i++)
buf[i] = target->thread.TS_FPR(i);
buf[32] = target->thread.fp_state.fpscr;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+#endif
-#else
+
+#if !defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
offsetof(struct thread_fp_state, fpr[32][0]));
-
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
#endif
}
+/*
+ * fpr_set
+ *
+ * When the transaction is active, 'transact_fp' holds the current running
+ * value of all FPR registers and 'fp_state' holds the last checkpointed
+ * value of all FPR registers for the current transaction. When transaction
+ * is not active 'fp_state' holds the current running state of all the FPR
+ * registers. So this function which setss the current running values of
+ * all the FPR registers, needs to know whether any transaction is active
+ * or not.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ * };
+ *
+ * There are two config options CONFIG_VSX and CONFIG_PPC_TRANSACTIONAL_MEM
+ * which determines the final code in this function. All the combinations of
+ * these two config options are possible except the one below as transactional
+ * memory config pulls in CONFIG_VSX automatically.
+ *
+ * !defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ */
static int fpr_set(struct task_struct *target, const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
@@ -393,7 +462,27 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
#endif
flush_fp_to_thread(target);
-#ifdef CONFIG_VSX
+#if defined(CONFIG_VSX) && defined(CONFIG_PPC_TRANSACTIONAL_MEM)
+ /* copy to local buffer then write that out */
+ i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+ if (i)
+ return i;
+
+ if (MSR_TM_ACTIVE(target->thread.regs->msr)) {
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ target->thread.TS_TRANS_FPR(i) = buf[i];
+ target->thread.transact_fp.fpscr = buf[32];
+ } else {
+ for (i = 0; i < 32 ; i++)
+ target->thread.TS_FPR(i) = buf[i];
+ target->thread.fp_state.fpscr = buf[32];
+ }
+ return 0;
+#endif
+
+#if defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
/* copy to local buffer then write that out */
i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
if (i)
@@ -402,12 +491,14 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
target->thread.TS_FPR(i) = buf[i];
target->thread.fp_state.fpscr = buf[32];
return 0;
-#else
+#endif
+
+#if !defined(CONFIG_VSX) && !defined(CONFIG_PPC_TRANSACTIONAL_MEM)
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
offsetof(struct thread_fp_state, fpr[32][0]));
-
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
+
#endif
}
--
1.9.3
This patch enables the vr_get which gets the running value of all
the VMX registers and the vr_set which sets the running value of
of all the VMX registers to accommodate in transaction ptrace
interface based requests.
Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/kernel/ptrace.c | 94 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index fd36b32..2bbbd10 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -523,10 +523,30 @@ static int vr_active(struct task_struct *target,
return target->thread.used_vr ? regset->n : 0;
}
+/*
+ * vr_get
+ *
+ * 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);
@@ -534,8 +554,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
+ 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) {
/*
@@ -546,7 +577,16 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
u32 word;
} vrsave;
memset(&vrsave, 0, sizeof(vrsave));
+
+#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_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
33 * sizeof(vector128), -1);
}
@@ -554,10 +594,30 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset,
return ret;
}
+/*
+ * vr_set
+ *
+ * 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)
{
+ struct thread_vr_state *addr;
int ret;
flush_altivec_to_thread(target);
@@ -565,8 +625,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
+ 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) {
/*
@@ -577,11 +648,28 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset,
u32 word;
} vrsave;
memset(&vrsave, 0, sizeof(vrsave));
+
+#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) {
+
+#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
+ }
}
return ret;
--
1.9.3
This patch adds one more test case called tm-ptrace targeting TM
related ptrace interfaces. This test creates one child process to
run some basic transactions and the parent process attaches the
child to do some ptrace probing using the recently added regset
interfaces. The parent process then compares the received values
against the expected values to verify whether it passed the test
or not.
Signed-off-by: Anshuman Khandual <[email protected]>
---
tools/testing/selftests/powerpc/tm/Makefile | 2 +-
tools/testing/selftests/powerpc/tm/tm-ptrace.c | 529 +++++++++++++++++++++++++
2 files changed, 530 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/tm/tm-ptrace.c
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
index 2cede23..71d400a 100644
--- a/tools/testing/selftests/powerpc/tm/Makefile
+++ b/tools/testing/selftests/powerpc/tm/Makefile
@@ -1,4 +1,4 @@
-PROGS := tm-resched-dscr
+PROGS := tm-resched-dscr tm-ptrace
all: $(PROGS)
diff --git a/tools/testing/selftests/powerpc/tm/tm-ptrace.c b/tools/testing/selftests/powerpc/tm/tm-ptrace.c
new file mode 100644
index 0000000..858e77e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/tm/tm-ptrace.c
@@ -0,0 +1,529 @@
+/*
+ * Test program for TM ptrace interface
+ *
+ * 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.
+ *
+ * Copyright 2014 IBM Corporation
+ *
+ * Author: Anshuman Khandual <[email protected]>
+ */
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <linux/elf.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+
+#include "utils.h"
+
+#define MAX_OUTPUT 100
+
+/* ELF core notes */
+#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
+#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
+#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
+#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
+#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
+
+/* TM instructions */
+#define TBEGIN ".long 0x7C00051D ;"
+#define TEND ".long 0x7C00055D ;"
+
+/* SPR number */
+#define SPRN_DSCR 3
+#define SPRN_TAR 815
+#define SPRN_PPR 896
+
+#define C_DSCR 10 /* TM checkpointed DSCR */
+#define C_TAR 20 /* TM checkpointed TAR */
+#define C_PPR 0x8000000000000 /* TM checkpointed PPR */
+
+#define DSCR 50 /* TM running DSCR */
+#define TAR 60 /* TM running TAR */
+#define PPR 0x4000000000000 /* TM running PPR */
+
+/* Values for GPR-FPR[0..31] */
+#define VAL0 0
+#define VAL1 1
+#define VAL2 2
+#define VAL3 3
+#define VAL4 4
+#define VAL5 5
+#define VAL6 6
+#define VAL7 7
+#define VAL8 8
+#define VAL9 9
+#define VAL10 10
+#define VAL11 11
+#define VAL12 12
+#define VAL13 13
+#define VAL14 14
+#define VAL15 15
+#define VAL_MAX 16
+
+/* Standard data types */
+typedef unsigned int u32;
+typedef __vector128 vector128;
+
+/* NT_PPC_TM_SPR buffer layout */
+struct tm_spr_regs {
+ u64 tm_tfhar;
+ u64 tm_texasr;
+ u64 tm_tfiar;
+ u64 tm_orig_msr;
+ u64 tm_tar;
+ u64 tm_ppr;
+ u64 tm_dscr;
+};
+
+/*
+ * NT_PPC_TM_CGPR buffer layout
+ *
+ * Same as that of struct pt_regs
+ */
+
+/* NT_PPC_TM_CFPR buffer layout */
+struct tm_cfpr {
+ u64 fpr[32];
+ u64 fpscr;
+};
+
+/* NT_PPC_TM_CVMX buffer layout */
+struct tm_cvmx {
+ vector128 vr[32] __attribute__((aligned(16)));
+ vector128 vscr __attribute__((aligned(16)));
+ u32 vrsave;
+};
+
+/* NT_PPC_MISC buffer layout */
+struct misc_regs {
+ u64 dscr;
+ u64 ppr;
+ u64 tar;
+};
+
+/*
+ * do_transaction
+ *
+ * This functions sets the values for TAR, DSCR, PPR, GPR[0..31],
+ * FPR[0..31] registers before starting the trasanction which will
+ * enable the kernel to save them as checkpointed values. Then it
+ * starts the transaction where it loads a different set of values
+ * into the same registers again thus enabling the kernel to save
+ * them off as running values for this transaction. Then the function
+ * gets stuck forcing the process to loop at one single instruction.
+ * The transaction never finishes, thus giving the parent process
+ * the opportunity to trace the running and checkpointed values of
+ * various registers.
+ */
+void do_transaction(void)
+{
+ asm __volatile__(
+ /* TM checkpointed values */
+
+ /* SPR */
+ "li 0, %[c_tar];" /* TAR */
+ "mtspr %[sprn_tar], 0;"
+ "li 0, %[c_dscr];" /* DSCR */
+ "mtspr %[sprn_dscr], 0;"
+ "or 1,1,1;" /* PPR (0x8000000000000) */
+
+ /* GPR[0..31] */
+ "li 0, %[val0];" /* GPR[0] */
+ "li 1, %[val1];" /* GPR[1] */
+ "li 2, %[val2];" /* GPR[2] */
+ "li 3, %[val3];" /* GPR[3] */
+ "li 4, %[val4];" /* GPR[4] */
+ "li 5, %[val5];" /* GPR[5] */
+ "li 6, %[val6];" /* GPR[6] */
+ "li 7, %[val7];" /* GPR[7] */
+ "li 8, %[val8];" /* GPR[8] */
+ "li 9, %[val9];" /* GPR[9] */
+ "li 10, %[val10];" /* GPR[10] */
+ "li 11, %[val11];" /* GPR[11] */
+ "li 12, %[val12];" /* GPR[12] */
+ "li 13, %[val13];" /* GPR[13] */
+ "li 14, %[val14];" /* GPR[14] */
+ "li 15, %[val15];" /* GPR[15] */
+ "li 16, %[val0];" /* GPR[16] */
+ "li 17, %[val1];" /* GPR[17] */
+ "li 18, %[val2];" /* GPR[18] */
+ "li 19, %[val3];" /* GPR[19] */
+ "li 20, %[val4];" /* GPR[20] */
+ "li 21, %[val5];" /* GPR[21] */
+ "li 22, %[val6];" /* GPR[22] */
+ "li 23, %[val7];" /* GPR[23] */
+ "li 24, %[val8];" /* GPR[24] */
+ "li 25, %[val9];" /* GPR[25] */
+ "li 26, %[val10];" /* GPR[26] */
+ "li 27, %[val11];" /* GPR[27] */
+ "li 28, %[val12];" /* GPR[28] */
+ "li 29, %[val13];" /* GPR[29] */
+ "li 30, %[val14];" /* GPR[30] */
+ "li 31, %[val15];" /* GPR[31] */
+
+ /* FPR[0..31] */
+ ".long 0x7C000166;" /* GPR[0] --> FPR[0] */
+ ".long 0x7C210166;" /* GPR[1] --> FPR[1] */
+ ".long 0x7C420166;" /* GPR[0] --> FPR[2] */
+ ".long 0x7C630166;" /* GPR[3] --> FPR[3] */
+ ".long 0x7C840166;" /* GPR[4] --> FPR[4] */
+ ".long 0x7CA50166;" /* GPR[5] --> FPR[5] */
+ ".long 0x7CC60166;" /* GPR[6] --> FPR[6] */
+ ".long 0x7CE70166;" /* GPR[7] --> FPR[7] */
+ ".long 0x7D080166;" /* GPR[8] --> FPR[8] */
+ ".long 0x7D290166;" /* GPR[9] --> FPR[9] */
+ ".long 0x7d4a0166;" /* GPR[10] --> FPR[10] */
+ ".long 0x7d6b0166;" /* GPR[11] --> FPR[11] */
+ ".long 0x7d8c0166;" /* GPR[12] --> FPR[12] */
+ ".long 0x7dad0166;" /* GPR[13] --> FPR[13] */
+ ".long 0x7dce0166;" /* GPR[14] --> FPR[14] */
+ ".long 0x7def0166;" /* GPR[15] --> FPR[15] */
+ ".long 0x7e100166;" /* GPR[16] --> FPR[16] */
+ ".long 0x7e310166;" /* GPR[17] --> FPR[17] */
+ ".long 0x7e520166;" /* GPR[18] --> FPR[18] */
+ ".long 0x7e730166;" /* GPR[19] --> FPR[19] */
+ ".long 0x7e940166;" /* GPR[20] --> FPR[20] */
+ ".long 0x7eb50166;" /* GPR[21] --> FPR[21] */
+ ".long 0x7ed60166;" /* GPR[22] --> FPR[22] */
+ ".long 0x7ef70166;" /* GPR[23] --> FPR[23] */
+ ".long 0x7f180166;" /* GPR[24] --> FPR[24] */
+ ".long 0x7f390166;" /* GPR[25] --> FPR[25] */
+ ".long 0x7f5a0166;" /* GPR[26] --> FPR[26] */
+ ".long 0x7f7b0166;" /* GPR[27] --> FPR[27] */
+ ".long 0x7f9c0166;" /* GPR[28] --> FPR[28] */
+ ".long 0x7fbd0166;" /* GPR[29] --> FPR[29] */
+ ".long 0x7fde0166;" /* GPR[30] --> FPR[30] */
+ ".long 0x7fff0166;" /* GPR[31] --> FPR[31] */
+
+ /* TM running values */
+
+ "1: ;"
+ TBEGIN
+ "beq 2f;"
+
+ /* SPR */
+ "li 0, %[tar];" /* TAR */
+ "mtspr %[sprn_tar], 0;"
+ "li 0, %[dscr];" /* DSCR */
+ "mtspr %[sprn_dscr], 0;"
+ "or 31,31,31;" /* PPR (0x4000000000000) */
+
+ /* GPR[0..31] */
+ "li 0, %[val15];" /* GPR[0] */
+ "li 1, %[val14];" /* GPR[1] */
+ "li 2, %[val13];" /* GPR[2] */
+ "li 3, %[val12];" /* GPR[3] */
+ "li 4, %[val11];" /* GPR[4] */
+ "li 5, %[val10];" /* GPR[5] */
+ "li 6, %[val9];" /* GPR[6] */
+ "li 7, %[val8];" /* GPR[7] */
+ "li 8, %[val7];" /* GPR[8] */
+ "li 9, %[val6];" /* GPR[9] */
+ "li 10, %[val5];" /* GPR[10] */
+ "li 11, %[val4];" /* GPR[11] */
+ "li 12, %[val3];" /* GPR[12] */
+ "li 13, %[val2];" /* GPR[13] */
+ "li 14, %[val1];" /* GPR[14] */
+ "li 15, %[val0];" /* GPR[15] */
+ "li 16, %[val15];" /* GPR[16] */
+ "li 17, %[val14];" /* GPR[17] */
+ "li 18, %[val13];" /* GPR[18] */
+ "li 19, %[val12];" /* GPR[19] */
+ "li 20, %[val11];" /* GPR[20] */
+ "li 21, %[val10];" /* GPR[21] */
+ "li 22, %[val9];" /* GPR[22] */
+ "li 23, %[val8];" /* GPR[23] */
+ "li 24, %[val7];" /* GPR[24] */
+ "li 25, %[val6];" /* GPR[25] */
+ "li 26, %[val5];" /* GPR[26] */
+ "li 27, %[val4];" /* GPR[27] */
+ "li 28, %[val3];" /* GPR[28] */
+ "li 29, %[val2];" /* GPR[29] */
+ "li 30, %[val1];" /* GPR[30] */
+ "li 31, %[val0];" /* GPR[31] */
+
+ /* FPR[0..31] */
+ ".long 0x7C000166;" /* GPR[0] --> FPR[0] */
+ ".long 0x7C210166;" /* GPR[1] --> FPR[1] */
+ ".long 0x7C420166;" /* GPR[2] --> FPR[2] */
+ ".long 0x7C630166;" /* GPR[3] --> FPR[3] */
+ ".long 0x7C840166;" /* GPR[4] --> FPR[4] */
+ ".long 0x7CA50166;" /* GPR[5] --> FPR[5] */
+ ".long 0x7CC60166;" /* GPR[6] --> FPR[6] */
+ ".long 0x7CE70166;" /* GPR[7] --> FPR[7] */
+ ".long 0x7D080166;" /* GPR[8] --> FPR[8] */
+ ".long 0x7D290166;" /* GPR[9] --> FPR[9] */
+ ".long 0x7d4a0166;" /* GPR[10] --> FPR[10] */
+ ".long 0x7d6b0166;" /* GPR[11] --> FPR[11] */
+ ".long 0x7d8c0166;" /* GPR[12] --> FPR[12] */
+ ".long 0x7dad0166;" /* GPR[13] --> FPR[13] */
+ ".long 0x7dce0166;" /* GPR[14] --> FPR[14] */
+ ".long 0x7def0166;" /* GPR[15] --> FPR[15] */
+ ".long 0x7e100166;" /* GPR[16] --> FPR[16] */
+ ".long 0x7e310166;" /* GPR[17] --> FPR[17] */
+ ".long 0x7e520166;" /* GPR[18] --> FPR[18] */
+ ".long 0x7e730166;" /* GPR[19] --> FPR[19] */
+ ".long 0x7e940166;" /* GPR[20] --> FPR[20] */
+ ".long 0x7eb50166;" /* GPR[21] --> FPR[21] */
+ ".long 0x7ed60166;" /* GPR[22] --> FPR[22] */
+ ".long 0x7ef70166;" /* GPR[23] --> FPR[23] */
+ ".long 0x7f180166;" /* GPR[24] --> FPR[24] */
+ ".long 0x7f390166;" /* GPR[25] --> FPR[25] */
+ ".long 0x7f5a0166;" /* GPR[26] --> FPR[26] */
+ ".long 0x7f7b0166;" /* GPR[27] --> FPR[27] */
+ ".long 0x7f9c0166;" /* GPR[28] --> FPR[28] */
+ ".long 0x7fbd0166;" /* GPR[29] --> FPR[29] */
+ ".long 0x7fde0166;" /* GPR[30] --> FPR[30] */
+ ".long 0x7fff0166;" /* GPR[31] --> FPR[31] */
+
+ "b .;" /* Get stuck here */
+ TEND
+
+ /* Transaction abort handler */
+ "2: ;"
+ "b 1b;" /* Start from TBEGIN */
+
+ :: [sprn_dscr]"i"(SPRN_DSCR), [sprn_tar]"i"(SPRN_TAR),
+ [sprn_ppr]"i"(SPRN_PPR), [val0]"i"(VAL0),
+ [val1]"i"(VAL1), [val2]"i"(VAL2), [val3]"i"(VAL3),
+ [val4]"i"(VAL4), [val5]"i"(VAL5), [val6]"i"(VAL6),
+ [val7]"i"(VAL7), [val8]"i"(VAL8), [val9]"i"(VAL9),
+ [val10]"i"(VAL10), [val11]"i"(VAL11), [val12]"i"(VAL12),
+ [val13]"i"(VAL13), [val14]"i"(VAL14), [val15]"i"(VAL15),
+ [c_tar]"i"(C_TAR), [c_dscr]"i"(C_DSCR), [tar]"i"(TAR),
+ [dscr]"i"(DSCR), [ppr]"i"(PPR), [c_ppr]"i"(C_PPR)
+ : "memory", "r7");
+}
+
+void test_result(const u64 variable, const u64 value, const char *str)
+{
+ if (variable == value)
+ printf("%s: %llx (PASSED)\n", str, variable);
+ else
+ printf("%s: %llx (FAILED)\n", str, variable);
+}
+
+int trace_transaction(pid_t child)
+{
+ struct tm_spr_regs *tmspr;
+ struct pt_regs *cregs, *regs;
+ struct tm_cfpr *cfpr, *fpr;
+ struct misc_regs *mregs;
+ struct iovec iov;
+ char str[MAX_OUTPUT];
+ int ret, i, j;
+
+ regs = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ fpr = (struct tm_cfpr *) malloc(sizeof(struct tm_cfpr));
+
+ /* Wait till the tracee hits "b ." instruction */
+ sleep(3);
+
+ ret = ptrace(PTRACE_ATTACH, child, NULL, NULL);
+ if (ret) {
+ printf("ptrace(PTRACE_ATTACH) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ ret = waitpid(child, NULL, 0);
+ if (ret != child) {
+ printf("PID mismatch: %s\n", strerror(errno));
+ return 1;
+ }
+
+ /* TM specific SPR */
+ iov.iov_base = (struct tm_spr_regs *)
+ malloc(sizeof(struct tm_spr_regs));
+ iov.iov_len = sizeof(struct tm_spr_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_SPR, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_TM_SPR) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (iov.iov_len != sizeof(struct tm_spr_regs)) {
+ printf("ptrace(NT_PPC_TM_SPR): Returned wrong length\n");
+ return 1;
+ }
+
+ printf("-------TM Specific SPR------\n");
+ tmspr = iov.iov_base;
+
+ printf("TFHAR: %llx\n", tmspr->tm_tfhar);
+ printf("TEXASR: %llx\n", tmspr->tm_texasr);
+ printf("TFIAR: %llx\n", tmspr->tm_tfiar);
+ printf("TM ORIG_MSR: %llx\n", tmspr->tm_orig_msr);
+
+ test_result(tmspr->tm_dscr, C_DSCR, "TM CH DSCR");
+ test_result(tmspr->tm_tar, C_TAR, "TM CH TAR");
+ test_result(tmspr->tm_ppr, C_PPR, "TM CH PPR");
+
+ /* TM checkpointed GPR */
+ iov.iov_base = (struct pt_regs *) malloc(sizeof(struct pt_regs));
+ iov.iov_len = sizeof(struct pt_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_TM_CGPR) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (iov.iov_len != sizeof(struct pt_regs)) {
+ printf("ptrace(NT_PPC_TM_CGPR): Returned wrong length\n");
+ return 1;
+ }
+
+ printf("-------TM Checkpointed GPR-----\n");
+ cregs = iov.iov_base;
+
+ printf("TM CH NIP: %lx\n", cregs->nip);
+ printf("TM CH LINK: %lx\n", cregs->link);
+ printf("TM CH CCR: %lx\n", cregs->ccr);
+
+ for (i = 0; i < VAL_MAX; i++) {
+ sprintf(str, "TM CH GPR[%d]", i);
+ test_result(cregs->gpr[i], i, str);
+ }
+
+ for (j = 0; i < VAL_MAX * 2; j++, i++) {
+ sprintf(str, "TM CH GPR[%d]", i);
+ test_result(cregs->gpr[i], j, str);
+ }
+
+ /* TM checkpointed FPR */
+ iov.iov_base = (struct tm_cfpr *) malloc(sizeof(struct tm_cfpr));
+ iov.iov_len = sizeof(struct tm_cfpr);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_TM_CFPR) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (iov.iov_len != sizeof(struct tm_cfpr)) {
+ printf("ptrace(NT_PPC_TM_CFPR): Returned wrong length\n");
+ return 1;
+ }
+
+ printf("-------TM Checkpointed FPR-----\n");
+ cfpr = iov.iov_base;
+ printf("TM CH FPSCR: %llx\n", cfpr->fpscr);
+
+ for (i = 0; i < VAL_MAX; i++) {
+ sprintf(str, "TM CH FPR[%d]", i);
+ test_result(cfpr->fpr[i], i, str);
+ }
+
+ for (j = 0; i < VAL_MAX * 2; j++, i++) {
+ sprintf(str, "TM CH FPR[%d]", i);
+ test_result(cfpr->fpr[i], j, str);
+ }
+
+ /* TM running GPR */
+ ret = ptrace(PTRACE_GETREGS, child, NULL, regs);
+ if (ret) {
+ printf("ptrace(PTRACE_GETREGS) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ printf("-------TM Running GPR-----\n");
+ printf("TM RN NIP: %lx\n", regs->nip);
+ printf("TM RN LINK: %lx\n", regs->link);
+ printf("TM RN CCR: %lx\n", regs->ccr);
+
+ for (i = 0, j = VAL_MAX - 1; i < VAL_MAX; i++, j--) {
+ sprintf(str, "TM RN GPR[%d]", i);
+ test_result(regs->gpr[i], j, str);
+ }
+
+ for (j = VAL_MAX - 1 ; i < VAL_MAX * 2; i++, j--) {
+ sprintf(str, "TM RN GPR[%d]", i);
+ test_result(regs->gpr[i], j, str);
+ }
+
+ /* TM running FPR */
+ ret = ptrace(PTRACE_GETFPREGS, child, NULL, fpr);
+ if (ret) {
+ printf("ptrace(PTRACE_GETFPREGS) Failed: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ printf("-------TM Running FPR-----\n");
+ printf("TM RN FPSCR: %llx\n", fpr->fpscr);
+
+ for (i = 0, j = VAL_MAX - 1; i < VAL_MAX; i++, j--) {
+ sprintf(str, "TM RN FPR[%d]", i);
+ test_result(fpr->fpr[i], j, str);
+ }
+
+ for (j = VAL_MAX - 1; i < VAL_MAX * 2; i++, j--) {
+ sprintf(str, "TM RN FPR[%d]", i);
+ test_result(fpr->fpr[i], j, str);
+ }
+
+ /* Running misc debug registers */
+ iov.iov_base = (struct misc_regs *) malloc(sizeof(struct misc_regs));
+ iov.iov_len = sizeof(struct misc_regs);
+ ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_MISC, &iov);
+ if (ret) {
+ printf("ptrace(NT_PPC_MISC): Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (iov.iov_len != sizeof(struct misc_regs)) {
+ printf("ptrace(NT_PPC_TM_MISC): Returned wrong length\n");
+ return 1;
+ }
+
+ printf("-------TM Running MISC Registers-------\n");
+ mregs = iov.iov_base;
+ test_result(mregs->dscr, DSCR, "TM RN DSCR");
+ test_result(mregs->tar, TAR, "TM RN TAR");
+ test_result(mregs->ppr, PPR, "TM RN PPR");
+
+ ret = ptrace(PTRACE_DETACH, child, NULL, NULL);
+ if (ret) {
+ printf("ptrace(PTRACE_DETACH) Failed: %s\n", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+int tm_ptrace_test(void)
+{
+ pid_t child;
+
+ printf("=============Testing TM based PTRACE calls==============\n");
+ fflush(stdout);
+ child = fork();
+ if (child < 0) {
+ printf("fork() Failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ /* Child to run the transaction */
+ if (child == 0)
+ do_transaction();
+
+ /* Parent to trace the child */
+ if (child)
+ trace_transaction(child);
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(tm_ptrace_test, "tm_ptrace");
+}
--
1.9.3
This patch enables get and set of miscellaneous debug registers through
ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing new
powerpc specific register set REGSET_MISC support corresponding to the
new ELF core note NT_PPC_MISC added previously in this regard.
Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/include/uapi/asm/elf.h | 1 +
arch/powerpc/kernel/ptrace.c | 102 ++++++++++++++++++++++++++++++++++++
2 files changed, 103 insertions(+)
diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
index fdc8e2f..a41bd98 100644
--- a/arch/powerpc/include/uapi/asm/elf.h
+++ b/arch/powerpc/include/uapi/asm/elf.h
@@ -93,6 +93,7 @@
#define ELF_NFPREG 33 /* includes fpscr */
#define ELF_NVMX 34 /* includes all vector registers */
#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
+#define ELF_NMISCREG 3 /* includes dscr, tar, ppr */
typedef unsigned long elf_greg_t64;
typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index b279947..61a2581 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -1327,6 +1327,97 @@ static int tm_cvmx_set(struct task_struct *target,
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
/*
+ * get_misc_dbg
+ *
+ * This function gets miscellaneous debug registers
+ * which includes DSCR, PPR and TAR.
+ *
+ * Userspace intarface buffer layout:
+ *
+ * struct {
+ * unsigned long dscr;
+ * unsigned long ppr;
+ * unsigned long tar;
+ * };
+ */
+static int get_misc_dbg(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ /* Build test */
+ BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
+ BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
+
+ /* DSCR register */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dscr, 0,
+ sizeof(unsigned long));
+
+ /* PPR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ppr,
+ sizeof(unsigned long),
+ 2 * sizeof(unsigned long));
+
+ /* TAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tar,
+ 2 * sizeof(unsigned long),
+ 3 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
+ * set_misc_dbg
+ *
+ * This function sets miscellaneous debug registers
+ * which includes DSCR, PPR and TAR.
+ *
+ * Userspace intarface buffer layout:
+ *
+ * struct {
+ * unsigned long dscr;
+ * unsigned long ppr;
+ * unsigned long tar;
+ * };
+ */
+static int set_misc_dbg(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 test */
+ BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
+ BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
+
+ /* DSCR register */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.dscr, 0,
+ sizeof(unsigned long));
+
+ /* PPR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.ppr,
+ sizeof(unsigned long),
+ 2 * sizeof(unsigned long));
+
+ /* TAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tar,
+ 2 * sizeof(unsigned long),
+ 3 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
* These are our native regset flavors.
*/
enum powerpc_regset {
@@ -1347,6 +1438,7 @@ enum powerpc_regset {
REGSET_TM_CFPR, /* TM checkpointed FPR registers */
REGSET_TM_CVMX, /* TM checkpointed VMX registers */
#endif
+ REGSET_MISC /* Miscellaneous debug registers */
};
static const struct user_regset native_regsets[] = {
@@ -1403,6 +1495,11 @@ static const struct user_regset native_regsets[] = {
.active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+ [REGSET_MISC] = {
+ .core_note_type = NT_PPC_MISC, .n = ELF_NMISCREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = get_misc_dbg, .set = set_misc_dbg
+ },
};
static const struct user_regset_view user_ppc_native_view = {
@@ -1646,6 +1743,11 @@ static const struct user_regset compat_regsets[] = {
.active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+ [REGSET_MISC] = {
+ .core_note_type = NT_PPC_MISC, .n = ELF_NMISCREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .get = get_misc_dbg, .set = set_misc_dbg
+ },
};
static const struct user_regset_view user_ppc_compat_view = {
--
1.9.3
This patch enables get and set of transactional memory related register
sets through PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing
four new powerpc specific register sets i.e REGSET_TM_SPR, REGSET_TM_CGPR,
REGSET_TM_CFPR, REGSET_CVMX support corresponding to these following new
ELF core note types added previously in this regard.
(1) NT_PPC_TM_SPR
(2) NT_PPC_TM_CGPR
(3) NT_PPC_TM_CFPR
(4) NT_PPC_TM_CVMX
Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/include/uapi/asm/elf.h | 2 +
arch/powerpc/kernel/ptrace.c | 666 +++++++++++++++++++++++++++++++++++-
2 files changed, 653 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
index 59dad11..fdc8e2f 100644
--- a/arch/powerpc/include/uapi/asm/elf.h
+++ b/arch/powerpc/include/uapi/asm/elf.h
@@ -91,6 +91,8 @@
#define ELF_NGREG 48 /* includes nip, msr, lr, etc. */
#define ELF_NFPREG 33 /* includes fpscr */
+#define ELF_NVMX 34 /* includes all vector registers */
+#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
typedef unsigned long elf_greg_t64;
typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 2bbbd10..b279947 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -63,6 +63,11 @@ struct pt_regs_offset {
{.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
#define REG_OFFSET_END {.name = NULL, .offset = 0}
+/* Some common structure offsets */
+#define TSO(f) (offsetof(struct thread_struct, f))
+#define TVSO(f) (offsetof(struct thread_vr_state, f))
+#define TFSO(f) (offsetof(struct thread_fp_state, f))
+
static const struct pt_regs_offset regoffset_table[] = {
GPR_OFFSET_NAME(0),
GPR_OFFSET_NAME(1),
@@ -792,6 +797,534 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
}
#endif /* CONFIG_SPE */
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+/*
+ * tm_spr_active
+ *
+ * This function checks number of available regisers in
+ * the transactional memory SPR category.
+ */
+static int tm_spr_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_spr_get
+ *
+ * This function gets transactional memory related SPR registers
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct {
+ * u64 tm_tfhar;
+ * u64 tm_texasr;
+ * u64 tm_tfiar;
+ * unsigned long tm_orig_msr;
+ * unsigned long tm_tar;
+ * unsigned long tm_ppr;
+ * unsigned long tm_dscr;
+ * };
+ */
+static int tm_spr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ /* Build tests */
+ BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
+ BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
+ BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
+ BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long) +
+ sizeof(struct pt_regs) != TSO(tm_tar));
+ BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
+ BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ /* Flush the states */
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ /* TFHAR register */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfhar, 0, sizeof(u64));
+
+ /* TEXASR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_texasr, sizeof(u64),
+ 2 * sizeof(u64));
+
+ /* TFIAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfiar,
+ 2 * sizeof(u64), 3 * sizeof(u64));
+
+ /* TM checkpointed original MSR */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_orig_msr, 3 * sizeof(u64),
+ 3 * sizeof(u64) + sizeof(unsigned long));
+
+ /* TM checkpointed TAR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tar, 3 * sizeof(u64) +
+ sizeof(unsigned long) ,
+ 3 * sizeof(u64) + 2 * sizeof(unsigned long));
+
+ /* TM checkpointed PPR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_ppr, 3 * sizeof(u64) +
+ 2 * sizeof(unsigned long),
+ 3 * sizeof(u64) + 3 * sizeof(unsigned long));
+
+ /* TM checkpointed DSCR register */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_dscr, 3 * sizeof(u64) +
+ 3 * sizeof(unsigned long),
+ 3 * sizeof(u64) + 4 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
+ * tm_spr_set
+ *
+ * This function sets transactional memory related SPR registers
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct {
+ * u64 tm_tfhar;
+ * u64 tm_texasr;
+ * u64 tm_tfiar;
+ * unsigned long tm_orig_msr;
+ * unsigned long tm_tar;
+ * unsigned long tm_ppr;
+ * unsigned long tm_dscr;
+ * };
+ */
+static int tm_spr_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 tests */
+ BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
+ BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
+ BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long)
+ + sizeof(struct pt_regs) != TSO(tm_tar));
+ BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
+ BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
+ BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
+
+ if (!cpu_has_feature(CPU_FTR_TM))
+ return -ENODEV;
+
+ if (!MSR_TM_ACTIVE(target->thread.regs->msr))
+ return -ENODATA;
+
+ /* Flush the states */
+ flush_fp_to_thread(target);
+ flush_altivec_to_thread(target);
+ flush_tmregs_to_thread(target);
+
+ /* TFHAR register */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfhar, 0, sizeof(u64));
+
+ /* TEXASR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_texasr, sizeof(u64),
+ 2 * sizeof(u64));
+
+ /* TFIAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tfiar,
+ 2 * sizeof(u64), 3 * sizeof(u64));
+
+
+ /* TM checkpointed orig MSR */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_orig_msr, 3 * sizeof(u64),
+ 3 * sizeof(u64) + sizeof(unsigned long));
+
+
+ /* TM checkpointed TAR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_tar, 3 * sizeof(u64) +
+ sizeof(unsigned long), 3 * sizeof(u64) +
+ 2 * sizeof(unsigned long));
+
+ /* TM checkpointed PPR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_ppr, 3 * sizeof(u64) +
+ 2 * sizeof(unsigned long), 3 * sizeof(u64) +
+ 3 * sizeof(unsigned long));
+
+ /* TM checkpointed DSCR register */
+ if (!ret)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.tm_dscr, 3 * sizeof(u64) +
+ 3 * sizeof(unsigned long), 3 * sizeof(u64) +
+ 4 * sizeof(unsigned long));
+ return ret;
+}
+
+/*
+ * tm_cgpr_active
+ *
+ * This function checks the 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
+ *
+ * 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.
+ *
+ * Userspace interface buffer layout:
+ *
+ * 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,
+ sizeof(struct pt_regs));
+ return ret;
+}
+
+/*
+ * tm_cgpr_set
+ *
+ * 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.
+ *
+ * Userspace intaerface buffer:
+ *
+ * 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)
+{
+ 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,
+ sizeof(struct pt_regs));
+ return ret;
+}
+
+/*
+ * tm_cfpr_active
+ *
+ * This function checks number of available regisers in
+ * transaction checkpointed FPR category.
+ */
+static int tm_cfpr_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_cfpr_get
+ *
+ * This function gets in transaction checkpointed FPR registers
+ *
+ * When the transaction is active 'fp_state' holds the checkpointed
+ * values for the current transaction to fall back on if it aborts
+ * in between. This function gets those checkpointed FPR registers.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ *};
+ */
+static int tm_cfpr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ u64 buf[33];
+ int i;
+
+ 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);
+
+ /* copy to local buffer then write that out */
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_FPR(i);
+ buf[32] = target->thread.fp_state.fpscr;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+}
+
+/*
+ * tm_cfpr_set
+ *
+ * This function sets in transaction checkpointed FPR registers
+ *
+ * When the transaction is active 'fp_state' holds the checkpointed
+ * FPR register values for the current transaction to fall back on
+ * if it aborts in between. This function sets these checkpointed
+ * FPR registers.
+ *
+ * Userspace interface buffer layout:
+ *
+ * struct data {
+ * u64 fpr[32];
+ * u64 fpscr;
+ *};
+ */
+static int tm_cfpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ u64 buf[33];
+ int i;
+
+ 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);
+
+ /* copy to local buffer then write that out */
+ i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
+ if (i)
+ return i;
+ for (i = 0; i < 32 ; i++)
+ target->thread.TS_FPR(i) = buf[i];
+ target->thread.fp_state.fpscr = buf[32];
+ return 0;
+}
+
+/*
+ * tm_cvmx_active
+ *
+ * This function checks the 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
+ *
+ * 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.
+ *
+ * User interface buffer:
+ *
+ * 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
+ *
+ * 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.
+ *
+ * Userspace interface buffer:
+ *
+ * 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 first 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 /* CONFIG_PPC_TRANSACTIONAL_MEM */
/*
* These are our native regset flavors.
@@ -808,6 +1341,12 @@ enum powerpc_regset {
#ifdef CONFIG_SPE
REGSET_SPE,
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ REGSET_TM_SPR, /* TM specific SPR registers */
+ REGSET_TM_CGPR, /* TM checkpointed GPR registers */
+ REGSET_TM_CFPR, /* TM checkpointed FPR registers */
+ REGSET_TM_CVMX, /* TM checkpointed VMX registers */
+#endif
};
static const struct user_regset native_regsets[] = {
@@ -842,6 +1381,28 @@ static const struct user_regset native_regsets[] = {
.active = evr_active, .get = evr_get, .set = evr_set
},
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ [REGSET_TM_SPR] = {
+ .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
+ },
+ [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
+ },
+ [REGSET_TM_CFPR] = {
+ .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
+ .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
};
static const struct user_regset_view user_ppc_native_view = {
@@ -852,24 +1413,35 @@ static const struct user_regset_view user_ppc_native_view = {
#ifdef CONFIG_PPC64
#include <linux/compat.h>
-static int gpr32_get(struct task_struct *target,
+static int common_gpr32_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
- void *kbuf, void __user *ubuf)
+ void *kbuf, void __user *ubuf, bool in_tm)
{
- const unsigned long *regs = &target->thread.regs->gpr[0];
+ const unsigned long *regs;
compat_ulong_t *k = kbuf;
compat_ulong_t __user *u = ubuf;
compat_ulong_t reg;
int i;
- if (target->thread.regs == NULL)
- return -EIO;
+ if (in_tm) {
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ regs = &target->thread.ckpt_regs.gpr[0];
+#endif
+ } else {
+ regs = &target->thread.regs->gpr[0];
- if (!FULL_REGS(target->thread.regs)) {
- /* We have a partial register set. Fill 14-31 with bogus values */
- for (i = 14; i < 32; i++)
- target->thread.regs->gpr[i] = NV_REG_POISON;
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ if (!FULL_REGS(target->thread.regs)) {
+ /*
+ * We have a partial register set.
+ * Fill 14-31 with bogus values.
+ */
+ for (i = 14; i < 32; i++)
+ target->thread.regs->gpr[i] = NV_REG_POISON;
+ }
}
pos /= sizeof(reg);
@@ -909,20 +1481,28 @@ static int gpr32_get(struct task_struct *target,
PT_REGS_COUNT * sizeof(reg), -1);
}
-static int gpr32_set(struct task_struct *target,
+static int common_gpr32_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
+ const void *kbuf, const void __user *ubuf, bool in_tm)
{
- unsigned long *regs = &target->thread.regs->gpr[0];
+ unsigned long *regs;
const compat_ulong_t *k = kbuf;
const compat_ulong_t __user *u = ubuf;
compat_ulong_t reg;
- if (target->thread.regs == NULL)
- return -EIO;
+ if (in_tm) {
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ regs = &target->thread.ckpt_regs.gpr[0];
+#endif
+ } else {
+ regs = &target->thread.regs->gpr[0];
- CHECK_FULL_REGS(target->thread.regs);
+ if (target->thread.regs == NULL)
+ return -EIO;
+
+ CHECK_FULL_REGS(target->thread.regs);
+ }
pos /= sizeof(reg);
count /= sizeof(reg);
@@ -982,6 +1562,39 @@ static int gpr32_set(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 common_gpr32_get(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 common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
+}
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+
+static int gpr32_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 0);
+}
+
+static int gpr32_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
+}
/*
* These are the regset flavors matching the CONFIG_PPC32 native set.
*/
@@ -1010,6 +1623,29 @@ static const struct user_regset compat_regsets[] = {
.active = evr_active, .get = evr_get, .set = evr_set
},
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ [REGSET_TM_SPR] = {
+ .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
+ .size = sizeof(u64), .align = sizeof(u64),
+ .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
+ },
+ [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
+ },
+ [REGSET_TM_CFPR] = {
+ .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
+ .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
};
static const struct user_regset_view user_ppc_compat_view = {
--
1.9.3
This patch creates a new function called flush_tm_state to flush
the existing transactional memory state from the thread. It also
creates a function called flush_tmregs_to_thread which will then
be used on subsequent patches in this series.
Signed-off-by: Anshuman Khandual <[email protected]>
---
arch/powerpc/include/asm/switch_to.h | 8 ++++++
arch/powerpc/kernel/process.c | 49 +++++++++++++++++++++++++++++-------
2 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
index 58abeda..23752a9 100644
--- a/arch/powerpc/include/asm/switch_to.h
+++ b/arch/powerpc/include/asm/switch_to.h
@@ -82,6 +82,14 @@ static inline void flush_spe_to_thread(struct task_struct *t)
}
#endif
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+extern void flush_tmregs_to_thread(struct task_struct *);
+#else
+static inline void flush_tmregs_to_thread(struct task_struct *t)
+{
+}
+#endif
+
static inline void clear_task_ebb(struct task_struct *t)
{
#ifdef CONFIG_PPC_BOOK3S_64
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index aa1df89..c36477b 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -745,6 +745,45 @@ void restore_tm_state(struct pt_regs *regs)
#define __switch_to_tm(prev)
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+/*
+ * flush_tm_state
+ *
+ * Flush TM state out so we can copy it. __switch_to_tm() does this
+ * flush but it removes the checkpointed state from the current CPU and
+ * transitions the CPU out of TM mode. Hence we need to call
+ * tm_recheckpoint_new_task() (on the same task) to restore the
+ * checkpointed state back and the TM mode.
+ */
+static void flush_tm_state(struct task_struct *src)
+{
+ __switch_to_tm(src);
+ tm_recheckpoint_new_task(src);
+}
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+void flush_tmregs_to_thread(struct task_struct *tsk)
+{
+ /*
+ * If task is not current, it should have been flushed
+ * already to it's thread_struct during __switch_to().
+ */
+ if (tsk != current)
+ return;
+
+ preempt_disable();
+ if (tsk->thread.regs) {
+ /*
+ * If we are still current, the TM state need to
+ * be flushed to thread_struct as it will be still
+ * present in the current cpu.
+ */
+ if (MSR_TM_ACTIVE(tsk->thread.regs->msr))
+ flush_tm_state(tsk);
+ }
+ preempt_enable();
+}
+#endif
+
struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *new)
{
@@ -1079,15 +1118,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
flush_altivec_to_thread(src);
flush_vsx_to_thread(src);
flush_spe_to_thread(src);
- /*
- * Flush TM state out so we can copy it. __switch_to_tm() does this
- * flush but it removes the checkpointed state from the current CPU and
- * transitions the CPU out of TM mode. Hence we need to call
- * tm_recheckpoint_new_task() (on the same task) to restore the
- * checkpointed state back and the TM mode.
- */
- __switch_to_tm(src);
- tm_recheckpoint_new_task(src);
+ flush_tm_state(src);
*dst = *src;
--
1.9.3
On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
> This patch enables get and set of miscellaneous debug registers through
> ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing new
> powerpc specific register set REGSET_MISC support corresponding to the
> new ELF core note NT_PPC_MISC added previously in this regard.
Right now this one does not compile for "ppc64e_defconfig" and
"pmac32_defconfig" config options. The patch below will fix it
and would be part of next revision.
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 61a2581..be566eb 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -1326,6 +1326,7 @@ static int tm_cvmx_set(struct task_struct *target,
}
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+#ifdef CONFIG_PPC64
/*
* get_misc_dbg
*
@@ -1339,6 +1340,9 @@ static int tm_cvmx_set(struct task_struct *target,
* unsigned long ppr;
* unsigned long tar;
* };
+ *
+ * The data element 'tar' will be valid only if the
+ * kernel has CONFIG_PPC_BOOK3S_64 config option enabled.
*/
static int get_misc_dbg(struct task_struct *target,
const struct user_regset *regset, unsigned int pos,
@@ -1348,7 +1352,10 @@ static int get_misc_dbg(struct task_struct *target,
/* Build test */
BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
+
+#ifdef CONFIG_PPC_BOOK3S_64
BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
+#endif
/* DSCR register */
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
@@ -1362,12 +1369,14 @@ static int get_misc_dbg(struct task_struct *target,
sizeof(unsigned long),
2 * sizeof(unsigned long));
+#ifdef CONFIG_PPC_BOOK3S_64
/* TAR register */
if (!ret)
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.tar,
2 * sizeof(unsigned long),
3 * sizeof(unsigned long));
+#endif
return ret;
}
@@ -1384,6 +1393,9 @@ static int get_misc_dbg(struct task_struct *target,
* unsigned long ppr;
* unsigned long tar;
* };
+ *
+ * The data element 'tar' will be valid only if the
+ * kernel has CONFIG_PPC_BOOK3S_64 config option enabled.
*/
static int set_misc_dbg(struct task_struct *target,
const struct user_regset *regset, unsigned int pos,
@@ -1394,7 +1406,10 @@ static int set_misc_dbg(struct task_struct *target,
/* Build test */
BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
+
+#ifdef CONFIG_PPC_BOOK3S_64
BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
+#endif
/* DSCR register */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
@@ -1407,15 +1422,17 @@ static int set_misc_dbg(struct task_struct *target,
&target->thread.ppr,
sizeof(unsigned long),
2 * sizeof(unsigned long));
-
+#ifdef CONFIG_PPC_BOOK3S_64
/* TAR register */
if (!ret)
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.tar,
2 * sizeof(unsigned long),
3 * sizeof(unsigned long));
+#endif
return ret;
}
+#endif /* CONFIG_PPC64 */
/*
* These are our native regset flavors.
@@ -1438,7 +1455,9 @@ enum powerpc_regset {
REGSET_TM_CFPR, /* TM checkpointed FPR registers */
REGSET_TM_CVMX, /* TM checkpointed VMX registers */
#endif
+#ifdef CONFIG_PPC64
REGSET_MISC /* Miscellaneous debug registers */
+#endif
};
static const struct user_regset native_regsets[] = {
@@ -1495,11 +1514,13 @@ static const struct user_regset native_regsets[] = {
.active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
},
#endif
+#ifdef CONFIG_PPC64
[REGSET_MISC] = {
.core_note_type = NT_PPC_MISC, .n = ELF_NMISCREG,
.size = sizeof(u64), .align = sizeof(u64),
.get = get_misc_dbg, .set = set_misc_dbg
},
+#endif
};
static const struct user_regset_view user_ppc_native_view = {
On 11/13/14, Anshuman Khandual <[email protected]> wrote:
> On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
>> This patch enables get and set of miscellaneous debug registers through
>> ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing new
>> powerpc specific register set REGSET_MISC support corresponding to the
>> new ELF core note NT_PPC_MISC added previously in this regard.
>
> Right now this one does not compile for "ppc64e_defconfig" and
> "pmac32_defconfig" config options. The patch below will fix it
> and would be part of next revision.
It would be great to have a test tool for that under
tools/testing/selftests/ptrace/
> diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
> index 61a2581..be566eb 100644
> --- a/arch/powerpc/kernel/ptrace.c
> +++ b/arch/powerpc/kernel/ptrace.c
> @@ -1326,6 +1326,7 @@ static int tm_cvmx_set(struct task_struct *target,
> }
> #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
>
> +#ifdef CONFIG_PPC64
> /*
> * get_misc_dbg
> *
> @@ -1339,6 +1340,9 @@ static int tm_cvmx_set(struct task_struct *target,
> * unsigned long ppr;
> * unsigned long tar;
> * };
> + *
> + * The data element 'tar' will be valid only if the
> + * kernel has CONFIG_PPC_BOOK3S_64 config option enabled.
> */
> static int get_misc_dbg(struct task_struct *target,
> const struct user_regset *regset, unsigned int pos,
> @@ -1348,7 +1352,10 @@ static int get_misc_dbg(struct task_struct *target,
>
> /* Build test */
> BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
> +
> +#ifdef CONFIG_PPC_BOOK3S_64
> BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
> +#endif
>
> /* DSCR register */
> ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> @@ -1362,12 +1369,14 @@ static int get_misc_dbg(struct task_struct *target,
> sizeof(unsigned long),
> 2 * sizeof(unsigned long));
>
> +#ifdef CONFIG_PPC_BOOK3S_64
> /* TAR register */
> if (!ret)
> ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> &target->thread.tar,
> 2 * sizeof(unsigned long),
> 3 * sizeof(unsigned long));
> +#endif
> return ret;
> }
>
> @@ -1384,6 +1393,9 @@ static int get_misc_dbg(struct task_struct *target,
> * unsigned long ppr;
> * unsigned long tar;
> * };
> + *
> + * The data element 'tar' will be valid only if the
> + * kernel has CONFIG_PPC_BOOK3S_64 config option enabled.
> */
> static int set_misc_dbg(struct task_struct *target,
> const struct user_regset *regset, unsigned int pos,
> @@ -1394,7 +1406,10 @@ static int set_misc_dbg(struct task_struct *target,
>
> /* Build test */
> BUILD_BUG_ON(TSO(dscr) + 2 * sizeof(unsigned long) != TSO(ppr));
> +
> +#ifdef CONFIG_PPC_BOOK3S_64
> BUILD_BUG_ON(TSO(ppr) + sizeof(unsigned long) != TSO(tar));
> +#endif
>
> /* DSCR register */
> ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> @@ -1407,15 +1422,17 @@ static int set_misc_dbg(struct task_struct *target,
> &target->thread.ppr,
> sizeof(unsigned long),
> 2 * sizeof(unsigned long));
> -
> +#ifdef CONFIG_PPC_BOOK3S_64
> /* TAR register */
> if (!ret)
> ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> &target->thread.tar,
> 2 * sizeof(unsigned long),
> 3 * sizeof(unsigned long));
> +#endif
> return ret;
> }
> +#endif /* CONFIG_PPC64 */
>
> /*
> * These are our native regset flavors.
> @@ -1438,7 +1455,9 @@ enum powerpc_regset {
> REGSET_TM_CFPR, /* TM checkpointed FPR registers */
> REGSET_TM_CVMX, /* TM checkpointed VMX registers */
> #endif
> +#ifdef CONFIG_PPC64
> REGSET_MISC /* Miscellaneous debug registers */
> +#endif
> };
>
> static const struct user_regset native_regsets[] = {
> @@ -1495,11 +1514,13 @@ static const struct user_regset native_regsets[] = {
> .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
> },
> #endif
> +#ifdef CONFIG_PPC64
> [REGSET_MISC] = {
> .core_note_type = NT_PPC_MISC, .n = ELF_NMISCREG,
> .size = sizeof(u64), .align = sizeof(u64),
> .get = get_misc_dbg, .set = set_misc_dbg
> },
> +#endif
> };
>
> static const struct user_regset_view user_ppc_native_view = {
>
> _______________________________________________
> Linuxppc-dev mailing list
> [email protected]
> https://lists.ozlabs.org/listinfo/linuxppc-dev
--
Regards,
Denis
Anshuman Khandual [[email protected]] wrote:
| This patch enables get and set of transactional memory related register
| sets through PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing
| four new powerpc specific register sets i.e REGSET_TM_SPR, REGSET_TM_CGPR,
| REGSET_TM_CFPR, REGSET_CVMX support corresponding to these following new
| ELF core note types added previously in this regard.
|
| (1) NT_PPC_TM_SPR
| (2) NT_PPC_TM_CGPR
| (3) NT_PPC_TM_CFPR
| (4) NT_PPC_TM_CVMX
|
| Signed-off-by: Anshuman Khandual <[email protected]>
| ---
| arch/powerpc/include/uapi/asm/elf.h | 2 +
| arch/powerpc/kernel/ptrace.c | 666 +++++++++++++++++++++++++++++++++++-
| 2 files changed, 653 insertions(+), 15 deletions(-)
|
| diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
| index 59dad11..fdc8e2f 100644
| --- a/arch/powerpc/include/uapi/asm/elf.h
| +++ b/arch/powerpc/include/uapi/asm/elf.h
| @@ -91,6 +91,8 @@
|
| #define ELF_NGREG 48 /* includes nip, msr, lr, etc. */
| #define ELF_NFPREG 33 /* includes fpscr */
| +#define ELF_NVMX 34 /* includes all vector registers */
| +#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
|
| typedef unsigned long elf_greg_t64;
| typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
| diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
| index 2bbbd10..b279947 100644
| --- a/arch/powerpc/kernel/ptrace.c
| +++ b/arch/powerpc/kernel/ptrace.c
| @@ -63,6 +63,11 @@ struct pt_regs_offset {
| {.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
| #define REG_OFFSET_END {.name = NULL, .offset = 0}
|
| +/* Some common structure offsets */
| +#define TSO(f) (offsetof(struct thread_struct, f))
| +#define TVSO(f) (offsetof(struct thread_vr_state, f))
| +#define TFSO(f) (offsetof(struct thread_fp_state, f))
| +
| static const struct pt_regs_offset regoffset_table[] = {
| GPR_OFFSET_NAME(0),
| GPR_OFFSET_NAME(1),
| @@ -792,6 +797,534 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
| }
| #endif /* CONFIG_SPE */
|
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| +/*
| + * tm_spr_active
| + *
| + * This function checks number of available regisers in
| + * the transactional memory SPR category.
| + */
| +static int tm_spr_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_spr_get
| + *
| + * This function gets transactional memory related SPR registers
| + *
| + * Userspace interface buffer layout:
| + *
| + * struct {
| + * u64 tm_tfhar;
| + * u64 tm_texasr;
| + * u64 tm_tfiar;
| + * unsigned long tm_orig_msr;
| + * unsigned long tm_tar;
| + * unsigned long tm_ppr;
| + * unsigned long tm_dscr;
| + * };
| + */
| +static int tm_spr_get(struct task_struct *target,
| + const struct user_regset *regset,
| + unsigned int pos, unsigned int count,
| + void *kbuf, void __user *ubuf)
| +{
| + int ret;
| +
| + /* Build tests */
| + BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
| + BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
| + BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
| + BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long) +
Can we replace TSO(tm_orig_msr) + sizeof(unsigned long) with
TSO(ckpt_regs) ?
| + sizeof(struct pt_regs) != TSO(tm_tar));
| + BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
| + BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
| +
| + if (!cpu_has_feature(CPU_FTR_TM))
| + return -ENODEV;
| +
| + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
| + return -ENODATA;
| +
| + /* Flush the states */
| + flush_fp_to_thread(target);
| + flush_altivec_to_thread(target);
| + flush_tmregs_to_thread(target);
| +
| + /* TFHAR register */
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_tfhar, 0, sizeof(u64));
The last two parameters, (start_pos, end_pos) are easy to understand
here, but...
| +
| + /* TEXASR register */
| + if (!ret)
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_texasr, sizeof(u64),
| + 2 * sizeof(u64));
... gets harder to understand here and subsequent fields below.
Given that you already do the BUILD_BUG_ON() tests above, how about
using TSO(tm_texasr) and TSO(tfiar) here for start_pos and end_pos ?
Also, how about just returning if the copyout fails ? If the first
copyout fails, we will still check 'if(!ret)' several times below.
| +
| + /* TFIAR register */
| + if (!ret)
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_tfiar,
| + 2 * sizeof(u64), 3 * sizeof(u64));
| +
| + /* TM checkpointed original MSR */
| + if (!ret)
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_orig_msr, 3 * sizeof(u64),
| + 3 * sizeof(u64) + sizeof(unsigned long));
| +
| + /* TM checkpointed TAR register */
| + if (!ret)
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_tar, 3 * sizeof(u64) +
| + sizeof(unsigned long) ,
| + 3 * sizeof(u64) + 2 * sizeof(unsigned long));
| +
| + /* TM checkpointed PPR register */
| + if (!ret)
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_ppr, 3 * sizeof(u64) +
| + 2 * sizeof(unsigned long),
| + 3 * sizeof(u64) + 3 * sizeof(unsigned long));
| +
| + /* TM checkpointed DSCR register */
| + if (!ret)
| + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_dscr, 3 * sizeof(u64) +
| + 3 * sizeof(unsigned long),
| + 3 * sizeof(u64) + 4 * sizeof(unsigned long));
| + return ret;
| +}
| +
| +/*
| + * tm_spr_set
| + *
| + * This function sets transactional memory related SPR registers
| + *
| + * Userspace interface buffer layout:
| + *
| + * struct {
| + * u64 tm_tfhar;
| + * u64 tm_texasr;
| + * u64 tm_tfiar;
| + * unsigned long tm_orig_msr;
| + * unsigned long tm_tar;
| + * unsigned long tm_ppr;
| + * unsigned long tm_dscr;
| + * };
| + */
| +static int tm_spr_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 tests */
| + BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
| + BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
| + BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long)
Can we replace TSO(tm_orig_msr) + sizeof(unsigned long) with
TSO(ckpt_regs) ?
| + + sizeof(struct pt_regs) != TSO(tm_tar));
| + BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
| + BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
| + BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
How about moving this last line up after the check for TSO(tm_tfiar) ?
| +
| + if (!cpu_has_feature(CPU_FTR_TM))
| + return -ENODEV;
| +
| + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
| + return -ENODATA;
| +
| + /* Flush the states */
| + flush_fp_to_thread(target);
| + flush_altivec_to_thread(target);
| + flush_tmregs_to_thread(target);
| +
| + /* TFHAR register */
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_tfhar, 0, sizeof(u64));
| +
| + /* TEXASR register */
| + if (!ret)
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_texasr, sizeof(u64),
| + 2 * sizeof(u64));
Return if copyin() fails ?
| +
| + /* TFIAR register */
| + if (!ret)
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_tfiar,
| + 2 * sizeof(u64), 3 * sizeof(u64));
| +
| +
| + /* TM checkpointed orig MSR */
| + if (!ret)
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_orig_msr, 3 * sizeof(u64),
| + 3 * sizeof(u64) + sizeof(unsigned long));
| +
| +
| + /* TM checkpointed TAR register */
| + if (!ret)
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_tar, 3 * sizeof(u64) +
| + sizeof(unsigned long), 3 * sizeof(u64) +
| + 2 * sizeof(unsigned long));
| +
| + /* TM checkpointed PPR register */
| + if (!ret)
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_ppr, 3 * sizeof(u64) +
| + 2 * sizeof(unsigned long), 3 * sizeof(u64) +
| + 3 * sizeof(unsigned long));
| +
| + /* TM checkpointed DSCR register */
| + if (!ret)
| + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
| + &target->thread.tm_dscr, 3 * sizeof(u64) +
| + 3 * sizeof(unsigned long), 3 * sizeof(u64) +
| + 4 * sizeof(unsigned long));
| + return ret;
| +}
| +
| +/*
| + * tm_cgpr_active
| + *
| + * This function checks the 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
| + *
| + * 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.
| + *
| + * Userspace interface buffer layout:
| + *
| + * 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,
| + sizeof(struct pt_regs));
| + return ret;
| +}
| +
| +/*
| + * tm_cgpr_set
| + *
| + * 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.
| + *
| + * Userspace intaerface buffer:
| + *
| + * 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)
| +{
| + 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,
| + sizeof(struct pt_regs));
| + return ret;
| +}
| +
| +/*
| + * tm_cfpr_active
| + *
| + * This function checks number of available regisers in
| + * transaction checkpointed FPR category.
| + */
| +static int tm_cfpr_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_cfpr_get
| + *
| + * This function gets in transaction checkpointed FPR registers
| + *
| + * When the transaction is active 'fp_state' holds the checkpointed
| + * values for the current transaction to fall back on if it aborts
| + * in between. This function gets those checkpointed FPR registers.
| + *
| + * Userspace interface buffer layout:
| + *
| + * struct data {
| + * u64 fpr[32];
| + * u64 fpscr;
| + *};
| + */
| +static int tm_cfpr_get(struct task_struct *target,
| + const struct user_regset *regset,
| + unsigned int pos, unsigned int count,
| + void *kbuf, void __user *ubuf)
| +{
| + u64 buf[33];
| + int i;
| +
| + 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);
| +
| + /* copy to local buffer then write that out */
| + for (i = 0; i < 32 ; i++)
| + buf[i] = target->thread.TS_FPR(i);
| + buf[32] = target->thread.fp_state.fpscr;
| + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
| +}
| +
| +/*
| + * tm_cfpr_set
| + *
| + * This function sets in transaction checkpointed FPR registers
| + *
| + * When the transaction is active 'fp_state' holds the checkpointed
| + * FPR register values for the current transaction to fall back on
| + * if it aborts in between. This function sets these checkpointed
| + * FPR registers.
| + *
| + * Userspace interface buffer layout:
| + *
| + * struct data {
| + * u64 fpr[32];
| + * u64 fpscr;
| + *};
| + */
| +static int tm_cfpr_set(struct task_struct *target,
| + const struct user_regset *regset,
| + unsigned int pos, unsigned int count,
| + const void *kbuf, const void __user *ubuf)
| +{
| + u64 buf[33];
| + int i;
| +
| + 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);
| +
| + /* copy to local buffer then write that out */
| + i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
| + if (i)
| + return i;
| + for (i = 0; i < 32 ; i++)
| + target->thread.TS_FPR(i) = buf[i];
| + target->thread.fp_state.fpscr = buf[32];
| + return 0;
| +}
| +
| +/*
| + * tm_cvmx_active
| + *
| + * This function checks the 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
| + *
| + * 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.
| + *
| + * User interface buffer:
| + *
| + * 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
| + *
| + * 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.
| + *
| + * Userspace interface buffer:
| + *
| + * 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 first word of vrsave.
For consistency with the _get() function above, s/first/low-order/ ?
| + */
| + 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 /* CONFIG_PPC_TRANSACTIONAL_MEM */
|
| /*
| * These are our native regset flavors.
| @@ -808,6 +1341,12 @@ enum powerpc_regset {
| #ifdef CONFIG_SPE
| REGSET_SPE,
| #endif
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| + REGSET_TM_SPR, /* TM specific SPR registers */
| + REGSET_TM_CGPR, /* TM checkpointed GPR registers */
| + REGSET_TM_CFPR, /* TM checkpointed FPR registers */
| + REGSET_TM_CVMX, /* TM checkpointed VMX registers */
| +#endif
| };
|
| static const struct user_regset native_regsets[] = {
| @@ -842,6 +1381,28 @@ static const struct user_regset native_regsets[] = {
| .active = evr_active, .get = evr_get, .set = evr_set
| },
| #endif
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| + [REGSET_TM_SPR] = {
| + .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
| + .size = sizeof(u64), .align = sizeof(u64),
| + .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
| + },
| + [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
| + },
| + [REGSET_TM_CFPR] = {
| + .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
| + .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
| };
|
| static const struct user_regset_view user_ppc_native_view = {
| @@ -852,24 +1413,35 @@ static const struct user_regset_view user_ppc_native_view = {
| #ifdef CONFIG_PPC64
| #include <linux/compat.h>
|
| -static int gpr32_get(struct task_struct *target,
| +static int common_gpr32_get(struct task_struct *target,
| const struct user_regset *regset,
| unsigned int pos, unsigned int count,
| - void *kbuf, void __user *ubuf)
| + void *kbuf, void __user *ubuf, bool in_tm)
| {
| - const unsigned long *regs = &target->thread.regs->gpr[0];
| + const unsigned long *regs;
| compat_ulong_t *k = kbuf;
| compat_ulong_t __user *u = ubuf;
| compat_ulong_t reg;
| int i;
|
| - if (target->thread.regs == NULL)
| - return -EIO;
| + if (in_tm) {
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| + regs = &target->thread.ckpt_regs.gpr[0];
| +#endif
regs uninitialized if in_tm is true and CONFIG_PPC_TRANSACTIONAL_MEM
is false ? It appears that it cannot/should not happen, how about BUGON() ?
or at least regs = NULL to silence compiler warnings ?
| + } else {
| + regs = &target->thread.regs->gpr[0];
|
| - if (!FULL_REGS(target->thread.regs)) {
| - /* We have a partial register set. Fill 14-31 with bogus values */
| - for (i = 14; i < 32; i++)
| - target->thread.regs->gpr[i] = NV_REG_POISON;
| + if (target->thread.regs == NULL)
| + return -EIO;
| +
| + if (!FULL_REGS(target->thread.regs)) {
| + /*
| + * We have a partial register set.
| + * Fill 14-31 with bogus values.
| + */
| + for (i = 14; i < 32; i++)
| + target->thread.regs->gpr[i] = NV_REG_POISON;
| + }
| }
|
| pos /= sizeof(reg);
| @@ -909,20 +1481,28 @@ static int gpr32_get(struct task_struct *target,
| PT_REGS_COUNT * sizeof(reg), -1);
| }
|
| -static int gpr32_set(struct task_struct *target,
| +static int common_gpr32_set(struct task_struct *target,
| const struct user_regset *regset,
| unsigned int pos, unsigned int count,
| - const void *kbuf, const void __user *ubuf)
| + const void *kbuf, const void __user *ubuf, bool in_tm)
| {
| - unsigned long *regs = &target->thread.regs->gpr[0];
| + unsigned long *regs;
| const compat_ulong_t *k = kbuf;
| const compat_ulong_t __user *u = ubuf;
| compat_ulong_t reg;
|
| - if (target->thread.regs == NULL)
| - return -EIO;
| + if (in_tm) {
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| + regs = &target->thread.ckpt_regs.gpr[0];
| +#endif
ditto
| + } else {
| + regs = &target->thread.regs->gpr[0];
|
| - CHECK_FULL_REGS(target->thread.regs);
| + if (target->thread.regs == NULL)
| + return -EIO;
| +
| + CHECK_FULL_REGS(target->thread.regs);
| + }
|
| pos /= sizeof(reg);
| count /= sizeof(reg);
| @@ -982,6 +1562,39 @@ static int gpr32_set(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 common_gpr32_get(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 common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
| +}
| +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
| +
| +static int gpr32_get(struct task_struct *target,
| + const struct user_regset *regset,
| + unsigned int pos, unsigned int count,
| + void *kbuf, void __user *ubuf)
| +{
| + return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 0);
| +}
| +
| +static int gpr32_set(struct task_struct *target,
| + const struct user_regset *regset,
| + unsigned int pos, unsigned int count,
| + const void *kbuf, const void __user *ubuf)
| +{
| + return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
| +}
| /*
| * These are the regset flavors matching the CONFIG_PPC32 native set.
| */
| @@ -1010,6 +1623,29 @@ static const struct user_regset compat_regsets[] = {
| .active = evr_active, .get = evr_get, .set = evr_set
| },
| #endif
| +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
| + [REGSET_TM_SPR] = {
| + .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
| + .size = sizeof(u64), .align = sizeof(u64),
| + .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
| + },
| + [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
| + },
| + [REGSET_TM_CFPR] = {
| + .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
| + .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
| };
|
| static const struct user_regset_view user_ppc_compat_view = {
| --
| 1.9.3
Anshuman Khandual [[email protected]] wrote:
| This patch adds four new core note sections for PowerPC transactional
| memory and one core note section for general miscellaneous debug registers.
| These addition of new elf core note sections extends the existing elf ABI
| without affecting it in any manner.
|
| Signed-off-by: Anshuman Khandual <[email protected]>
Except for some minor comments to one of the patches, the patchset
looks good to me. I was also able to run the included selftest on
a PowerVM guest.
Tested-by: Sukadev Bhattiprolu <[email protected]>
| ---
| include/uapi/linux/elf.h | 5 +++++
| 1 file changed, 5 insertions(+)
|
| diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
| index ea9bf25..2260fc0 100644
| --- a/include/uapi/linux/elf.h
| +++ b/include/uapi/linux/elf.h
| @@ -379,6 +379,11 @@ typedef struct elf64_shdr {
| #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
| #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
| #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
| +#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
| +#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
| +#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
| +#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
| +#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
| #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
| #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
| #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
| --
| 1.9.3
On 11/14/2014 03:30 PM, Denis Kirjanov wrote:
> On 11/13/14, Anshuman Khandual <[email protected]> wrote:
>> > On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
>>> >> This patch enables get and set of miscellaneous debug registers through
>>> >> ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing new
>>> >> powerpc specific register set REGSET_MISC support corresponding to the
>>> >> new ELF core note NT_PPC_MISC added previously in this regard.
>> >
>> > Right now this one does not compile for "ppc64e_defconfig" and
>> > "pmac32_defconfig" config options. The patch below will fix it
>> > and would be part of next revision.
> It would be great to have a test tool for that under
> tools/testing/selftests/ptrace/
>
Hey Denis,
I have already added one test case under selftests/powerpc/tm/ which
will test all the newly added ELF core note based ptrace requests on
powerpc. This test case is added as part of the next patch 8/8 in
the series. Please let me know if the test case does not cover something
you are looking for.
On 11/19/2014 02:48 AM, Sukadev Bhattiprolu wrote:
> Anshuman Khandual [[email protected]] wrote:
> | This patch enables get and set of transactional memory related register
> | sets through PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing
> | four new powerpc specific register sets i.e REGSET_TM_SPR, REGSET_TM_CGPR,
> | REGSET_TM_CFPR, REGSET_CVMX support corresponding to these following new
> | ELF core note types added previously in this regard.
> |
> | (1) NT_PPC_TM_SPR
> | (2) NT_PPC_TM_CGPR
> | (3) NT_PPC_TM_CFPR
> | (4) NT_PPC_TM_CVMX
> |
> | Signed-off-by: Anshuman Khandual <[email protected]>
> | ---
> | arch/powerpc/include/uapi/asm/elf.h | 2 +
> | arch/powerpc/kernel/ptrace.c | 666 +++++++++++++++++++++++++++++++++++-
> | 2 files changed, 653 insertions(+), 15 deletions(-)
> |
> | diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
> | index 59dad11..fdc8e2f 100644
> | --- a/arch/powerpc/include/uapi/asm/elf.h
> | +++ b/arch/powerpc/include/uapi/asm/elf.h
> | @@ -91,6 +91,8 @@
> |
> | #define ELF_NGREG 48 /* includes nip, msr, lr, etc. */
> | #define ELF_NFPREG 33 /* includes fpscr */
> | +#define ELF_NVMX 34 /* includes all vector registers */
> | +#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
> |
> | typedef unsigned long elf_greg_t64;
> | typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
> | diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
> | index 2bbbd10..b279947 100644
> | --- a/arch/powerpc/kernel/ptrace.c
> | +++ b/arch/powerpc/kernel/ptrace.c
> | @@ -63,6 +63,11 @@ struct pt_regs_offset {
> | {.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
> | #define REG_OFFSET_END {.name = NULL, .offset = 0}
> |
> | +/* Some common structure offsets */
> | +#define TSO(f) (offsetof(struct thread_struct, f))
> | +#define TVSO(f) (offsetof(struct thread_vr_state, f))
> | +#define TFSO(f) (offsetof(struct thread_fp_state, f))
> | +
> | static const struct pt_regs_offset regoffset_table[] = {
> | GPR_OFFSET_NAME(0),
> | GPR_OFFSET_NAME(1),
> | @@ -792,6 +797,534 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
> | }
> | #endif /* CONFIG_SPE */
> |
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | +/*
> | + * tm_spr_active
> | + *
> | + * This function checks number of available regisers in
> | + * the transactional memory SPR category.
> | + */
> | +static int tm_spr_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_spr_get
> | + *
> | + * This function gets transactional memory related SPR registers
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct {
> | + * u64 tm_tfhar;
> | + * u64 tm_texasr;
> | + * u64 tm_tfiar;
> | + * unsigned long tm_orig_msr;
> | + * unsigned long tm_tar;
> | + * unsigned long tm_ppr;
> | + * unsigned long tm_dscr;
> | + * };
> | + */
> | +static int tm_spr_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + /* Build tests */
> | + BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
> | + BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
> | + BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
> | + BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long) +
> Can we replace TSO(tm_orig_msr) + sizeof(unsigned long) with
> TSO(ckpt_regs) ?
Yeah we can.
> | + sizeof(struct pt_regs) != TSO(tm_tar));
> | + BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
> | + BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + /* Flush the states */
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + /* TFHAR register */
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfhar, 0, sizeof(u64));
>
> The last two parameters, (start_pos, end_pos) are easy to understand
> here, but...
Okay.
>
> | +
> | + /* TEXASR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_texasr, sizeof(u64),
> | + 2 * sizeof(u64));
>
> ... gets harder to understand here and subsequent fields below.
>
> Given that you already do the BUILD_BUG_ON() tests above, how about
> using TSO(tm_texasr) and TSO(tfiar) here for start_pos and end_pos ?
Hmm, I understand that as it looks kind of ugly, but writing to/from
the user level buffer is done looking at the user interface buffer
structure layout mentioned below.
* struct {
* u64 tm_tfhar;
* u64 tm_texasr;
* u64 tm_tfiar;
* unsigned long tm_orig_msr;
* unsigned long tm_tar;
* unsigned long tm_ppr;
* unsigned long tm_dscr;
* };
Looking at this structure will help some one understand the copy in/out
process and it's order better.
>
> Also, how about just returning if the copyout fails ? If the first
> copyout fails, we will still check 'if(!ret)' several times below.
Hmm, thats true. But the code flow is very similar to that of gpr_get/
gpr_set functions though it has a BUILD_BUG_ON check in between. The
rational is to stop copyout/in when we hit the first error and not to
proceed any further. We can return from the first error itself.
>
> | +
> | + /* TFIAR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfiar,
> | + 2 * sizeof(u64), 3 * sizeof(u64));
> | +
> | + /* TM checkpointed original MSR */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_orig_msr, 3 * sizeof(u64),
> | + 3 * sizeof(u64) + sizeof(unsigned long));
> | +
> | + /* TM checkpointed TAR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tar, 3 * sizeof(u64) +
> | + sizeof(unsigned long) ,
> | + 3 * sizeof(u64) + 2 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed PPR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_ppr, 3 * sizeof(u64) +
> | + 2 * sizeof(unsigned long),
> | + 3 * sizeof(u64) + 3 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed DSCR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_dscr, 3 * sizeof(u64) +
> | + 3 * sizeof(unsigned long),
> | + 3 * sizeof(u64) + 4 * sizeof(unsigned long));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_spr_set
> | + *
> | + * This function sets transactional memory related SPR registers
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct {
> | + * u64 tm_tfhar;
> | + * u64 tm_texasr;
> | + * u64 tm_tfiar;
> | + * unsigned long tm_orig_msr;
> | + * unsigned long tm_tar;
> | + * unsigned long tm_ppr;
> | + * unsigned long tm_dscr;
> | + * };
> | + */
> | +static int tm_spr_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 tests */
> | + BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
> | + BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
> | + BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long)
>
> Can we replace TSO(tm_orig_msr) + sizeof(unsigned long) with
> TSO(ckpt_regs) ?
Yeah we can.
>
> | + + sizeof(struct pt_regs) != TSO(tm_tar));
> | + BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
> | + BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
> | + BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
>
> How about moving this last line up after the check for TSO(tm_tfiar) ?
Done.
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + /* Flush the states */
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + /* TFHAR register */
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfhar, 0, sizeof(u64));
> | +
> | + /* TEXASR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_texasr, sizeof(u64),
> | + 2 * sizeof(u64));
>
> Return if copyin() fails ?
Yeah both the cases are similar.
>
> | +
> | + /* TFIAR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfiar,
> | + 2 * sizeof(u64), 3 * sizeof(u64));
> | +
> | +
> | + /* TM checkpointed orig MSR */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_orig_msr, 3 * sizeof(u64),
> | + 3 * sizeof(u64) + sizeof(unsigned long));
> | +
> | +
> | + /* TM checkpointed TAR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tar, 3 * sizeof(u64) +
> | + sizeof(unsigned long), 3 * sizeof(u64) +
> | + 2 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed PPR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_ppr, 3 * sizeof(u64) +
> | + 2 * sizeof(unsigned long), 3 * sizeof(u64) +
> | + 3 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed DSCR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_dscr, 3 * sizeof(u64) +
> | + 3 * sizeof(unsigned long), 3 * sizeof(u64) +
> | + 4 * sizeof(unsigned long));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cgpr_active
> | + *
> | + * This function checks the 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
> | + *
> | + * 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.
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * 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,
> | + sizeof(struct pt_regs));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cgpr_set
> | + *
> | + * 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.
> | + *
> | + * Userspace intaerface buffer:
> | + *
> | + * 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)
> | +{
> | + 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,
> | + sizeof(struct pt_regs));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cfpr_active
> | + *
> | + * This function checks number of available regisers in
> | + * transaction checkpointed FPR category.
> | + */
> | +static int tm_cfpr_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_cfpr_get
> | + *
> | + * This function gets in transaction checkpointed FPR registers
> | + *
> | + * When the transaction is active 'fp_state' holds the checkpointed
> | + * values for the current transaction to fall back on if it aborts
> | + * in between. This function gets those checkpointed FPR registers.
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct data {
> | + * u64 fpr[32];
> | + * u64 fpscr;
> | + *};
> | + */
> | +static int tm_cfpr_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + u64 buf[33];
> | + int i;
> | +
> | + 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);
> | +
> | + /* copy to local buffer then write that out */
> | + for (i = 0; i < 32 ; i++)
> | + buf[i] = target->thread.TS_FPR(i);
> | + buf[32] = target->thread.fp_state.fpscr;
> | + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
> | +}
> | +
> | +/*
> | + * tm_cfpr_set
> | + *
> | + * This function sets in transaction checkpointed FPR registers
> | + *
> | + * When the transaction is active 'fp_state' holds the checkpointed
> | + * FPR register values for the current transaction to fall back on
> | + * if it aborts in between. This function sets these checkpointed
> | + * FPR registers.
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct data {
> | + * u64 fpr[32];
> | + * u64 fpscr;
> | + *};
> | + */
> | +static int tm_cfpr_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + u64 buf[33];
> | + int i;
> | +
> | + 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);
> | +
> | + /* copy to local buffer then write that out */
> | + i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
> | + if (i)
> | + return i;
> | + for (i = 0; i < 32 ; i++)
> | + target->thread.TS_FPR(i) = buf[i];
> | + target->thread.fp_state.fpscr = buf[32];
> | + return 0;
> | +}
> | +
> | +/*
> | + * tm_cvmx_active
> | + *
> | + * This function checks the 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
> | + *
> | + * 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.
> | + *
> | + * User interface buffer:
> | + *
> | + * 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
> | + *
> | + * 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.
> | + *
> | + * Userspace interface buffer:
> | + *
> | + * 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 first word of vrsave.
>
> For consistency with the _get() function above, s/first/low-order/ ?
Done.
> | + */
> | + 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 /* CONFIG_PPC_TRANSACTIONAL_MEM */
> |
> | /*
> | * These are our native regset flavors.
> | @@ -808,6 +1341,12 @@ enum powerpc_regset {
> | #ifdef CONFIG_SPE
> | REGSET_SPE,
> | #endif
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + REGSET_TM_SPR, /* TM specific SPR registers */
> | + REGSET_TM_CGPR, /* TM checkpointed GPR registers */
> | + REGSET_TM_CFPR, /* TM checkpointed FPR registers */
> | + REGSET_TM_CVMX, /* TM checkpointed VMX registers */
> | +#endif
> | };
> |
> | static const struct user_regset native_regsets[] = {
> | @@ -842,6 +1381,28 @@ static const struct user_regset native_regsets[] = {
> | .active = evr_active, .get = evr_get, .set = evr_set
> | },
> | #endif
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + [REGSET_TM_SPR] = {
> | + .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
> | + .size = sizeof(u64), .align = sizeof(u64),
> | + .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
> | + },
> | + [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
> | + },
> | + [REGSET_TM_CFPR] = {
> | + .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
> | + .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
> | };
> |
> | static const struct user_regset_view user_ppc_native_view = {
> | @@ -852,24 +1413,35 @@ static const struct user_regset_view user_ppc_native_view = {
> | #ifdef CONFIG_PPC64
> | #include <linux/compat.h>
> |
> | -static int gpr32_get(struct task_struct *target,
> | +static int common_gpr32_get(struct task_struct *target,
> | const struct user_regset *regset,
> | unsigned int pos, unsigned int count,
> | - void *kbuf, void __user *ubuf)
> | + void *kbuf, void __user *ubuf, bool in_tm)
> | {
> | - const unsigned long *regs = &target->thread.regs->gpr[0];
> | + const unsigned long *regs;
> | compat_ulong_t *k = kbuf;
> | compat_ulong_t __user *u = ubuf;
> | compat_ulong_t reg;
> | int i;
> |
> | - if (target->thread.regs == NULL)
> | - return -EIO;
> | + if (in_tm) {
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + regs = &target->thread.ckpt_regs.gpr[0];
> | +#endif
>
> regs uninitialized if in_tm is true and CONFIG_PPC_TRANSACTIONAL_MEM
> is false ? It appears that it cannot/should not happen, how about BUGON() ?
> or at least regs = NULL to silence compiler warnings ?
Yeah it cannot happen, seems like compiler is able to figure that out as
I dont get any warning because of that. BUG_ON sounds like a good option
after the if-else block to verify whether the regs variable got any valid
address value to it or not. Also we can start with regs = NULL at the
starting of the function as well.
>
>
> | + } else {
> | + regs = &target->thread.regs->gpr[0];
> |
> | - if (!FULL_REGS(target->thread.regs)) {
> | - /* We have a partial register set. Fill 14-31 with bogus values */
> | - for (i = 14; i < 32; i++)
> | - target->thread.regs->gpr[i] = NV_REG_POISON;
> | + if (target->thread.regs == NULL)
> | + return -EIO;
> | +
> | + if (!FULL_REGS(target->thread.regs)) {
> | + /*
> | + * We have a partial register set.
> | + * Fill 14-31 with bogus values.
> | + */
> | + for (i = 14; i < 32; i++)
> | + target->thread.regs->gpr[i] = NV_REG_POISON;
> | + }
> | }
> |
> | pos /= sizeof(reg);
> | @@ -909,20 +1481,28 @@ static int gpr32_get(struct task_struct *target,
> | PT_REGS_COUNT * sizeof(reg), -1);
> | }
> |
> | -static int gpr32_set(struct task_struct *target,
> | +static int common_gpr32_set(struct task_struct *target,
> | const struct user_regset *regset,
> | unsigned int pos, unsigned int count,
> | - const void *kbuf, const void __user *ubuf)
> | + const void *kbuf, const void __user *ubuf, bool in_tm)
> | {
> | - unsigned long *regs = &target->thread.regs->gpr[0];
> | + unsigned long *regs;
> | const compat_ulong_t *k = kbuf;
> | const compat_ulong_t __user *u = ubuf;
> | compat_ulong_t reg;
> |
> | - if (target->thread.regs == NULL)
> | - return -EIO;
> | + if (in_tm) {
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + regs = &target->thread.ckpt_regs.gpr[0];
> | +#endif
>
> ditto
Done.
>
> | + } else {
> | + regs = &target->thread.regs->gpr[0];
> |
> | - CHECK_FULL_REGS(target->thread.regs);
> | + if (target->thread.regs == NULL)
> | + return -EIO;
> | +
> | + CHECK_FULL_REGS(target->thread.regs);
> | + }
> |
> | pos /= sizeof(reg);
> | count /= sizeof(reg);
> | @@ -982,6 +1562,39 @@ static int gpr32_set(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 common_gpr32_get(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 common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
> | +}
> | +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
> | +
> | +static int gpr32_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 0);
> | +}
> | +
> | +static int gpr32_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
> | +}
> | /*
> | * These are the regset flavors matching the CONFIG_PPC32 native set.
> | */
> | @@ -1010,6 +1623,29 @@ static const struct user_regset compat_regsets[] = {
> | .active = evr_active, .get = evr_get, .set = evr_set
> | },
> | #endif
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + [REGSET_TM_SPR] = {
> | + .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
> | + .size = sizeof(u64), .align = sizeof(u64),
> | + .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
> | + },
> | + [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
> | + },
> | + [REGSET_TM_CFPR] = {
> | + .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
> | + .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
> | };
> |
> | static const struct user_regset_view user_ppc_compat_view = {
> | --
> | 1.9.3
>
On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
> This patch creates a new function called flush_tm_state to flush
> the existing transactional memory state from the thread. It also
> creates a function called flush_tmregs_to_thread which will then
> be used on subsequent patches in this series.
>
> Signed-off-by: Anshuman Khandual <[email protected]>
> ---
> arch/powerpc/include/asm/switch_to.h | 8 ++++++
> arch/powerpc/kernel/process.c | 49 +++++++++++++++++++++++++++++-------
> 2 files changed, 48 insertions(+), 9 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
> index 58abeda..23752a9 100644
> --- a/arch/powerpc/include/asm/switch_to.h
> +++ b/arch/powerpc/include/asm/switch_to.h
> @@ -82,6 +82,14 @@ static inline void flush_spe_to_thread(struct task_struct *t)
> }
> #endif
>
> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> +extern void flush_tmregs_to_thread(struct task_struct *);
> +#else
> +static inline void flush_tmregs_to_thread(struct task_struct *t)
> +{
> +}
> +#endif
> +
> static inline void clear_task_ebb(struct task_struct *t)
> {
> #ifdef CONFIG_PPC_BOOK3S_64
> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> index aa1df89..c36477b 100644
> --- a/arch/powerpc/kernel/process.c
> +++ b/arch/powerpc/kernel/process.c
> @@ -745,6 +745,45 @@ void restore_tm_state(struct pt_regs *regs)
> #define __switch_to_tm(prev)
> #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
>
> +/*
> + * flush_tm_state
> + *
> + * Flush TM state out so we can copy it. __switch_to_tm() does this
> + * flush but it removes the checkpointed state from the current CPU and
> + * transitions the CPU out of TM mode. Hence we need to call
> + * tm_recheckpoint_new_task() (on the same task) to restore the
> + * checkpointed state back and the TM mode.
> + */
> +static void flush_tm_state(struct task_struct *src)
> +{
> + __switch_to_tm(src);
> + tm_recheckpoint_new_task(src);
> +}
> +
> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> +void flush_tmregs_to_thread(struct task_struct *tsk)
> +{
> + /*
> + * If task is not current, it should have been flushed
> + * already to it's thread_struct during __switch_to().
> + */
> + if (tsk != current)
> + return;
As of now, ptrace does not support process self tracing. So here in this
function we really dont have to do anything if current and tsk does not
match each other. In case they match, we will just post one warning (as
ptrace generic code should have prevented this from happening) and move
on. Will make this function very simple like the following and remove
the next patch as well which was created because of this patch.
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
void flush_tmregs_to_thread(struct task_struct *tsk)
{
/*
* Self tracing is not yet supported through ptrace
* interface. Warn here if attempted.
*/
WARN_ON(tsk == current);
return;
}
#endif
On Tue, 11 Nov 2014 10:56:30 +0530 Anshuman Khandual <[email protected]> wrote:
> This patch adds four new core note sections for PowerPC transactional
> memory and one core note section for general miscellaneous debug registers.
> These addition of new elf core note sections extends the existing elf ABI
> without affecting it in any manner.
>
> Signed-off-by: Anshuman Khandual <[email protected]>
> ---
> include/uapi/linux/elf.h | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
> index ea9bf25..2260fc0 100644
> --- a/include/uapi/linux/elf.h
> +++ b/include/uapi/linux/elf.h
> @@ -379,6 +379,11 @@ typedef struct elf64_shdr {
> #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
> #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
> #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
> +#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
> +#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
> +#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
> +#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
> +#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
> #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
> #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
> #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
ack from me, if that was at all expected.
Please cc Shuah Khan <[email protected]> on the tools/testing/selftests
changes.
On 11/21/14, Anshuman Khandual <[email protected]> wrote:
> On 11/14/2014 03:30 PM, Denis Kirjanov wrote:
>> On 11/13/14, Anshuman Khandual <[email protected]> wrote:
>>> > On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
>>>> >> This patch enables get and set of miscellaneous debug registers
>>>> >> through
>>>> >> ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing
>>>> >> new
>>>> >> powerpc specific register set REGSET_MISC support corresponding to
>>>> >> the
>>>> >> new ELF core note NT_PPC_MISC added previously in this regard.
>>> >
>>> > Right now this one does not compile for "ppc64e_defconfig" and
>>> > "pmac32_defconfig" config options. The patch below will fix it
>>> > and would be part of next revision.
>> It would be great to have a test tool for that under
>> tools/testing/selftests/ptrace/
>>
>
> Hey Denis,
>
> I have already added one test case under selftests/powerpc/tm/ which
> will test all the newly added ELF core note based ptrace requests on
> powerpc. This test case is added as part of the next patch 8/8 in
> the series. Please let me know if the test case does not cover something
> you are looking for.
Oh, I've missed this one. Anyway, I'll wait the second version to test.
Thanks.
>
> _______________________________________________
> Linuxppc-dev mailing list
> [email protected]
> https://lists.ozlabs.org/listinfo/linuxppc-dev
On 11/22/2014 05:13 AM, Andrew Morton wrote:
> On Tue, 11 Nov 2014 10:56:30 +0530 Anshuman Khandual <[email protected]> wrote:
>
>> This patch adds four new core note sections for PowerPC transactional
>> memory and one core note section for general miscellaneous debug registers.
>> These addition of new elf core note sections extends the existing elf ABI
>> without affecting it in any manner.
>>
>> Signed-off-by: Anshuman Khandual <[email protected]>
>> ---
>> include/uapi/linux/elf.h | 5 +++++
>> 1 file changed, 5 insertions(+)
>>
>> diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
>> index ea9bf25..2260fc0 100644
>> --- a/include/uapi/linux/elf.h
>> +++ b/include/uapi/linux/elf.h
>> @@ -379,6 +379,11 @@ typedef struct elf64_shdr {
>> #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
>> #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
>> #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
>> +#define NT_PPC_TM_SPR 0x103 /* PowerPC TM special registers */
>> +#define NT_PPC_TM_CGPR 0x104 /* PowerpC TM checkpointed GPR */
>> +#define NT_PPC_TM_CFPR 0x105 /* PowerPC TM checkpointed FPR */
>> +#define NT_PPC_TM_CVMX 0x106 /* PowerPC TM checkpointed VMX */
>> +#define NT_PPC_MISC 0x107 /* PowerPC miscellaneous registers */
>> #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
>> #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
>> #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
>
> ack from me, if that was at all expected.
Thanks Andrew.
>
> Please cc Shuah Khan <[email protected]> on the tools/testing/selftests
> changes.
Sure, will do.