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:
@@ -42,6 +42,8 @@ config IA64
|
|||||||
select GENERIC_TIME_VSYSCALL_OLD
|
select GENERIC_TIME_VSYSCALL_OLD
|
||||||
select HAVE_MOD_ARCH_SPECIFIC
|
select HAVE_MOD_ARCH_SPECIFIC
|
||||||
select MODULES_USE_ELF_RELA
|
select MODULES_USE_ELF_RELA
|
||||||
|
select GENERIC_KERNEL_THREAD
|
||||||
|
select GENERIC_KERNEL_EXECVE
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
The Itanium Processor Family is Intel's 64-bit successor to
|
The Itanium Processor Family is Intel's 64-bit successor to
|
||||||
|
@@ -340,22 +340,6 @@ struct task_struct;
|
|||||||
*/
|
*/
|
||||||
#define release_thread(dead_task)
|
#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. */
|
/* Get wait channel for task P. */
|
||||||
extern unsigned long get_wchan (struct task_struct *p);
|
extern unsigned long get_wchan (struct task_struct *p);
|
||||||
|
|
||||||
|
@@ -484,13 +484,6 @@ GLOBAL_ENTRY(prefetch_stack)
|
|||||||
br.ret.sptk.many rp
|
br.ret.sptk.many rp
|
||||||
END(prefetch_stack)
|
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.
|
* Invoke a system call, but do some tracing before and after the call.
|
||||||
* We MUST preserve the current register frame throughout this routine
|
* 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
|
.ret4: br.cond.sptk ia64_leave_kernel
|
||||||
END(ia64_strace_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)
|
GLOBAL_ENTRY(ia64_ret_from_clone)
|
||||||
PT_REGS_UNWIND_INFO(0)
|
PT_REGS_UNWIND_INFO(0)
|
||||||
{ /*
|
{ /*
|
||||||
@@ -610,6 +624,7 @@ GLOBAL_ENTRY(ia64_ret_from_clone)
|
|||||||
br.call.sptk.many rp=ia64_invoke_schedule_tail
|
br.call.sptk.many rp=ia64_invoke_schedule_tail
|
||||||
}
|
}
|
||||||
.ret8:
|
.ret8:
|
||||||
|
(pKStk) br.call.sptk.many rp=call_payload
|
||||||
adds r2=TI_FLAGS+IA64_TASK_SIZE,r13
|
adds r2=TI_FLAGS+IA64_TASK_SIZE,r13
|
||||||
;;
|
;;
|
||||||
ld4 r2=[r2]
|
ld4 r2=[r2]
|
||||||
|
@@ -1093,19 +1093,6 @@ GLOBAL_ENTRY(cycle_to_cputime)
|
|||||||
END(cycle_to_cputime)
|
END(cycle_to_cputime)
|
||||||
#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
|
#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
|
#ifdef CONFIG_IA64_BRL_EMU
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -401,55 +401,15 @@ copy_thread(unsigned long clone_flags,
|
|||||||
struct pt_regs *child_ptregs;
|
struct pt_regs *child_ptregs;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
stack = ((struct switch_stack *) regs) - 1;
|
|
||||||
|
|
||||||
child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
|
child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
|
||||||
child_stack = (struct switch_stack *) child_ptregs - 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;
|
rbs = (unsigned long) current + IA64_RBS_OFFSET;
|
||||||
child_rbs = (unsigned long) p + 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: */
|
/* copy parts of thread_struct: */
|
||||||
p->thread.ksp = (unsigned long) child_stack - 16;
|
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
|
* NOTE: The calling convention considers all floating point
|
||||||
* registers in the high partition (fph) to be scratch. Since
|
* 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
|
# define THREAD_FLAGS_TO_SET 0
|
||||||
p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
|
p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
|
||||||
| THREAD_FLAGS_TO_SET);
|
| THREAD_FLAGS_TO_SET);
|
||||||
|
|
||||||
ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */
|
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
|
#ifdef CONFIG_PERFMON
|
||||||
if (current->thread.pfm_context)
|
if (current->thread.pfm_context)
|
||||||
pfm_inherit(p, child_ptregs);
|
pfm_inherit(p, child_ptregs);
|
||||||
@@ -618,37 +636,6 @@ out:
|
|||||||
return error;
|
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().
|
* Flush thread state. This is called when a thread does an execve().
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user