[PATCH v2 2/2] sparc: Add architecture support for clone3

Ludwig Rydberg posted 2 patches 3 weeks ago
[PATCH v2 2/2] sparc: Add architecture support for clone3
Posted by Ludwig Rydberg 3 weeks ago
Add support for the clone3 system call to the SPARC architectures.

The implementation follows the pattern of the original clone syscall.
However, instead of explicitly calling kernel_clone, the clone3
handler calls the generic sys_clone3 handler in kernel/fork.
In case no stack is provided, the parents stack is reused.

The return value convention for clone3 follows the regular kernel return
value convention (in contrast to the original clone/fork on SPARC).

Closes: https://github.com/sparclinux/issues/issues/10
Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
---
 arch/sparc/include/asm/syscalls.h      |  1 +
 arch/sparc/include/asm/unistd.h        |  2 --
 arch/sparc/kernel/entry.S              | 15 ++++++++++++++
 arch/sparc/kernel/kernel.h             |  1 +
 arch/sparc/kernel/process.c            | 14 +++++++++++++
 arch/sparc/kernel/process_32.c         | 23 ++++++++++++++++------
 arch/sparc/kernel/process_64.c         | 27 ++++++++++++++++++++------
 arch/sparc/kernel/syscalls.S           |  8 ++++++++
 arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
 9 files changed, 78 insertions(+), 15 deletions(-)

diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
index 35575fbfb9dc..282e62b66518 100644
--- a/arch/sparc/include/asm/syscalls.h
+++ b/arch/sparc/include/asm/syscalls.h
@@ -7,5 +7,6 @@ struct pt_regs;
 asmlinkage long sparc_fork(struct pt_regs *regs);
 asmlinkage long sparc_vfork(struct pt_regs *regs);
 asmlinkage long sparc_clone(struct pt_regs *regs);
+asmlinkage long sparc_clone3(struct pt_regs *regs);
 
 #endif /* _SPARC64_SYSCALLS_H */
diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
index 3380411a4537..d6bc76706a7a 100644
--- a/arch/sparc/include/asm/unistd.h
+++ b/arch/sparc/include/asm/unistd.h
@@ -49,8 +49,6 @@
 #define __ARCH_WANT_COMPAT_STAT
 #endif
 
-#define __ARCH_BROKEN_SYS_CLONE3
-
 #ifdef __32bit_syscall_numbers__
 /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
  * it never had the plain ones and there is no value to adding those
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index a3fdee4cd6fa..ea51ef52c952 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -907,6 +907,21 @@ flush_patch_four:
 	jmpl	%l1 + %lo(sparc_vfork), %g0
 	 add	%sp, STACKFRAME_SZ, %o0
 
+	.globl	__sys_clone3, flush_patch_five
+__sys_clone3:
+	mov	%o7, %l5
+flush_patch_five:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ld	[%curptr + TI_TASK], %o4
+	rd	%psr, %g4
+	WRITE_PAUSE
+	rd	%wim, %g5
+	WRITE_PAUSE
+	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
+	add	%sp, STACKFRAME_SZ, %o0
+	call	sparc_clone3
+	 mov	%l5, %o7
+
         .align  4
 linux_sparc_ni_syscall:
 	sethi   %hi(sys_ni_syscall), %l7
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
index 8328a3b78a44..4ee85051521a 100644
--- a/arch/sparc/kernel/kernel.h
+++ b/arch/sparc/kernel/kernel.h
@@ -18,6 +18,7 @@ extern int ncpus_probed;
 asmlinkage long sparc_clone(struct pt_regs *regs);
 asmlinkage long sparc_fork(struct pt_regs *regs);
 asmlinkage long sparc_vfork(struct pt_regs *regs);
+asmlinkage long sparc_clone3(struct pt_regs *regs);
 
 #ifdef CONFIG_SPARC64
 /* setup_64.c */
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
index 7d69877511fa..d72fa0665943 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -12,6 +12,7 @@
 #include <linux/sched/task.h>
 #include <linux/sched/task_stack.h>
 #include <linux/signal.h>
+#include <linux/syscalls.h>
 
 #include "kernel.h"
 
@@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
 
 	return ret;
 }
+
+asmlinkage long sparc_clone3(struct pt_regs *regs)
+{
+	unsigned long sz;
+	struct clone_args __user *cl_args;
+
+	synchronize_user_stack();
+
+	cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
+	sz = regs->u_regs[UREG_I1];
+
+	return sys_clone3(cl_args, sz);
+}
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
index 5a28c0e91bf1..dd8c6c02b0f1 100644
--- a/arch/sparc/kernel/process_32.c
+++ b/arch/sparc/kernel/process_32.c
@@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
  *
+ * clone3() - Uses regular kernel return value conventions
+ *
  * NOTE: We have a separate fork kpsr/kwim because
  *       the parent could change these values between
  *       sys_fork invocation and when we reach here
@@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
 int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 {
 	u64 clone_flags = args->flags;
-	unsigned long sp = args->stack;
 	unsigned long tls = args->tls;
 	struct thread_info *ti = task_thread_info(p);
 	struct pt_regs *childregs, *regs = current_pt_regs();
 	char *new_stack;
+	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
 
 #ifndef CONFIG_SMP
 	if(last_task_used_math == current) {
@@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 	childregs->psr &= ~PSR_EF;
 	clear_tsk_thread_flag(p, TIF_USEDFPU);
 #endif
+	/* Handle return value conventions */
+	if (regs->u_regs[UREG_G1] == __NR_clone3) {
+		/* clone3() - use regular kernel return value convention */
+
+		/* Set the return value for the child. */
+		childregs->u_regs[UREG_I0] = 0;
+	} else {
+		/* clone()/fork() - use SunOS return value convention */
 
-	/* Set the return value for the child. */
-	childregs->u_regs[UREG_I0] = current->pid;
-	childregs->u_regs[UREG_I1] = 1;
+		/* Set the return value for the child. */
+		childregs->u_regs[UREG_I0] = current->pid;
+		childregs->u_regs[UREG_I1] = 1;
 
-	/* Set the return value for the parent. */
-	regs->u_regs[UREG_I1] = 0;
+		/* Set the return value for the parent. */
+		regs->u_regs[UREG_I1] = 0;
+	}
 
 	if (clone_flags & CLONE_SETTLS)
 		childregs->u_regs[UREG_G7] = tls;
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
index 25781923788a..e889da8e4835 100644
--- a/arch/sparc/kernel/process_64.c
+++ b/arch/sparc/kernel/process_64.c
@@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
  * under SunOS are nothing short of bletcherous:
  * Parent -->  %o0 == childs  pid, %o1 == 0
  * Child  -->  %o0 == parents pid, %o1 == 1
+ *
+ * clone3() - Uses regular kernel return value conventions
  */
 int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 {
 	u64 clone_flags = args->flags;
-	unsigned long sp = args->stack;
 	unsigned long tls = args->tls;
 	struct thread_info *t = task_thread_info(p);
 	struct pt_regs *regs = current_pt_regs();
 	struct sparc_stackf *parent_sf;
 	unsigned long child_stack_sz;
 	char *child_trap_frame;
+	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
 
 	/* Calculate offset to stack_frame & pt_regs */
 	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
@@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 	if (t->utraps)
 		t->utraps[0]++;
 
-	/* Set the return value for the child. */
-	t->kregs->u_regs[UREG_I0] = current->pid;
-	t->kregs->u_regs[UREG_I1] = 1;
+	/* Handle return value conventions */
+	if (regs->u_regs[UREG_G1] == __NR_clone3) {
+		/* clone3() - use regular kernel return value convention */
+
+		/* Set the return value for the child. */
+		t->kregs->u_regs[UREG_I0] = 0;
+
+		/* Clear g1 to indicate user thread */
+		t->kregs->u_regs[UREG_G1] = 0;
+	} else {
+		/* clone()/fork() - use SunOS return value convention */
+
+		/* Set the return value for the child. */
+		t->kregs->u_regs[UREG_I0] = current->pid;
+		t->kregs->u_regs[UREG_I1] = 1;
 
-	/* Set the second return value for the parent. */
-	regs->u_regs[UREG_I1] = 0;
+		/* Set the second return value for the parent. */
+		regs->u_regs[UREG_I1] = 0;
+	}
 
 	if (clone_flags & CLONE_SETTLS)
 		t->kregs->u_regs[UREG_G7] = tls;
diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 0e8ab0602c36..96fe8763d70c 100644
--- a/arch/sparc/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
@@ -103,6 +103,12 @@ sys_clone:
 	ba,pt	%xcc, sparc_clone
 	 add	%sp, PTREGS_OFF, %o0
 
+	.align	32
+__sys_clone3:
+	flushw
+	ba,pt	%xcc, sparc_clone3
+	 add	%sp, PTREGS_OFF, %o0
+
 	.globl	ret_from_fork
 ret_from_fork:
 	/* Clear current_thread_info()->new_child. */
@@ -113,6 +119,8 @@ ret_from_fork:
 	brnz,pt	%o0, ret_sys_call
 	 ldx	[%g6 + TI_FLAGS], %l0
 	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l1
+	brz,pt	%l1, ret_sys_call
+	 nop
 	call	%l1
 	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
 	ba,pt	%xcc, ret_sys_call
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 39aa26b6a50b..c0307bb09892 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -480,7 +480,7 @@
 432	common	fsmount				sys_fsmount
 433	common	fspick				sys_fspick
 434	common	pidfd_open			sys_pidfd_open
-# 435 reserved for clone3
+435	common	clone3				__sys_clone3
 436	common	close_range			sys_close_range
 437	common	openat2			sys_openat2
 438	common	pidfd_getfd			sys_pidfd_getfd
-- 
2.35.3
Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
Posted by Andreas Larsson 2 weeks ago
On 2026-01-19 15:47, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
> 
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
> 
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
> 
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
> ---
>  arch/sparc/include/asm/syscalls.h      |  1 +
>  arch/sparc/include/asm/unistd.h        |  2 --
>  arch/sparc/kernel/entry.S              | 15 ++++++++++++++
>  arch/sparc/kernel/kernel.h             |  1 +
>  arch/sparc/kernel/process.c            | 14 +++++++++++++
>  arch/sparc/kernel/process_32.c         | 23 ++++++++++++++++------
>  arch/sparc/kernel/process_64.c         | 27 ++++++++++++++++++++------
>  arch/sparc/kernel/syscalls.S           |  8 ++++++++
>  arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
>  9 files changed, 78 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
> index 35575fbfb9dc..282e62b66518 100644
> --- a/arch/sparc/include/asm/syscalls.h
> +++ b/arch/sparc/include/asm/syscalls.h
> @@ -7,5 +7,6 @@ struct pt_regs;
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
>  asmlinkage long sparc_clone(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #endif /* _SPARC64_SYSCALLS_H */
> diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
> index 3380411a4537..d6bc76706a7a 100644
> --- a/arch/sparc/include/asm/unistd.h
> +++ b/arch/sparc/include/asm/unistd.h
> @@ -49,8 +49,6 @@
>  #define __ARCH_WANT_COMPAT_STAT
>  #endif
>  
> -#define __ARCH_BROKEN_SYS_CLONE3
> -
>  #ifdef __32bit_syscall_numbers__
>  /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
>   * it never had the plain ones and there is no value to adding those
> diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
> index a3fdee4cd6fa..ea51ef52c952 100644
> --- a/arch/sparc/kernel/entry.S
> +++ b/arch/sparc/kernel/entry.S
> @@ -907,6 +907,21 @@ flush_patch_four:
>  	jmpl	%l1 + %lo(sparc_vfork), %g0
>  	 add	%sp, STACKFRAME_SZ, %o0
>  
> +	.globl	__sys_clone3, flush_patch_five
> +__sys_clone3:
> +	mov	%o7, %l5
> +flush_patch_five:
> +	FLUSH_ALL_KERNEL_WINDOWS;
> +	ld	[%curptr + TI_TASK], %o4
> +	rd	%psr, %g4
> +	WRITE_PAUSE
> +	rd	%wim, %g5
> +	WRITE_PAUSE
> +	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
> +	add	%sp, STACKFRAME_SZ, %o0
> +	call	sparc_clone3
> +	 mov	%l5, %o7
> +
>          .align  4
>  linux_sparc_ni_syscall:
>  	sethi   %hi(sys_ni_syscall), %l7
> diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
> index 8328a3b78a44..4ee85051521a 100644
> --- a/arch/sparc/kernel/kernel.h
> +++ b/arch/sparc/kernel/kernel.h
> @@ -18,6 +18,7 @@ extern int ncpus_probed;
>  asmlinkage long sparc_clone(struct pt_regs *regs);
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #ifdef CONFIG_SPARC64
>  /* setup_64.c */
> diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
> index 7d69877511fa..d72fa0665943 100644
> --- a/arch/sparc/kernel/process.c
> +++ b/arch/sparc/kernel/process.c
> @@ -12,6 +12,7 @@
>  #include <linux/sched/task.h>
>  #include <linux/sched/task_stack.h>
>  #include <linux/signal.h>
> +#include <linux/syscalls.h>
>  
>  #include "kernel.h"
>  
> @@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
>  
>  	return ret;
>  }
> +
> +asmlinkage long sparc_clone3(struct pt_regs *regs)
> +{
> +	unsigned long sz;
> +	struct clone_args __user *cl_args;
> +
> +	synchronize_user_stack();
> +
> +	cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
> +	sz = regs->u_regs[UREG_I1];
> +
> +	return sys_clone3(cl_args, sz);
> +}
> diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
> index 5a28c0e91bf1..dd8c6c02b0f1 100644
> --- a/arch/sparc/kernel/process_32.c
> +++ b/arch/sparc/kernel/process_32.c
> @@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
>   *
> + * clone3() - Uses regular kernel return value conventions
> + *
>   * NOTE: We have a separate fork kpsr/kwim because
>   *       the parent could change these values between
>   *       sys_fork invocation and when we reach here
> @@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *ti = task_thread_info(p);
>  	struct pt_regs *childregs, *regs = current_pt_regs();
>  	char *new_stack;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  #ifndef CONFIG_SMP
>  	if(last_task_used_math == current) {
> @@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	childregs->psr &= ~PSR_EF;
>  	clear_tsk_thread_flag(p, TIF_USEDFPU);
>  #endif
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
>  
> -	/* Set the return value for the child. */
> -	childregs->u_regs[UREG_I0] = current->pid;
> -	childregs->u_regs[UREG_I1] = 1;
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = current->pid;
> +		childregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		childregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
> index 25781923788a..e889da8e4835 100644
> --- a/arch/sparc/kernel/process_64.c
> +++ b/arch/sparc/kernel/process_64.c
> @@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
>   * under SunOS are nothing short of bletcherous:
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
> + *
> + * clone3() - Uses regular kernel return value conventions
>   */
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *t = task_thread_info(p);
>  	struct pt_regs *regs = current_pt_regs();
>  	struct sparc_stackf *parent_sf;
>  	unsigned long child_stack_sz;
>  	char *child_trap_frame;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  	/* Calculate offset to stack_frame & pt_regs */
>  	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
> @@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	if (t->utraps)
>  		t->utraps[0]++;
>  
> -	/* Set the return value for the child. */
> -	t->kregs->u_regs[UREG_I0] = current->pid;
> -	t->kregs->u_regs[UREG_I1] = 1;
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = 0;
> +
> +		/* Clear g1 to indicate user thread */
> +		t->kregs->u_regs[UREG_G1] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = current->pid;
> +		t->kregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the second return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the second return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		t->kregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
> index 0e8ab0602c36..96fe8763d70c 100644
> --- a/arch/sparc/kernel/syscalls.S
> +++ b/arch/sparc/kernel/syscalls.S
> @@ -103,6 +103,12 @@ sys_clone:
>  	ba,pt	%xcc, sparc_clone
>  	 add	%sp, PTREGS_OFF, %o0
>  
> +	.align	32
> +__sys_clone3:
> +	flushw
> +	ba,pt	%xcc, sparc_clone3
> +	 add	%sp, PTREGS_OFF, %o0
> +
>  	.globl	ret_from_fork
>  ret_from_fork:
>  	/* Clear current_thread_info()->new_child. */
> @@ -113,6 +119,8 @@ ret_from_fork:
>  	brnz,pt	%o0, ret_sys_call
>  	 ldx	[%g6 + TI_FLAGS], %l0
>  	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l1
> +	brz,pt	%l1, ret_sys_call
> +	 nop
>  	call	%l1
>  	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
>  	ba,pt	%xcc, ret_sys_call
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index 39aa26b6a50b..c0307bb09892 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -480,7 +480,7 @@
>  432	common	fsmount				sys_fsmount
>  433	common	fspick				sys_fspick
>  434	common	pidfd_open			sys_pidfd_open
> -# 435 reserved for clone3
> +435	common	clone3				__sys_clone3
>  436	common	close_range			sys_close_range
>  437	common	openat2			sys_openat2
>  438	common	pidfd_getfd			sys_pidfd_getfd

Reviewed-by: Andreas Larsson <andreas@gaisler.com>
Tested-by: Andreas Larsson <andreas@gaisler.com>

Thanks,
Andreas
Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
Posted by John Paul Adrian Glaubitz 2 weeks, 6 days ago
Hi Ludwig,

On Mon, 2026-01-19 at 15:47 +0100, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
> 
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
> 
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
> 
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>
> ---
>  arch/sparc/include/asm/syscalls.h      |  1 +
>  arch/sparc/include/asm/unistd.h        |  2 --
>  arch/sparc/kernel/entry.S              | 15 ++++++++++++++
>  arch/sparc/kernel/kernel.h             |  1 +
>  arch/sparc/kernel/process.c            | 14 +++++++++++++
>  arch/sparc/kernel/process_32.c         | 23 ++++++++++++++++------
>  arch/sparc/kernel/process_64.c         | 27 ++++++++++++++++++++------
>  arch/sparc/kernel/syscalls.S           |  8 ++++++++
>  arch/sparc/kernel/syscalls/syscall.tbl |  2 +-
>  9 files changed, 78 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/sparc/include/asm/syscalls.h b/arch/sparc/include/asm/syscalls.h
> index 35575fbfb9dc..282e62b66518 100644
> --- a/arch/sparc/include/asm/syscalls.h
> +++ b/arch/sparc/include/asm/syscalls.h
> @@ -7,5 +7,6 @@ struct pt_regs;
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
>  asmlinkage long sparc_clone(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #endif /* _SPARC64_SYSCALLS_H */
> diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
> index 3380411a4537..d6bc76706a7a 100644
> --- a/arch/sparc/include/asm/unistd.h
> +++ b/arch/sparc/include/asm/unistd.h
> @@ -49,8 +49,6 @@
>  #define __ARCH_WANT_COMPAT_STAT
>  #endif
>  
> -#define __ARCH_BROKEN_SYS_CLONE3
> -
>  #ifdef __32bit_syscall_numbers__
>  /* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
>   * it never had the plain ones and there is no value to adding those
> diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
> index a3fdee4cd6fa..ea51ef52c952 100644
> --- a/arch/sparc/kernel/entry.S
> +++ b/arch/sparc/kernel/entry.S
> @@ -907,6 +907,21 @@ flush_patch_four:
>  	jmpl	%l1 + %lo(sparc_vfork), %g0
>  	 add	%sp, STACKFRAME_SZ, %o0
>  
> +	.globl	__sys_clone3, flush_patch_five
> +__sys_clone3:
> +	mov	%o7, %l5
> +flush_patch_five:
> +	FLUSH_ALL_KERNEL_WINDOWS;
> +	ld	[%curptr + TI_TASK], %o4
> +	rd	%psr, %g4
> +	WRITE_PAUSE
> +	rd	%wim, %g5
> +	WRITE_PAUSE
> +	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
> +	add	%sp, STACKFRAME_SZ, %o0
> +	call	sparc_clone3
> +	 mov	%l5, %o7
> +
>          .align  4
>  linux_sparc_ni_syscall:
>  	sethi   %hi(sys_ni_syscall), %l7
> diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
> index 8328a3b78a44..4ee85051521a 100644
> --- a/arch/sparc/kernel/kernel.h
> +++ b/arch/sparc/kernel/kernel.h
> @@ -18,6 +18,7 @@ extern int ncpus_probed;
>  asmlinkage long sparc_clone(struct pt_regs *regs);
>  asmlinkage long sparc_fork(struct pt_regs *regs);
>  asmlinkage long sparc_vfork(struct pt_regs *regs);
> +asmlinkage long sparc_clone3(struct pt_regs *regs);
>  
>  #ifdef CONFIG_SPARC64
>  /* setup_64.c */
> diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
> index 7d69877511fa..d72fa0665943 100644
> --- a/arch/sparc/kernel/process.c
> +++ b/arch/sparc/kernel/process.c
> @@ -12,6 +12,7 @@
>  #include <linux/sched/task.h>
>  #include <linux/sched/task_stack.h>
>  #include <linux/signal.h>
> +#include <linux/syscalls.h>
>  
>  #include "kernel.h"
>  
> @@ -118,3 +119,16 @@ asmlinkage long sparc_clone(struct pt_regs *regs)
>  
>  	return ret;
>  }
> +
> +asmlinkage long sparc_clone3(struct pt_regs *regs)
> +{
> +	unsigned long sz;
> +	struct clone_args __user *cl_args;
> +
> +	synchronize_user_stack();
> +
> +	cl_args = (struct clone_args __user *)regs->u_regs[UREG_I0];
> +	sz = regs->u_regs[UREG_I1];
> +
> +	return sys_clone3(cl_args, sz);
> +}
> diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
> index 5a28c0e91bf1..dd8c6c02b0f1 100644
> --- a/arch/sparc/kernel/process_32.c
> +++ b/arch/sparc/kernel/process_32.c
> @@ -247,6 +247,8 @@ clone_stackframe(struct sparc_stackf __user *dst,
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
>   *
> + * clone3() - Uses regular kernel return value conventions
> + *
>   * NOTE: We have a separate fork kpsr/kwim because
>   *       the parent could change these values between
>   *       sys_fork invocation and when we reach here
> @@ -261,11 +263,11 @@ extern void ret_from_kernel_thread(void);
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *ti = task_thread_info(p);
>  	struct pt_regs *childregs, *regs = current_pt_regs();
>  	char *new_stack;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  #ifndef CONFIG_SMP
>  	if(last_task_used_math == current) {
> @@ -350,13 +352,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	childregs->psr &= ~PSR_EF;
>  	clear_tsk_thread_flag(p, TIF_USEDFPU);
>  #endif
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
>  
> -	/* Set the return value for the child. */
> -	childregs->u_regs[UREG_I0] = current->pid;
> -	childregs->u_regs[UREG_I1] = 1;
> +		/* Set the return value for the child. */
> +		childregs->u_regs[UREG_I0] = current->pid;
> +		childregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		childregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
> index 25781923788a..e889da8e4835 100644
> --- a/arch/sparc/kernel/process_64.c
> +++ b/arch/sparc/kernel/process_64.c
> @@ -564,17 +564,19 @@ void fault_in_user_windows(struct pt_regs *regs)
>   * under SunOS are nothing short of bletcherous:
>   * Parent -->  %o0 == childs  pid, %o1 == 0
>   * Child  -->  %o0 == parents pid, %o1 == 1
> + *
> + * clone3() - Uses regular kernel return value conventions
>   */
>  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  {
>  	u64 clone_flags = args->flags;
> -	unsigned long sp = args->stack;
>  	unsigned long tls = args->tls;
>  	struct thread_info *t = task_thread_info(p);
>  	struct pt_regs *regs = current_pt_regs();
>  	struct sparc_stackf *parent_sf;
>  	unsigned long child_stack_sz;
>  	char *child_trap_frame;
> +	unsigned long sp = args->stack ? args->stack : regs->u_regs[UREG_FP];
>  
>  	/* Calculate offset to stack_frame & pt_regs */
>  	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
> @@ -616,12 +618,25 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>  	if (t->utraps)
>  		t->utraps[0]++;
>  
> -	/* Set the return value for the child. */
> -	t->kregs->u_regs[UREG_I0] = current->pid;
> -	t->kregs->u_regs[UREG_I1] = 1;
> +	/* Handle return value conventions */
> +	if (regs->u_regs[UREG_G1] == __NR_clone3) {
> +		/* clone3() - use regular kernel return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = 0;
> +
> +		/* Clear g1 to indicate user thread */
> +		t->kregs->u_regs[UREG_G1] = 0;
> +	} else {
> +		/* clone()/fork() - use SunOS return value convention */
> +
> +		/* Set the return value for the child. */
> +		t->kregs->u_regs[UREG_I0] = current->pid;
> +		t->kregs->u_regs[UREG_I1] = 1;
>  
> -	/* Set the second return value for the parent. */
> -	regs->u_regs[UREG_I1] = 0;
> +		/* Set the second return value for the parent. */
> +		regs->u_regs[UREG_I1] = 0;
> +	}
>  
>  	if (clone_flags & CLONE_SETTLS)
>  		t->kregs->u_regs[UREG_G7] = tls;
> diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
> index 0e8ab0602c36..96fe8763d70c 100644
> --- a/arch/sparc/kernel/syscalls.S
> +++ b/arch/sparc/kernel/syscalls.S
> @@ -103,6 +103,12 @@ sys_clone:
>  	ba,pt	%xcc, sparc_clone
>  	 add	%sp, PTREGS_OFF, %o0
>  
> +	.align	32
> +__sys_clone3:
> +	flushw
> +	ba,pt	%xcc, sparc_clone3
> +	 add	%sp, PTREGS_OFF, %o0
> +
>  	.globl	ret_from_fork
>  ret_from_fork:
>  	/* Clear current_thread_info()->new_child. */
> @@ -113,6 +119,8 @@ ret_from_fork:
>  	brnz,pt	%o0, ret_sys_call
>  	 ldx	[%g6 + TI_FLAGS], %l0
>  	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l1
> +	brz,pt	%l1, ret_sys_call
> +	 nop
>  	call	%l1
>  	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
>  	ba,pt	%xcc, ret_sys_call
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index 39aa26b6a50b..c0307bb09892 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -480,7 +480,7 @@
>  432	common	fsmount				sys_fsmount
>  433	common	fspick				sys_fspick
>  434	common	pidfd_open			sys_pidfd_open
> -# 435 reserved for clone3
> +435	common	clone3				__sys_clone3
>  436	common	close_range			sys_close_range
>  437	common	openat2			sys_openat2
>  438	common	pidfd_getfd			sys_pidfd_getfd

I can confirm that this patch passes both the clone3 selftest from within the
kernel sources in ./tools/testing/selftests/clone3:

root@raverin:/usr/src/linux/tools/testing/selftests/clone3# uname -a
Linux raverin 6.19.0-rc6+ #21 Tue Jan 20 12:41:03 UTC 2026 sparc64 GNU/Linux
root@raverin:/usr/src/linux/tools/testing/selftests/clone3# ./clone3
TAP version 13
1..19
# clone3() syscall supported
# Running test 'simple clone3()'
# [1495] Trying clone3() with flags 0 (size 0)
# I am the parent (1495). My child's pid is 1496
# I am the child, my PID is 1496
# [1495] clone3() with flags says: 0 expected 0
ok 1 simple clone3()
# Running test 'clone3() in a new PID_NS'
# [1495] Trying clone3() with flags 0x20000000 (size 0)
# I am the parent (1495). My child's pid is 1497
# I am the child, my PID is 1
# [1495] clone3() with flags says: 0 expected 0
ok 2 clone3() in a new PID_NS
# Running test 'CLONE_ARGS_SIZE_VER0'
# [1495] Trying clone3() with flags 0 (size 64)
# I am the parent (1495). My child's pid is 1498
# I am the child, my PID is 1498
# [1495] clone3() with flags says: 0 expected 0
ok 3 CLONE_ARGS_SIZE_VER0
# Running test 'CLONE_ARGS_SIZE_VER0 - 8'
# [1495] Trying clone3() with flags 0 (size 56)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 4 CLONE_ARGS_SIZE_VER0 - 8
# Running test 'sizeof(struct clone_args) + 8'
# [1495] Trying clone3() with flags 0 (size 96)
# I am the parent (1495). My child's pid is 1499
# I am the child, my PID is 1499
# [1495] clone3() with flags says: 0 expected 0
ok 5 sizeof(struct clone_args) + 8
# Running test 'exit_signal with highest 32 bits non-zero'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 6 exit_signal with highest 32 bits non-zero
# Running test 'negative 32-bit exit_signal'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 7 negative 32-bit exit_signal
# Running test 'exit_signal not fitting into CSIGNAL mask'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 8 exit_signal not fitting into CSIGNAL mask
# Running test 'NSIG < exit_signal < CSIG'
# [1495] Trying clone3() with flags 0 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 9 NSIG < exit_signal < CSIG
# Running test 'Arguments sizeof(struct clone_args) + 8'
# [1495] Trying clone3() with flags 0 (size 96)
# I am the parent (1495). My child's pid is 1500
# I am the child, my PID is 1500
# [1495] clone3() with flags says: 0 expected 0
ok 10 Arguments sizeof(struct clone_args) + 8
# Running test 'Arguments sizeof(struct clone_args) + 16'
# [1495] Trying clone3() with flags 0 (size 104)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 11 Arguments sizeof(struct clone_args) + 16
# Running test 'Arguments sizeof(struct clone_arg) * 2'
# [1495] Trying clone3() with flags 0 (size 104)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 12 Arguments sizeof(struct clone_arg) * 2
# Running test 'Arguments > page size'
# [1495] Trying clone3() with flags 0 (size 8200)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 13 Arguments > page size
# Running test 'CLONE_ARGS_SIZE_VER0 in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 64)
# I am the parent (1495). My child's pid is 1501
# I am the child, my PID is 1
# [1495] clone3() with flags says: 0 expected 0
ok 14 CLONE_ARGS_SIZE_VER0 in a new PID NS
# Running test 'CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 56)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 15 CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS
# Running test 'sizeof(struct clone_args) + 8 in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 96)
# I am the parent (1495). My child's pid is 1502
# I am the child, my PID is 1
# [1495] clone3() with flags says: 0 expected 0
ok 16 sizeof(struct clone_args) + 8 in a new PID NS
# Running test 'Arguments > page size in a new PID NS'
# [1495] Trying clone3() with flags 0x20000000 (size 8200)
# Argument list too long - Failed to create new process
# [1495] clone3() with flags says: -7 expected -7
ok 17 Arguments > page size in a new PID NS
# Time namespaces are not supported
ok 18 # SKIP New time NS
# Running test 'exit signal (SIGCHLD) in flags'
# [1495] Trying clone3() with flags 0x14 (size 0)
# Invalid argument - Failed to create new process
# [1495] clone3() with flags says: -22 expected -22
ok 19 exit signal (SIGCHLD) in flags
# 1 skipped test(s) detected. Consider enabling relevant config options to improve coverage.
# Totals: pass:18 fail:0 xfail:0 xpass:0 skip:1 error:0
root@raverin:/usr/src/linux/tools/testing/selftests/clone3#

and the libcamera build and testsuite:

Ok:                38
Expected Fail:     1
Fail:              0
Skipped:           31

Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913
Re: [PATCH v2 2/2] sparc: Add architecture support for clone3
Posted by Arnd Bergmann 3 weeks ago
On Mon, Jan 19, 2026, at 15:47, Ludwig Rydberg wrote:
> Add support for the clone3 system call to the SPARC architectures.
>
> The implementation follows the pattern of the original clone syscall.
> However, instead of explicitly calling kernel_clone, the clone3
> handler calls the generic sys_clone3 handler in kernel/fork.
> In case no stack is provided, the parents stack is reused.
>
> The return value convention for clone3 follows the regular kernel return
> value convention (in contrast to the original clone/fork on SPARC).
>
> Closes: https://github.com/sparclinux/issues/issues/10
> Signed-off-by: Ludwig Rydberg <ludwig.rydberg@gaisler.com>

Thanks a lot for addressing this!

Acked-by: Arnd Bergmann <arnd@arndb.de>