mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] alpha: Add PTRACE_GETREGSET/PTRACE_SETREGSET support
@ 2026-04-03 15:06 Matt Turner
  2026-04-12 12:14 ` Magnus Lindholm
  0 siblings, 1 reply; 2+ messages in thread
From: Matt Turner @ 2026-04-03 15:06 UTC (permalink / raw)
  To: Magnus Lindholm; +Cc: linux-alpha, linux-kernel, Matt Turner

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 <mattst88@gmail.com>
---
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


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] alpha: Add PTRACE_GETREGSET/PTRACE_SETREGSET support
  2026-04-03 15:06 [PATCH] alpha: Add PTRACE_GETREGSET/PTRACE_SETREGSET support Matt Turner
@ 2026-04-12 12:14 ` Magnus Lindholm
  0 siblings, 0 replies; 2+ messages in thread
From: Magnus Lindholm @ 2026-04-12 12:14 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-alpha, linux-kernel

On Fri, Apr 3, 2026 at 5:07 PM Matt Turner <mattst88@gmail.com> wrote:
>
> 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 <mattst88@gmail.com>
> ---
> 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(-)
>

Thanks for working on this, this is a really useful step
towards getting Alpha onto the generic ptrace/regset
infrastructure.

I’ve been comparing this with parallel patch series:

- alpha: enable regset-based ptrace and core dumps:
https://git.kernel.org/pub/scm/linux/kernel/git/lindholm/alpha.git/log/?h=entry

- SECCOMPv3:
https://lore.kernel.org/linux-alpha/20260409171439.8759-2-linmag7@gmail.com/


Especially what I’ve been referring to as the SECCOMPv3
patch, and I think the cleanest way forward upstream
would be to stage this a bit differently.

In particular, I would suggest:

  1. First let the SECCOMPv3 patch land upstream.

     That patch establishes the new syscall register model
     (r1 = current nr, r2 = original nr, r0 = return value,
     r19 = error flag) and provides the syscall accessors
     expected by generic code.

  2. Then rework this patch on top of that new baseline.

     As it stands, this patch assumes the old Alpha syscall
     conventions (e.g. syscall nr in r0, no rollback state,
     etc.), which will conflict semantically once the
     SECCOMPv3 changes are merged.

     In particular, the asm/syscall.h additions here should
     be aligned with (or dropped in favour of) the
     SECCOMPv3 definitions, so that:

       - syscall_get_nr() uses r1
       - syscall_set_nr() updates r1
       - syscall_rollback() restores r1 from r2
       - return/error handling matches the r0/r19 contract

     My suggestion would be to avoid duplicating that logic
     here entirely and instead rely on the SECCOMPv3 version,
     only adding anything that is still missing for
     PTRACE_GET_SYSCALL_INFO.

  3. Keep this patch focused on ptrace/regset enablement.

     The regset pieces (task_user_regset_view(), GPR/FPR
     sets, etc.) look like a good fit for the generic
     ptrace_request() path once HAVE_ARCH_TRACEHOOK is
     enabled.

     For now, we would leave out the ELF core dump changes
     from the other series and handle those as a follow-up
     once the ptrace side is settled.

  4. One open point is how we want to handle the user stack
     pointer (USP).

     This patch derives USP from thread_info/PCB, while the
     other series snapshots it into pt_regs. It would be
     good to settle on one model before merging regset
     support, as it affects user_stack_pointer() and the
     GPR regset layout. Either approach is workable
     as long as it is consistent.

I think structuring it this way gives a clear
upstream story:

  - SECCOMPv3: establish new syscall state
  - this patch (reworked): enable regset-based ptrace
  - later: possibly switch ELF core dumps to regsets

Any thought on this approach?

Magnus Lindholm

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-04-12 12:14 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-03 15:06 [PATCH] alpha: Add PTRACE_GETREGSET/PTRACE_SETREGSET support Matt Turner
2026-04-12 12:14 ` Magnus Lindholm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®