int copy_thread()

in kernel/process.c [141:212]


int copy_thread(unsigned long clone_flags, unsigned long usp, unsigned long arg,
		struct task_struct *p, unsigned long tls)
{
	struct fork_frame {
		struct switch_stack sw;
		struct pt_regs regs;
	} *frame;

	frame = (struct fork_frame *) (task_stack_page(p) + THREAD_SIZE) - 1;

	p->thread.ksp = (unsigned long)frame;
	p->thread.esp0 = (unsigned long)&frame->regs;

	/*
	 * Must save the current SFC/DFC value, NOT the value when
	 * the parent was last descheduled - RGH  10-08-96
	 */
	p->thread.fc = USER_DATA;

	if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) {
		/* kernel thread */
		memset(frame, 0, sizeof(struct fork_frame));
		frame->regs.sr = PS_S;
		frame->sw.a3 = usp; /* function */
		frame->sw.d7 = arg;
		frame->sw.retpc = (unsigned long)ret_from_kernel_thread;
		p->thread.usp = 0;
		return 0;
	}
	memcpy(frame, container_of(current_pt_regs(), struct fork_frame, regs),
		sizeof(struct fork_frame));
	frame->regs.d0 = 0;
	frame->sw.retpc = (unsigned long)ret_from_fork;
	p->thread.usp = usp ?: rdusp();

	if (clone_flags & CLONE_SETTLS)
		task_thread_info(p)->tp_value = tls;

#ifdef CONFIG_FPU
	if (!FPU_IS_EMU) {
		/* Copy the current fpu state */
		asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory");

		if (!CPU_IS_060 ? p->thread.fpstate[0] : p->thread.fpstate[2]) {
			if (CPU_IS_COLDFIRE) {
				asm volatile ("fmovemd %/fp0-%/fp7,%0\n\t"
					      "fmovel %/fpiar,%1\n\t"
					      "fmovel %/fpcr,%2\n\t"
					      "fmovel %/fpsr,%3"
					      :
					      : "m" (p->thread.fp[0]),
						"m" (p->thread.fpcntl[0]),
						"m" (p->thread.fpcntl[1]),
						"m" (p->thread.fpcntl[2])
					      : "memory");
			} else {
				asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
					      "fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
					      :
					      : "m" (p->thread.fp[0]),
						"m" (p->thread.fpcntl[0])
					      : "memory");
			}
		}

		/* Restore the state in case the fpu was busy */
		asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0]));
	}
#endif /* CONFIG_FPU */

	return 0;
}