mirror of https://gitee.com/openkylin/linux.git
ia64: switch to generic kernel_thread()/kernel_execve()
Acked-by: Tony Luck <tony.luck@intel.com> Tested-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
c19e6d67e4
commit
54d496c391
|
@ -42,6 +42,8 @@ config IA64
|
|||
select GENERIC_TIME_VSYSCALL_OLD
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
default y
|
||||
help
|
||||
The Itanium Processor Family is Intel's 64-bit successor to
|
||||
|
|
|
@ -340,22 +340,6 @@ struct task_struct;
|
|||
*/
|
||||
#define release_thread(dead_task)
|
||||
|
||||
/*
|
||||
* This is the mechanism for creating a new kernel thread.
|
||||
*
|
||||
* NOTE 1: 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 free'd until both the parent and
|
||||
* the child have exited.
|
||||
*
|
||||
* NOTE 2: This MUST NOT be an inlined function. Otherwise, we get
|
||||
* into trouble in init/main.c when the child thread returns to
|
||||
* do_basic_setup() and the timing is such that free_initmem() has
|
||||
* been called already.
|
||||
*/
|
||||
extern pid_t kernel_thread (int (*fn)(void *), void *arg, unsigned long flags);
|
||||
|
||||
/* Get wait channel for task P. */
|
||||
extern unsigned long get_wchan (struct task_struct *p);
|
||||
|
||||
|
|
|
@ -484,13 +484,6 @@ GLOBAL_ENTRY(prefetch_stack)
|
|||
br.ret.sptk.many rp
|
||||
END(prefetch_stack)
|
||||
|
||||
GLOBAL_ENTRY(kernel_execve)
|
||||
rum psr.ac
|
||||
mov r15=__NR_execve // put syscall number in place
|
||||
break __BREAK_SYSCALL
|
||||
br.ret.sptk.many rp
|
||||
END(kernel_execve)
|
||||
|
||||
/*
|
||||
* Invoke a system call, but do some tracing before and after the call.
|
||||
* We MUST preserve the current register frame throughout this routine
|
||||
|
@ -594,6 +587,27 @@ GLOBAL_ENTRY(ia64_strace_leave_kernel)
|
|||
.ret4: br.cond.sptk ia64_leave_kernel
|
||||
END(ia64_strace_leave_kernel)
|
||||
|
||||
ENTRY(call_payload)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(0)
|
||||
/* call the kernel_thread payload; fn is in r4, arg - in r5 */
|
||||
alloc loc1=ar.pfs,0,3,1,0
|
||||
mov loc0=rp
|
||||
mov loc2=gp
|
||||
mov out0=r5 // arg
|
||||
ld8 r14 = [r4], 8 // fn.address
|
||||
;;
|
||||
mov b6 = r14
|
||||
ld8 gp = [r4] // fn.gp
|
||||
;;
|
||||
br.call.sptk.many rp=b6 // fn(arg)
|
||||
.ret12: mov gp=loc2
|
||||
mov rp=loc0
|
||||
mov ar.pfs=loc1
|
||||
/* ... and if it has returned, we are going to userland */
|
||||
cmp.ne pKStk,pUStk=r0,r0
|
||||
br.ret.sptk.many rp
|
||||
END(call_payload)
|
||||
|
||||
GLOBAL_ENTRY(ia64_ret_from_clone)
|
||||
PT_REGS_UNWIND_INFO(0)
|
||||
{ /*
|
||||
|
@ -610,6 +624,7 @@ GLOBAL_ENTRY(ia64_ret_from_clone)
|
|||
br.call.sptk.many rp=ia64_invoke_schedule_tail
|
||||
}
|
||||
.ret8:
|
||||
(pKStk) br.call.sptk.many rp=call_payload
|
||||
adds r2=TI_FLAGS+IA64_TASK_SIZE,r13
|
||||
;;
|
||||
ld4 r2=[r2]
|
||||
|
|
|
@ -1093,19 +1093,6 @@ GLOBAL_ENTRY(cycle_to_cputime)
|
|||
END(cycle_to_cputime)
|
||||
#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
|
||||
|
||||
GLOBAL_ENTRY(start_kernel_thread)
|
||||
.prologue
|
||||
.save rp, r0 // this is the end of the call-chain
|
||||
.body
|
||||
alloc r2 = ar.pfs, 0, 0, 2, 0
|
||||
mov out0 = r9
|
||||
mov out1 = r11;;
|
||||
br.call.sptk.many rp = kernel_thread_helper;;
|
||||
mov out0 = r8
|
||||
br.call.sptk.many rp = sys_exit;;
|
||||
1: br.sptk.few 1b // not reached
|
||||
END(start_kernel_thread)
|
||||
|
||||
#ifdef CONFIG_IA64_BRL_EMU
|
||||
|
||||
/*
|
||||
|
|
|
@ -401,55 +401,15 @@ copy_thread(unsigned long clone_flags,
|
|||
struct pt_regs *child_ptregs;
|
||||
int retval = 0;
|
||||
|
||||
stack = ((struct switch_stack *) regs) - 1;
|
||||
|
||||
child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
|
||||
child_stack = (struct switch_stack *) child_ptregs - 1;
|
||||
|
||||
/* copy parent's switch_stack & pt_regs to child: */
|
||||
memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
|
||||
|
||||
rbs = (unsigned long) current + IA64_RBS_OFFSET;
|
||||
child_rbs = (unsigned long) p + IA64_RBS_OFFSET;
|
||||
|
||||
if (likely(user_mode(child_ptregs))) {
|
||||
/* copy the parent's register backing store to the child: */
|
||||
rbs_size = stack->ar_bspstore - rbs;
|
||||
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */
|
||||
if (user_stack_base) {
|
||||
child_ptregs->r12 = user_stack_base + user_stack_size - 16;
|
||||
child_ptregs->ar_bspstore = user_stack_base;
|
||||
child_ptregs->ar_rnat = 0;
|
||||
child_ptregs->loadrs = 0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Note: we simply preserve the relative position of
|
||||
* the stack pointer here. There is no need to
|
||||
* allocate a scratch area here, since that will have
|
||||
* been taken care of by the caller of sys_clone()
|
||||
* already.
|
||||
*/
|
||||
rbs_size = 0;
|
||||
child_ptregs->r12 = (unsigned long) child_ptregs - 16; /* kernel sp */
|
||||
child_ptregs->r13 = (unsigned long) p; /* set `current' pointer */
|
||||
}
|
||||
child_stack->ar_bspstore = child_rbs + rbs_size;
|
||||
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
|
||||
|
||||
/* copy parts of thread_struct: */
|
||||
p->thread.ksp = (unsigned long) child_stack - 16;
|
||||
|
||||
/* stop some PSR bits from being inherited.
|
||||
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
|
||||
* therefore we must specify them explicitly here and not include them in
|
||||
* IA64_PSR_BITS_TO_CLEAR.
|
||||
*/
|
||||
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
|
||||
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
|
||||
|
||||
/*
|
||||
* NOTE: The calling convention considers all floating point
|
||||
* registers in the high partition (fph) to be scratch. Since
|
||||
|
@ -471,8 +431,66 @@ copy_thread(unsigned long clone_flags,
|
|||
# define THREAD_FLAGS_TO_SET 0
|
||||
p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
|
||||
| THREAD_FLAGS_TO_SET);
|
||||
|
||||
ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */
|
||||
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
if (unlikely(!user_stack_base)) {
|
||||
/* fork_idle() called us */
|
||||
return 0;
|
||||
}
|
||||
memset(child_stack, 0, sizeof(*child_ptregs) + sizeof(*child_stack));
|
||||
child_stack->r4 = user_stack_base; /* payload */
|
||||
child_stack->r5 = user_stack_size; /* argument */
|
||||
/*
|
||||
* Preserve PSR bits, except for bits 32-34 and 37-45,
|
||||
* which we can't read.
|
||||
*/
|
||||
child_ptregs->cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
|
||||
/* mark as valid, empty frame */
|
||||
child_ptregs->cr_ifs = 1UL << 63;
|
||||
child_stack->ar_fpsr = child_ptregs->ar_fpsr
|
||||
= ia64_getreg(_IA64_REG_AR_FPSR);
|
||||
child_stack->pr = (1 << PRED_KERNEL_STACK);
|
||||
child_stack->ar_bspstore = child_rbs;
|
||||
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
|
||||
|
||||
/* stop some PSR bits from being inherited.
|
||||
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
|
||||
* therefore we must specify them explicitly here and not include them in
|
||||
* IA64_PSR_BITS_TO_CLEAR.
|
||||
*/
|
||||
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
|
||||
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
|
||||
|
||||
return 0;
|
||||
}
|
||||
stack = ((struct switch_stack *) regs) - 1;
|
||||
/* copy parent's switch_stack & pt_regs to child: */
|
||||
memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
|
||||
|
||||
/* copy the parent's register backing store to the child: */
|
||||
rbs_size = stack->ar_bspstore - rbs;
|
||||
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */
|
||||
if (user_stack_base) {
|
||||
child_ptregs->r12 = user_stack_base + user_stack_size - 16;
|
||||
child_ptregs->ar_bspstore = user_stack_base;
|
||||
child_ptregs->ar_rnat = 0;
|
||||
child_ptregs->loadrs = 0;
|
||||
}
|
||||
child_stack->ar_bspstore = child_rbs + rbs_size;
|
||||
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
|
||||
|
||||
/* stop some PSR bits from being inherited.
|
||||
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
|
||||
* therefore we must specify them explicitly here and not include them in
|
||||
* IA64_PSR_BITS_TO_CLEAR.
|
||||
*/
|
||||
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
|
||||
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
if (current->thread.pfm_context)
|
||||
pfm_inherit(p, child_ptregs);
|
||||
|
@ -618,37 +636,6 @@ sys_execve (const char __user *filename,
|
|||
return error;
|
||||
}
|
||||
|
||||
pid_t
|
||||
kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
extern void start_kernel_thread (void);
|
||||
unsigned long *helper_fptr = (unsigned long *) &start_kernel_thread;
|
||||
struct {
|
||||
struct switch_stack sw;
|
||||
struct pt_regs pt;
|
||||
} regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
regs.pt.cr_iip = helper_fptr[0]; /* set entry point (IP) */
|
||||
regs.pt.r1 = helper_fptr[1]; /* set GP */
|
||||
regs.pt.r9 = (unsigned long) fn; /* 1st argument */
|
||||
regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
|
||||
/* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
|
||||
regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
|
||||
regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
|
||||
regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
|
||||
regs.sw.pr = (1 << PRED_KERNEL_STACK);
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s.pt, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/* This gets called from kernel_thread() via ia64_invoke_thread_helper(). */
|
||||
int
|
||||
kernel_thread_helper (int (*fn)(void *), void *arg)
|
||||
{
|
||||
return (*fn)(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush thread state. This is called when a thread does an execve().
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue