[PATCH] alpha: Add PTRACE_GETREGSET/PTRACE_SETREGSET support

Matt Turner <[email protected]>
Newsgroups org.kernel.vger.linux-alpha,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Enable HAVE_ARCH_TRACEHOOK and implement task_user_regset_view() to
provide regset-based register access on Alpha. This adds support for
PTRACE_GETREGSET and PTRACE_SETREGSET, which are handled by the
generic ptrace_request() path.

Two regsets are defined:
  - REGSET_GENERAL (NT_PRSTATUS): 33 registers (32 GPRs + unique),
    matching the existing elf_gregset_t layout used by dump_elf_thread()
  - REGSET_FPU (NT_PRFPREG): 32 floating-point registers from
    thread_info->fp[]

Also implement the full set of syscall accessor functions in
asm/syscall.h and user_stack_pointer() in asm/ptrace.h, which are
required by the generic PTRACE_GET_SYSCALL_INFO code that
HAVE_ARCH_TRACEHOOK enables.

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Matt Turner <[email protected]>
---
This is an implementation of PTRACE_{G,S}ETREGSET using the standard
infrastructure.

 arch/alpha/Kconfig               |   1 +
 arch/alpha/include/asm/ptrace.h  |  12 +++
 arch/alpha/include/asm/syscall.h |  67 ++++++++++++++-
 arch/alpha/kernel/ptrace.c       | 137 +++++++++++++++++++++++++++++++
 4 files changed, 215 insertions(+), 2 deletions(-)

diff --git ./arch/alpha/Kconfig ./arch/alpha/Kconfig
index 6c7dbf0adad6..3e61dfaa2ba9 100644
--- ./arch/alpha/Kconfig
+++ ./arch/alpha/Kconfig
@@ -14,6 +14,7 @@ config ALPHA
 	select FORCE_PCI
 	select PCI_DOMAINS if PCI
 	select PCI_SYSCALL if PCI
+	select HAVE_ARCH_TRACEHOOK
 	select HAVE_ASM_MODVERSIONS
 	select HAVE_PAGE_SIZE_8KB
 	select HAVE_PCSPKR_PLATFORM
diff --git ./arch/alpha/include/asm/ptrace.h ./arch/alpha/include/asm/ptrace.h
index 3557ce64ed21..af97a8d3ed5b 100644
--- ./arch/alpha/include/asm/ptrace.h
+++ ./arch/alpha/include/asm/ptrace.h
@@ -4,6 +4,7 @@
 
 #include <uapi/asm/ptrace.h>
 
+#include <asm/thread_info.h>
 
 #define arch_has_single_step()		(1)
 #define user_mode(regs) (((regs)->ps & 8) != 0)
@@ -24,4 +25,15 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
 	return regs->r0;
 }
 
+/*
+ * The user stack pointer is not stored in pt_regs.  It lives in the
+ * PCB, which sits at the base of the kernel stack (i.e. in thread_info).
+ */
+static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+	struct thread_info *ti =
+		(struct thread_info *)((char *)(regs + 1) - 2 * PAGE_SIZE);
+	return ti->pcb.usp;
+}
+
 #endif
diff --git ./arch/alpha/include/asm/syscall.h ./arch/alpha/include/asm/syscall.h
index f21babaeed85..dab35748ef74 100644
--- ./arch/alpha/include/asm/syscall.h
+++ ./arch/alpha/include/asm/syscall.h
@@ -3,10 +3,31 @@
 #define _ASM_ALPHA_SYSCALL_H
 
 #include <uapi/linux/audit.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
 
-static inline int syscall_get_arch(struct task_struct *task)
+static inline int syscall_get_nr(struct task_struct *task,
+				 struct pt_regs *regs)
 {
-	return AUDIT_ARCH_ALPHA;
+	return regs->r0;
+}
+
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs, int nr)
+{
+	regs->r0 = nr;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+				    struct pt_regs *regs)
+{
+	/* Alpha does not save the original syscall number separately. */
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+				     struct pt_regs *regs)
+{
+	return regs->r19 ? regs->r0 : 0;
 }
 
 static inline long syscall_get_return_value(struct task_struct *task,
@@ -15,4 +36,46 @@ static inline long syscall_get_return_value(struct task_struct *task,
 	return regs->r0;
 }
 
+static inline void syscall_set_return_value(struct task_struct *task,
+					    struct pt_regs *regs,
+					    int error, long val)
+{
+	if (error) {
+		regs->r0 = error;
+		regs->r19 = 1;		/* a3: signal error */
+	} else {
+		regs->r0 = val;
+		regs->r19 = 0;		/* a3: no error */
+	}
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned long *args)
+{
+	args[0] = regs->r16;		/* a0 */
+	args[1] = regs->r17;		/* a1 */
+	args[2] = regs->r18;		/* a2 */
+	args[3] = regs->r19;		/* a3 */
+	args[4] = regs->r20;		/* a4 */
+	args[5] = regs->r21;		/* a5 */
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	regs->r16 = args[0];		/* a0 */
+	regs->r17 = args[1];		/* a1 */
+	regs->r18 = args[2];		/* a2 */
+	regs->r19 = args[3];		/* a3 */
+	regs->r20 = args[4];		/* a4 */
+	regs->r21 = args[5];		/* a5 */
+}
+
+static inline int syscall_get_arch(struct task_struct *task)
+{
+	return AUDIT_ARCH_ALPHA;
+}
+
 #endif	/* _ASM_ALPHA_SYSCALL_H */
diff --git ./arch/alpha/kernel/ptrace.c ./arch/alpha/kernel/ptrace.c
index fde4c68e7a0b..f1a10c4112e7 100644
--- ./arch/alpha/kernel/ptrace.c
+++ ./arch/alpha/kernel/ptrace.c
@@ -12,10 +12,12 @@
 #include <linux/smp.h>
 #include <linux/errno.h>
 #include <linux/ptrace.h>
+#include <linux/regset.h>
 #include <linux/user.h>
 #include <linux/security.h>
 #include <linux/signal.h>
 #include <linux/audit.h>
+#include <linux/elf.h>
 
 #include <linux/uaccess.h>
 #include <asm/fpu.h>
@@ -274,6 +276,141 @@ void ptrace_disable(struct task_struct *child)
 	user_disable_single_step(child);
 }
 
+/*
+ * Get the general registers from a task.
+ * Follows the elf_gregset_t layout: 32 GPRs + unique (replacing PS).
+ */
+static int genregs_get(struct task_struct *target,
+		       const struct user_regset *regset,
+		       struct membuf to)
+{
+	elf_gregset_t gregs;
+
+	dump_elf_thread(gregs, task_pt_regs(target),
+			task_thread_info(target));
+	return membuf_write(&to, &gregs, sizeof(gregs));
+}
+
+/*
+ * Set the general registers for a task.
+ */
+static int genregs_set(struct task_struct *target,
+		       const struct user_regset *regset,
+		       unsigned int pos, unsigned int count,
+		       const void *kbuf, const void __user *ubuf)
+{
+	elf_gregset_t gregs;
+	struct pt_regs *regs = task_pt_regs(target);
+	struct switch_stack *sw = ((struct switch_stack *)regs) - 1;
+	struct thread_info *ti = task_thread_info(target);
+	int ret;
+
+	/* Start with the current register values. */
+	dump_elf_thread(gregs, regs, ti);
+
+	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				 &gregs, 0, sizeof(gregs));
+	if (ret)
+		return ret;
+
+	/* Apply the register values back. */
+	regs->r0  = gregs[0];
+	regs->r1  = gregs[1];
+	regs->r2  = gregs[2];
+	regs->r3  = gregs[3];
+	regs->r4  = gregs[4];
+	regs->r5  = gregs[5];
+	regs->r6  = gregs[6];
+	regs->r7  = gregs[7];
+	regs->r8  = gregs[8];
+	sw->r9    = gregs[9];
+	sw->r10   = gregs[10];
+	sw->r11   = gregs[11];
+	sw->r12   = gregs[12];
+	sw->r13   = gregs[13];
+	sw->r14   = gregs[14];
+	sw->r15   = gregs[15];
+	regs->r16 = gregs[16];
+	regs->r17 = gregs[17];
+	regs->r18 = gregs[18];
+	regs->r19 = gregs[19];
+	regs->r20 = gregs[20];
+	regs->r21 = gregs[21];
+	regs->r22 = gregs[22];
+	regs->r23 = gregs[23];
+	regs->r24 = gregs[24];
+	regs->r25 = gregs[25];
+	regs->r26 = gregs[26];
+	regs->r27 = gregs[27];
+	regs->r28 = gregs[28];
+	regs->gp  = gregs[29];
+	ti->pcb.usp = gregs[30];
+	regs->pc  = gregs[31];
+	ti->pcb.unique = gregs[32];
+
+	return 0;
+}
+
+/*
+ * Get the floating-point registers from a task.
+ */
+static int fpregs_get(struct task_struct *target,
+		      const struct user_regset *regset,
+		      struct membuf to)
+{
+	return membuf_write(&to, task_thread_info(target)->fp,
+			    ELF_NFPREG * sizeof(elf_fpreg_t));
+}
+
+/*
+ * Set the floating-point registers for a task.
+ */
+static int fpregs_set(struct task_struct *target,
+		      const struct user_regset *regset,
+		      unsigned int pos, unsigned int count,
+		      const void *kbuf, const void __user *ubuf)
+{
+	return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				  task_thread_info(target)->fp,
+				  0, ELF_NFPREG * sizeof(elf_fpreg_t));
+}
+
+enum alpha_regset {
+	REGSET_GENERAL,
+	REGSET_FPU,
+};
+
+static const struct user_regset alpha_regsets[] = {
+	[REGSET_GENERAL] = {
+		USER_REGSET_NOTE_TYPE(PRSTATUS),
+		.n = ELF_NGREG,
+		.size = sizeof(elf_greg_t),
+		.align = sizeof(elf_greg_t),
+		.regset_get = genregs_get,
+		.set = genregs_set,
+	},
+	[REGSET_FPU] = {
+		USER_REGSET_NOTE_TYPE(PRFPREG),
+		.n = ELF_NFPREG,
+		.size = sizeof(elf_fpreg_t),
+		.align = sizeof(elf_fpreg_t),
+		.regset_get = fpregs_get,
+		.set = fpregs_set,
+	},
+};
+
+static const struct user_regset_view user_alpha_view = {
+	.name = "alpha",
+	.e_machine = ELF_ARCH,
+	.regsets = alpha_regsets,
+	.n = ARRAY_SIZE(alpha_regsets),
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+	return &user_alpha_view;
+}
+
 long arch_ptrace(struct task_struct *child, long request,
 		 unsigned long addr, unsigned long data)
 {
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.