[PATCH -next 1/2] riscv: ptrace: Implement PTRACE_{PEEK,POKE}USR
Yipeng Zou
zouyipeng at huawei.com
Sun Aug 21 20:01:04 PDT 2022
Implement PTRACE_{PEEK,POKE}USR for RV64 user app to peek/poke tracee's
space register.
As the Linux manual page say: the addr is define to a word offset in
the tracee's USER area.In riscv it's defined in:
struct user {
struct user_regs {
ulong pc; // addr 0
ulong ra; // addr 8
...
...
ulong t6; // addr 248
}
struct d_ext_state {
u64 f[0]; // addr 256
...
...
u64 f[31]; // addr 504
u32 fcsr; // addr 512 read-only
}
}
Signed-off-by: Yipeng Zou <zouyipeng at huawei.com>
---
arch/riscv/kernel/ptrace.c | 66 ++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index 2ae8280ae475..6dc0687e419f 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -215,12 +215,78 @@ void ptrace_disable(struct task_struct *child)
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
+static inline unsigned long ptrace_get_gpr_reg(struct task_struct *child, unsigned long offset)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+
+ return *((unsigned long *)regs + offset / sizeof(regs->a0));
+}
+
+static inline int ptrace_set_gpr_reg(struct task_struct *child, unsigned long offset,
+ unsigned long data)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+
+ *((unsigned long *)regs + offset / sizeof(regs->a0)) = data;
+ return 0;
+}
+
+#ifdef CONFIG_FPU
+static inline unsigned long ptrace_get_fpr_reg(struct task_struct *child, unsigned long offset)
+{
+ struct __riscv_d_ext_state *fstate = &child->thread.fstate;
+
+ if (offset <= offsetof(struct __riscv_d_ext_state, f[31]))
+ return fstate->f[offset / sizeof(fstate->f[0])];
+ else if (offset == offsetof(struct __riscv_d_ext_state, fcsr))
+ return fstate->fcsr;
+ return -EIO;
+}
+
+static inline int ptrace_set_fpr_reg(struct task_struct *child, unsigned long offset,
+ unsigned long data)
+{
+ struct __riscv_d_ext_state *fstate = &child->thread.fstate;
+
+ if (offset <= offsetof(struct __riscv_d_ext_state, f[31]))
+ fstate->f[offset / sizeof(fstate->f[0])] = data;
+ else /* Only f[0] ~ f[31] can write by PTRACE_POKEUSR */
+ return -EIO;
+ return 0;
+}
+#endif
+
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
long ret = -EIO;
+ unsigned long __user *datap = (unsigned long __user *)data;
switch (request) {
+ case PTRACE_PEEKUSR:
+ case PTRACE_POKEUSR:
+ /* addr must be aligned to register size */
+ if (addr & (sizeof(addr) - 1))
+ break;
+
+ if (addr < sizeof(struct user_regs_struct)) {
+ if (request == PTRACE_PEEKUSR)
+ ret = put_user(ptrace_get_gpr_reg(child, addr), datap);
+ else // PTRACE_POKEUSR
+ ret = ptrace_set_gpr_reg(child, addr, data);
+#ifdef CONFIG_FPU
+ } else if (addr < (sizeof(struct user_regs_struct) +
+ sizeof(struct __riscv_d_ext_state))) {
+ addr -= sizeof(struct user_regs_struct);
+ if (request == PTRACE_PEEKUSR)
+ ret = put_user(ptrace_get_fpr_reg(child, addr), datap);
+ else // PTRACE_POKEUSR
+ ret = ptrace_set_fpr_reg(child, addr, data);
+#endif
+ } else {
+ // addr invalid
+ }
+ break;
default:
ret = ptrace_request(child, request, addr, data);
break;
--
2.17.1
More information about the linux-riscv
mailing list