Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1918c7f5 authored by Al Viro's avatar Al Viro
Browse files

sparc64: switch to generic kernel_thread()



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent dff933da
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ config SPARC64
	select ARCH_HAVE_NMI_SAFE_CMPXCHG
	select HAVE_C_RECORDMCOUNT
	select NO_BOOTMEM
	select GENERIC_KERNEL_THREAD

config ARCH_DEFCONFIG
	string
+0 −2
Original line number Diff line number Diff line
@@ -188,8 +188,6 @@ do { \
/* Free all resources held by a thread. */
#define release_thread(tsk)		do { } while (0)

extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);

extern unsigned long get_wchan(struct task_struct *task);

#define task_pt_regs(tsk) (task_thread_info(tsk)->kregs)
+3 −0
Original line number Diff line number Diff line
@@ -32,6 +32,9 @@ static inline bool pt_regs_clear_syscall(struct pt_regs *regs)
#define arch_ptrace_stop(exit_code, info) \
	synchronize_user_stack()

#define current_pt_regs() \
	((struct pt_regs *)((unsigned long)current_thread_info() + THREAD_SIZE) - 1)

struct global_reg_snapshot {
	unsigned long		tstate;
	unsigned long		tpc;
+30 −77
Original line number Diff line number Diff line
@@ -538,51 +538,44 @@ asmlinkage long sparc_do_fork(unsigned long clone_flags,
 * Child  -->  %o0 == parents pid, %o1 == 1
 */
int copy_thread(unsigned long clone_flags, unsigned long sp,
		unsigned long unused,
		unsigned long arg,
		struct task_struct *p, struct pt_regs *regs)
{
	struct thread_info *t = task_thread_info(p);
	struct sparc_stackf *parent_sf;
	unsigned long child_stack_sz;
	char *child_trap_frame;
	int kernel_thread;

	kernel_thread = (regs->tstate & TSTATE_PRIV) ? 1 : 0;
	parent_sf = ((struct sparc_stackf *) regs) - 1;

	/* Calculate offset to stack_frame & pt_regs */
	child_stack_sz = ((STACKFRAME_SZ + TRACEREG_SZ) +
			  (kernel_thread ? STACKFRAME_SZ : 0));
	child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
	child_trap_frame = (task_stack_page(p) +
			    (THREAD_SIZE - child_stack_sz));
	memcpy(child_trap_frame, parent_sf, child_stack_sz);

	__thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = 
		(regs->tstate + 1) & TSTATE_CWP;
	t->new_child = 1;
	t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS;
	t->kregs = (struct pt_regs *) (child_trap_frame +
				       sizeof(struct sparc_stackf));
	t->fpsaved[0] = 0;

	if (kernel_thread) {
		struct sparc_stackf *child_sf = (struct sparc_stackf *)
			(child_trap_frame + (STACKFRAME_SZ + TRACEREG_SZ));

		/* Zero terminate the stack backtrace.  */
		child_sf->fp = NULL;
		t->kregs->u_regs[UREG_FP] =
		  ((unsigned long) child_sf) - STACK_BIAS;

	if (unlikely(p->flags & PF_KTHREAD)) {
		memset(child_trap_frame, 0, child_stack_sz);
		__thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = 
			(current_pt_regs()->tstate + 1) & TSTATE_CWP;
		t->current_ds = ASI_P;
		t->kregs->u_regs[UREG_G6] = (unsigned long) t;
		t->kregs->u_regs[UREG_G4] = (unsigned long) t->task;
	} else {
		t->kregs->u_regs[UREG_G1] = sp; /* function */
		t->kregs->u_regs[UREG_G2] = arg;
		return 0;
	}

	parent_sf = ((struct sparc_stackf *) regs) - 1;
	memcpy(child_trap_frame, parent_sf, child_stack_sz);
	if (t->flags & _TIF_32BIT) {
		sp &= 0x00000000ffffffffUL;
		regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
	}
	t->kregs->u_regs[UREG_FP] = sp;
	__thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = 
		(regs->tstate + 1) & TSTATE_CWP;
	t->current_ds = ASI_AIUS;
	if (sp != regs->u_regs[UREG_FP]) {
		unsigned long csp;
@@ -594,7 +587,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
	}
	if (t->utraps)
		t->utraps[0]++;
	}

	/* Set the return value for the child. */
	t->kregs->u_regs[UREG_I0] = current->pid;
@@ -609,45 +601,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
	return 0;
}

/*
 * This is the mechanism for creating a new kernel thread.
 *
 * NOTE! Only a kernel-only process(ie the swapper or direct descendants
 * who haven't done an "execve()") should use this: it will work within
 * a system call from a "real" process, but the process memory space will
 * not be freed until both the parent and the child have exited.
 */
pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
	long retval;

	/* If the parent runs before fn(arg) is called by the child,
	 * the input registers of this function can be clobbered.
	 * So we stash 'fn' and 'arg' into global registers which
	 * will not be modified by the parent.
	 */
	__asm__ __volatile__("mov %4, %%g2\n\t"	   /* Save FN into global */
			     "mov %5, %%g3\n\t"	   /* Save ARG into global */
			     "mov %1, %%g1\n\t"	   /* Clone syscall nr. */
			     "mov %2, %%o0\n\t"	   /* Clone flags. */
			     "mov 0, %%o1\n\t"	   /* usp arg == 0 */
			     "t 0x6d\n\t"	   /* Linux/Sparc clone(). */
			     "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
			     " mov %%o0, %0\n\t"
			     "jmpl %%g2, %%o7\n\t"   /* Call the function. */
			     " mov %%g3, %%o0\n\t"   /* Set arg in delay. */
			     "mov %3, %%g1\n\t"
			     "t 0x6d\n\t"	   /* Linux/Sparc exit(). */
			     /* Notreached by child. */
			     "1:" :
			     "=r" (retval) :
			     "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
			     "i" (__NR_exit),  "r" (fn), "r" (arg) :
			     "g1", "g2", "g3", "o0", "o1", "memory", "cc");
	return retval;
}
EXPORT_SYMBOL(kernel_thread);

typedef struct {
	union {
		unsigned int	pr_regs[32];
+8 −3
Original line number Diff line number Diff line
@@ -112,11 +112,16 @@ sys_clone:
ret_from_syscall:
	/* Clear current_thread_info()->new_child. */
	stb	%g0, [%g6 + TI_NEW_CHILD]
	ldx	[%g6 + TI_FLAGS], %l0
	call	schedule_tail
	 mov	%g7, %o0
	ba,pt	%xcc, ret_sys_call
	ldx	[%sp + PTREGS_OFF + PT_V9_I0], %o0
	brnz,a,pt	%o0, ret_sys_call
	 ldx	[%g6 + TI_FLAGS], %l0
	ldx	[%sp + PTREGS_OFF + PT_V9_G1], %l0
	call	%l0
	 ldx	[%sp + PTREGS_OFF + PT_V9_G2], %o0
	call	do_exit	! will not return
	 mov	0,%o0

	.globl	sparc_exit
	.type	sparc_exit,#function