summaryrefslogtreecommitdiff
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 20:20:17 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 20:20:17 -0800
commit7e3a1e0cccbf6d9bcc05e2710f53a480639cf81d (patch)
tree9860fe05fc3212b5bc6a4c98101a224918ff8634 /arch/sparc/kernel
parentb33c3b84045e880d8a7596f260860038c71cf393 (diff)
parentd844152d85cfcc7d2ef9430a25882604c12da279 (diff)
downloadlwn-7e3a1e0cccbf6d9bcc05e2710f53a480639cf81d.tar.gz
lwn-7e3a1e0cccbf6d9bcc05e2710f53a480639cf81d.zip
Merge tag 'sparc-for-7.0-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/alarsson/linux-sparc
Pull sparc updates from Andreas Larsson: - Hardcode uapi ioctls.h TC* constants to not rely upon struct termio that has been dropped by glibc - Fix bug for fork/clone and add support for clone3 - Add ARCH_HAS_CC_CAN_LINK - API choice improvements and cleanup of unused variables * tag 'sparc-for-7.0-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/alarsson/linux-sparc: sparc: remove unused variable strtab sparc64: fix unused variable warning sparc: don't reference obsolete termio struct for TC* constants sparc: vio: Replace snprintf with strscpy in vio_create_one sparc: Add architecture support for clone3 sparc: Synchronize user stack on fork and clone sparc: Implement ARCH_HAS_CC_CAN_LINK
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r--arch/sparc/kernel/entry.S15
-rw-r--r--arch/sparc/kernel/kernel.h1
-rw-r--r--arch/sparc/kernel/module.c2
-rw-r--r--arch/sparc/kernel/process.c52
-rw-r--r--arch/sparc/kernel/process_32.c23
-rw-r--r--arch/sparc/kernel/process_64.c27
-rw-r--r--arch/sparc/kernel/syscalls.S8
-rw-r--r--arch/sparc/kernel/syscalls/syscall.tbl2
-rw-r--r--arch/sparc/kernel/vio.c4
9 files changed, 103 insertions, 31 deletions
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/module.c b/arch/sparc/kernel/module.c
index 49740450a685..2a8770369beb 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -29,7 +29,6 @@ int module_frob_arch_sections(Elf_Ehdr *hdr,
{
unsigned int symidx;
Elf_Sym *sym;
- char *strtab;
int i;
for (symidx = 0; sechdrs[symidx].sh_type != SHT_SYMTAB; symidx++) {
@@ -39,7 +38,6 @@ int module_frob_arch_sections(Elf_Ehdr *hdr,
}
}
sym = (Elf_Sym *)sechdrs[symidx].sh_addr;
- strtab = (char *)sechdrs[sechdrs[symidx].sh_link].sh_addr;
for (i = 1; i < sechdrs[symidx].sh_size / sizeof(Elf_Sym); i++) {
if (sym[i].st_shndx == SHN_UNDEF) {
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
index 0442ab00518d..d72fa0665943 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -12,19 +12,24 @@
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/signal.h>
+#include <linux/syscalls.h>
#include "kernel.h"
asmlinkage long sparc_fork(struct pt_regs *regs)
{
- unsigned long orig_i1 = regs->u_regs[UREG_I1];
+ unsigned long orig_i1;
long ret;
struct kernel_clone_args args = {
.exit_signal = SIGCHLD,
- /* Reuse the parent's stack for the child. */
- .stack = regs->u_regs[UREG_FP],
};
+ synchronize_user_stack();
+
+ orig_i1 = regs->u_regs[UREG_I1];
+ /* Reuse the parent's stack for the child. */
+ args.stack = regs->u_regs[UREG_FP];
+
ret = kernel_clone(&args);
/* If we get an error and potentially restart the system
@@ -40,16 +45,19 @@ asmlinkage long sparc_fork(struct pt_regs *regs)
asmlinkage long sparc_vfork(struct pt_regs *regs)
{
- unsigned long orig_i1 = regs->u_regs[UREG_I1];
+ unsigned long orig_i1;
long ret;
-
struct kernel_clone_args args = {
.flags = CLONE_VFORK | CLONE_VM,
.exit_signal = SIGCHLD,
- /* Reuse the parent's stack for the child. */
- .stack = regs->u_regs[UREG_FP],
};
+ synchronize_user_stack();
+
+ orig_i1 = regs->u_regs[UREG_I1];
+ /* Reuse the parent's stack for the child. */
+ args.stack = regs->u_regs[UREG_FP];
+
ret = kernel_clone(&args);
/* If we get an error and potentially restart the system
@@ -65,15 +73,18 @@ asmlinkage long sparc_vfork(struct pt_regs *regs)
asmlinkage long sparc_clone(struct pt_regs *regs)
{
- unsigned long orig_i1 = regs->u_regs[UREG_I1];
- unsigned int flags = lower_32_bits(regs->u_regs[UREG_I0]);
+ unsigned long orig_i1;
+ unsigned int flags;
long ret;
+ struct kernel_clone_args args = {0};
- struct kernel_clone_args args = {
- .flags = (flags & ~CSIGNAL),
- .exit_signal = (flags & CSIGNAL),
- .tls = regs->u_regs[UREG_I3],
- };
+ synchronize_user_stack();
+
+ orig_i1 = regs->u_regs[UREG_I1];
+ flags = lower_32_bits(regs->u_regs[UREG_I0]);
+ args.flags = (flags & ~CSIGNAL);
+ args.exit_signal = (flags & CSIGNAL);
+ args.tls = regs->u_regs[UREG_I3];
#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT)) {
@@ -108,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 @@ 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 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
diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c
index 1a1a9d6b8f2e..8c7dd72ef334 100644
--- a/arch/sparc/kernel/vio.c
+++ b/arch/sparc/kernel/vio.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/irq.h>
#include <linux/export.h>
#include <linux/init.h>
@@ -378,8 +379,7 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
* the parent doesn't require the MD node info.
*/
if (node_name != NULL) {
- (void) snprintf(vdev->node_name, VIO_MAX_NAME_LEN, "%s",
- node_name);
+ strscpy(vdev->node_name, node_name);
err = mdesc_get_node_info(hp, mp, node_name,
&vdev->md_node_info);