diff mbox series

[-next,1/2] riscv: ptrace: Implement PTRACE_{PEEK,POKE}USR

Message ID 20220822030105.16053-2-zouyipeng@huawei.com (mailing list archive)
State New, archived
Headers show
Series riscv: ptrace: implement PTRACE_{PEEK,POKE}USR | expand

Commit Message

Yipeng Zou Aug. 22, 2022, 3:01 a.m. UTC
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@huawei.com>
---
 arch/riscv/kernel/ptrace.c | 66 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
diff mbox series

Patch

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;