sparc: Add architecture support for clone3

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>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Andreas Larsson <andreas@gaisler.com>
Tested-by: Andreas Larsson <andreas@gaisler.com>
Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Link: https://lore.kernel.org/r/20260119144753.27945-3-ludwig.rydberg@gaisler.com
Signed-off-by: Andreas Larsson <andreas@gaisler.com>
This commit is contained in:
Ludwig Rydberg 2026-01-19 15:47:53 +01:00 committed by Andreas Larsson
parent e38eba3b77
commit 2153b2e891
9 changed files with 78 additions and 15 deletions

View file

@ -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 */

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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);
}

View file

@ -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] = current->pid;
childregs->u_regs[UREG_I1] = 1;
/* 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 parent. */
regs->u_regs[UREG_I1] = 0;
/* 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;
}
if (clone_flags & CLONE_SETTLS)
childregs->u_regs[UREG_G7] = tls;

View file

@ -564,17 +564,19 @@ barf:
* 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 second return value for the parent. */
regs->u_regs[UREG_I1] = 0;
/* 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;
}
if (clone_flags & CLONE_SETTLS)
t->kregs->u_regs[UREG_G7] = tls;

View file

@ -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

View file

@ -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