xtensa: switch to generic kernel_thread()
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Chris Zankel <chris@zankel.net>
This commit is contained in:
parent
6ebe7da25b
commit
3306a72669
|
@ -13,6 +13,7 @@ config XTENSA
|
||||||
select GENERIC_CPU_DEVICES
|
select GENERIC_CPU_DEVICES
|
||||||
select MODULES_USE_ELF_RELA
|
select MODULES_USE_ELF_RELA
|
||||||
select GENERIC_PCI_IOMAP
|
select GENERIC_PCI_IOMAP
|
||||||
|
select GENERIC_KERNEL_THREAD
|
||||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||||
help
|
help
|
||||||
Xtensa processors are 32-bit RISC machines designed by Tensilica
|
Xtensa processors are 32-bit RISC machines designed by Tensilica
|
||||||
|
|
|
@ -152,6 +152,7 @@ struct thread_struct {
|
||||||
|
|
||||||
/* Clearing a0 terminates the backtrace. */
|
/* Clearing a0 terminates the backtrace. */
|
||||||
#define start_thread(regs, new_pc, new_sp) \
|
#define start_thread(regs, new_pc, new_sp) \
|
||||||
|
memset(regs, 0, sizeof(*regs)); \
|
||||||
regs->pc = new_pc; \
|
regs->pc = new_pc; \
|
||||||
regs->ps = USER_PS_VALUE; \
|
regs->ps = USER_PS_VALUE; \
|
||||||
regs->areg[1] = new_sp; \
|
regs->areg[1] = new_sp; \
|
||||||
|
@ -168,9 +169,6 @@ struct mm_struct;
|
||||||
/* Free all resources held by a thread. */
|
/* Free all resources held by a thread. */
|
||||||
#define release_thread(thread) do { } while(0)
|
#define release_thread(thread) do { } while(0)
|
||||||
|
|
||||||
/* Create a kernel thread without removing it from tasklists */
|
|
||||||
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
|
||||||
|
|
||||||
/* Copy and release all segment info associated with a VM */
|
/* Copy and release all segment info associated with a VM */
|
||||||
#define copy_segments(p, mm) do { } while(0)
|
#define copy_segments(p, mm) do { } while(0)
|
||||||
#define release_segments(mm) do { } while(0)
|
#define release_segments(mm) do { } while(0)
|
||||||
|
|
|
@ -1832,34 +1832,6 @@ ENTRY(system_call)
|
||||||
retw
|
retw
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a kernel thread
|
|
||||||
*
|
|
||||||
* int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
|
||||||
* a2 a2 a3 a4
|
|
||||||
*/
|
|
||||||
|
|
||||||
ENTRY(kernel_thread)
|
|
||||||
entry a1, 16
|
|
||||||
|
|
||||||
mov a5, a2 # preserve fn over syscall
|
|
||||||
mov a7, a3 # preserve args over syscall
|
|
||||||
|
|
||||||
movi a3, _CLONE_VM | _CLONE_UNTRACED
|
|
||||||
movi a2, __NR_clone
|
|
||||||
or a6, a4, a3 # arg0: flags
|
|
||||||
mov a3, a1 # arg1: sp
|
|
||||||
syscall
|
|
||||||
|
|
||||||
beq a3, a1, 1f # branch if parent
|
|
||||||
mov a6, a7 # args
|
|
||||||
callx4 a5 # fn(args)
|
|
||||||
|
|
||||||
movi a2, __NR_exit
|
|
||||||
syscall # return value of fn(args) still in a6
|
|
||||||
|
|
||||||
1: retw
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do a system call from kernel instead of calling sys_execve, so we end up
|
* Do a system call from kernel instead of calling sys_execve, so we end up
|
||||||
* with proper pt_regs.
|
* with proper pt_regs.
|
||||||
|
@ -1958,3 +1930,16 @@ ENTRY(ret_from_fork)
|
||||||
|
|
||||||
j common_exception_return
|
j common_exception_return
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kernel thread creation helper
|
||||||
|
* On entry, set up by copy_thread: a2 = thread_fn, a3 = thread_fn arg
|
||||||
|
* left from _switch_to: a6 = prev
|
||||||
|
*/
|
||||||
|
ENTRY(ret_from_kernel_thread)
|
||||||
|
|
||||||
|
call4 schedule_tail
|
||||||
|
mov a6, a3
|
||||||
|
callx4 a2
|
||||||
|
call4 do_exit
|
||||||
|
|
||||||
|
ENDPROC(ret_from_kernel_thread)
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include <asm/regs.h>
|
#include <asm/regs.h>
|
||||||
|
|
||||||
extern void ret_from_fork(void);
|
extern void ret_from_fork(void);
|
||||||
|
extern void ret_from_kernel_thread(void);
|
||||||
|
|
||||||
struct task_struct *current_set[NR_CPUS] = {&init_task, };
|
struct task_struct *current_set[NR_CPUS] = {&init_task, };
|
||||||
|
|
||||||
|
@ -158,18 +159,30 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
|
||||||
/*
|
/*
|
||||||
* Copy thread.
|
* Copy thread.
|
||||||
*
|
*
|
||||||
|
* There are two modes in which this function is called:
|
||||||
|
* 1) Userspace thread creation,
|
||||||
|
* regs != NULL, usp_thread_fn is userspace stack pointer.
|
||||||
|
* It is expected to copy parent regs (in case CLONE_VM is not set
|
||||||
|
* in the clone_flags) and set up passed usp in the childregs.
|
||||||
|
* 2) Kernel thread creation,
|
||||||
|
* regs == NULL, usp_thread_fn is the function to run in the new thread
|
||||||
|
* and thread_fn_arg is its parameter.
|
||||||
|
* childregs are not used for the kernel threads.
|
||||||
|
*
|
||||||
* The stack layout for the new thread looks like this:
|
* The stack layout for the new thread looks like this:
|
||||||
*
|
*
|
||||||
* +------------------------+ <- sp in childregs (= tos)
|
* +------------------------+
|
||||||
* | childregs |
|
* | childregs |
|
||||||
* +------------------------+ <- thread.sp = sp in dummy-frame
|
* +------------------------+ <- thread.sp = sp in dummy-frame
|
||||||
* | dummy-frame | (saved in dummy-frame spill-area)
|
* | dummy-frame | (saved in dummy-frame spill-area)
|
||||||
* +------------------------+
|
* +------------------------+
|
||||||
*
|
*
|
||||||
* We create a dummy frame to return to ret_from_fork:
|
* We create a dummy frame to return to either ret_from_fork or
|
||||||
* a0 points to ret_from_fork (simulating a call4)
|
* ret_from_kernel_thread:
|
||||||
|
* a0 points to ret_from_fork/ret_from_kernel_thread (simulating a call4)
|
||||||
* sp points to itself (thread.sp)
|
* sp points to itself (thread.sp)
|
||||||
* a2, a3 are unused.
|
* a2, a3 are unused for userspace threads,
|
||||||
|
* a2 points to thread_fn, a3 holds thread_fn arg for kernel threads.
|
||||||
*
|
*
|
||||||
* Note: This is a pristine frame, so we don't need any spill region on top of
|
* Note: This is a pristine frame, so we don't need any spill region on top of
|
||||||
* childregs.
|
* childregs.
|
||||||
|
@ -185,41 +198,37 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
|
||||||
* involved. Much simpler to just not copy those live frames across.
|
* involved. Much simpler to just not copy those live frames across.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn,
|
||||||
unsigned long unused,
|
unsigned long thread_fn_arg,
|
||||||
struct task_struct * p, struct pt_regs * regs)
|
struct task_struct *p, struct pt_regs *unused)
|
||||||
{
|
{
|
||||||
struct pt_regs *childregs;
|
struct pt_regs *childregs = task_pt_regs(p);
|
||||||
unsigned long tos;
|
|
||||||
int user_mode = user_mode(regs);
|
|
||||||
|
|
||||||
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
|
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
|
||||||
struct thread_info *ti;
|
struct thread_info *ti;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set up new TSS. */
|
|
||||||
tos = (unsigned long)task_stack_page(p) + THREAD_SIZE;
|
|
||||||
if (user_mode)
|
|
||||||
childregs = (struct pt_regs*)(tos - PT_USER_SIZE);
|
|
||||||
else
|
|
||||||
childregs = (struct pt_regs*)tos - 1;
|
|
||||||
|
|
||||||
/* This does not copy all the regs. In a bout of brilliance or madness,
|
|
||||||
ARs beyond a0-a15 exist past the end of the struct. */
|
|
||||||
*childregs = *regs;
|
|
||||||
|
|
||||||
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
|
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
|
||||||
*((int*)childregs - 3) = (unsigned long)childregs;
|
*((int*)childregs - 3) = (unsigned long)childregs;
|
||||||
*((int*)childregs - 4) = 0;
|
*((int*)childregs - 4) = 0;
|
||||||
|
|
||||||
childregs->areg[2] = 0;
|
|
||||||
p->set_child_tid = p->clear_child_tid = NULL;
|
|
||||||
p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
|
|
||||||
p->thread.sp = (unsigned long)childregs;
|
p->thread.sp = (unsigned long)childregs;
|
||||||
|
|
||||||
if (user_mode(regs)) {
|
if (!(p->flags & PF_KTHREAD)) {
|
||||||
|
struct pt_regs *regs = current_pt_regs();
|
||||||
|
unsigned long usp = usp_thread_fn ?
|
||||||
|
usp_thread_fn : regs->areg[1];
|
||||||
|
|
||||||
|
p->thread.ra = MAKE_RA_FOR_CALL(
|
||||||
|
(unsigned long)ret_from_fork, 0x1);
|
||||||
|
|
||||||
|
/* This does not copy all the regs.
|
||||||
|
* In a bout of brilliance or madness,
|
||||||
|
* ARs beyond a0-a15 exist past the end of the struct.
|
||||||
|
*/
|
||||||
|
*childregs = *regs;
|
||||||
childregs->areg[1] = usp;
|
childregs->areg[1] = usp;
|
||||||
|
childregs->areg[2] = 0;
|
||||||
|
|
||||||
/* When sharing memory with the parent thread, the child
|
/* When sharing memory with the parent thread, the child
|
||||||
usually starts on a pristine stack, so we have to reset
|
usually starts on a pristine stack, so we have to reset
|
||||||
|
@ -254,11 +263,19 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||||
// FIXME: we need to set THREADPTR in thread_info...
|
// FIXME: we need to set THREADPTR in thread_info...
|
||||||
if (clone_flags & CLONE_SETTLS)
|
if (clone_flags & CLONE_SETTLS)
|
||||||
childregs->areg[2] = childregs->areg[6];
|
childregs->areg[2] = childregs->areg[6];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* In kernel space, we start a new thread with a new stack. */
|
p->thread.ra = MAKE_RA_FOR_CALL(
|
||||||
childregs->wmask = 1;
|
(unsigned long)ret_from_kernel_thread, 1);
|
||||||
childregs->areg[1] = tos;
|
|
||||||
|
/* pass parameters to ret_from_kernel_thread:
|
||||||
|
* a2 = thread_fn, a3 = thread_fn arg
|
||||||
|
*/
|
||||||
|
*((int *)childregs - 1) = thread_fn_arg;
|
||||||
|
*((int *)childregs - 2) = usp_thread_fn;
|
||||||
|
|
||||||
|
/* Childregs are only used when we're going to userspace
|
||||||
|
* in which case start_thread will set them up.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
|
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
|
||||||
|
@ -354,8 +371,6 @@ long xtensa_clone(unsigned long clone_flags, unsigned long newsp,
|
||||||
void __user *child_tid, long a5,
|
void __user *child_tid, long a5,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (!newsp)
|
|
||||||
newsp = regs->areg[1];
|
|
||||||
return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
|
return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ EXPORT_SYMBOL(__strncpy_user);
|
||||||
EXPORT_SYMBOL(clear_page);
|
EXPORT_SYMBOL(clear_page);
|
||||||
EXPORT_SYMBOL(copy_page);
|
EXPORT_SYMBOL(copy_page);
|
||||||
|
|
||||||
EXPORT_SYMBOL(kernel_thread);
|
|
||||||
EXPORT_SYMBOL(empty_zero_page);
|
EXPORT_SYMBOL(empty_zero_page);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue