forked from Minki/linux
Merge branches 'no-rebases', 'arch-avr32', 'arch-blackfin', 'arch-cris', 'arch-h8300', 'arch-m32r', 'arch-mn10300', 'arch-score', 'arch-sh' and 'arch-powerpc' into for-next
This commit is contained in:
parent
d2125043ae
a2b6dfaef0
5fd0b580a9
999121a10c
1703a219f7
71915d21e5
8eae10e86c
ddf23e87a8
1ffbed7220
80b249b71e
ab75819d39
commit
f4091322d7
@ -7,6 +7,8 @@ config ARM64
|
||||
select GENERIC_IOMAP
|
||||
select GENERIC_IRQ_PROBE
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_SMP_IDLE_THREAD
|
||||
select GENERIC_TIME_VSYSCALL
|
||||
select HARDIRQS_SW_RESEND
|
||||
|
@ -128,11 +128,6 @@ unsigned long get_wchan(struct task_struct *p);
|
||||
extern struct task_struct *cpu_switch_to(struct task_struct *prev,
|
||||
struct task_struct *next);
|
||||
|
||||
/*
|
||||
* Create a new kernel thread
|
||||
*/
|
||||
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
|
||||
#define task_pt_regs(p) \
|
||||
((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
|
||||
|
||||
|
@ -23,18 +23,16 @@
|
||||
/*
|
||||
* System call wrappers implemented in kernel/entry.S.
|
||||
*/
|
||||
asmlinkage long sys_execve_wrapper(const char __user *filename,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp);
|
||||
asmlinkage long sys_clone_wrapper(unsigned long clone_flags,
|
||||
unsigned long newsp,
|
||||
void __user *parent_tid,
|
||||
unsigned long tls_val,
|
||||
void __user *child_tid);
|
||||
asmlinkage long sys_rt_sigreturn_wrapper(void);
|
||||
asmlinkage long sys_sigaltstack_wrapper(const stack_t __user *uss,
|
||||
stack_t __user *uoss);
|
||||
|
||||
/*
|
||||
* AArch64 sys_clone implementation has a different prototype than the generic
|
||||
* one (additional TLS value argument).
|
||||
*/
|
||||
#define sys_clone sys_clone
|
||||
|
||||
#include <asm-generic/syscalls.h>
|
||||
|
||||
#endif /* __ASM_SYSCALLS_H */
|
||||
|
@ -25,4 +25,5 @@
|
||||
#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_COMPAT_SYS_SENDFILE
|
||||
#endif
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
#include <uapi/asm/unistd.h>
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
__SYSCALL(0, sys_restart_syscall)
|
||||
__SYSCALL(1, sys_exit)
|
||||
__SYSCALL(2, compat_sys_fork_wrapper)
|
||||
__SYSCALL(2, compat_sys_fork)
|
||||
__SYSCALL(3, sys_read)
|
||||
__SYSCALL(4, sys_write)
|
||||
__SYSCALL(5, compat_sys_open)
|
||||
@ -32,7 +32,7 @@ __SYSCALL(7, sys_ni_syscall) /* 7 was sys_waitpid */
|
||||
__SYSCALL(8, sys_creat)
|
||||
__SYSCALL(9, sys_link)
|
||||
__SYSCALL(10, sys_unlink)
|
||||
__SYSCALL(11, compat_sys_execve_wrapper)
|
||||
__SYSCALL(11, compat_sys_execve)
|
||||
__SYSCALL(12, sys_chdir)
|
||||
__SYSCALL(13, sys_ni_syscall) /* 13 was sys_time */
|
||||
__SYSCALL(14, sys_mknod)
|
||||
@ -141,7 +141,7 @@ __SYSCALL(116, compat_sys_sysinfo)
|
||||
__SYSCALL(117, sys_ni_syscall) /* 117 was sys_ipc */
|
||||
__SYSCALL(118, sys_fsync)
|
||||
__SYSCALL(119, compat_sys_sigreturn_wrapper)
|
||||
__SYSCALL(120, compat_sys_clone_wrapper)
|
||||
__SYSCALL(120, sys_clone)
|
||||
__SYSCALL(121, sys_setdomainname)
|
||||
__SYSCALL(122, sys_newuname)
|
||||
__SYSCALL(123, sys_ni_syscall) /* 123 was sys_modify_ldt */
|
||||
@ -211,7 +211,7 @@ __SYSCALL(186, compat_sys_sigaltstack_wrapper)
|
||||
__SYSCALL(187, compat_sys_sendfile)
|
||||
__SYSCALL(188, sys_ni_syscall) /* 188 reserved */
|
||||
__SYSCALL(189, sys_ni_syscall) /* 189 reserved */
|
||||
__SYSCALL(190, compat_sys_vfork_wrapper)
|
||||
__SYSCALL(190, compat_sys_vfork)
|
||||
__SYSCALL(191, compat_sys_getrlimit) /* SuS compliant getrlimit */
|
||||
__SYSCALL(192, sys_mmap_pgoff)
|
||||
__SYSCALL(193, compat_sys_truncate64_wrapper)
|
||||
|
@ -594,7 +594,7 @@ work_resched:
|
||||
/*
|
||||
* "slow" syscall return path.
|
||||
*/
|
||||
ENTRY(ret_to_user)
|
||||
ret_to_user:
|
||||
disable_irq // disable interrupts
|
||||
ldr x1, [tsk, #TI_FLAGS]
|
||||
and x2, x1, #_TIF_WORK_MASK
|
||||
@ -611,7 +611,10 @@ ENDPROC(ret_to_user)
|
||||
*/
|
||||
ENTRY(ret_from_fork)
|
||||
bl schedule_tail
|
||||
get_thread_info tsk
|
||||
cbz x19, 1f // not a kernel thread
|
||||
mov x0, x20
|
||||
blr x19
|
||||
1: get_thread_info tsk
|
||||
b ret_to_user
|
||||
ENDPROC(ret_from_fork)
|
||||
|
||||
@ -673,16 +676,6 @@ __sys_trace_return:
|
||||
/*
|
||||
* Special system call wrappers.
|
||||
*/
|
||||
ENTRY(sys_execve_wrapper)
|
||||
mov x3, sp
|
||||
b sys_execve
|
||||
ENDPROC(sys_execve_wrapper)
|
||||
|
||||
ENTRY(sys_clone_wrapper)
|
||||
mov x5, sp
|
||||
b sys_clone
|
||||
ENDPROC(sys_clone_wrapper)
|
||||
|
||||
ENTRY(sys_rt_sigreturn_wrapper)
|
||||
mov x0, sp
|
||||
b sys_rt_sigreturn
|
||||
|
@ -240,27 +240,41 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
unsigned long tls = p->thread.tp_value;
|
||||
|
||||
*childregs = *regs;
|
||||
childregs->regs[0] = 0;
|
||||
|
||||
if (is_compat_thread(task_thread_info(p)))
|
||||
childregs->compat_sp = stack_start;
|
||||
else {
|
||||
/*
|
||||
* Read the current TLS pointer from tpidr_el0 as it may be
|
||||
* out-of-sync with the saved value.
|
||||
*/
|
||||
asm("mrs %0, tpidr_el0" : "=r" (tls));
|
||||
childregs->sp = stack_start;
|
||||
}
|
||||
|
||||
memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));
|
||||
p->thread.cpu_context.sp = (unsigned long)childregs;
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
|
||||
/* If a TLS pointer was passed to clone, use that for the new thread. */
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
tls = regs->regs[3];
|
||||
if (likely(regs)) {
|
||||
*childregs = *regs;
|
||||
childregs->regs[0] = 0;
|
||||
if (is_compat_thread(task_thread_info(p))) {
|
||||
if (stack_start)
|
||||
childregs->compat_sp = stack_start;
|
||||
} else {
|
||||
/*
|
||||
* Read the current TLS pointer from tpidr_el0 as it may be
|
||||
* out-of-sync with the saved value.
|
||||
*/
|
||||
asm("mrs %0, tpidr_el0" : "=r" (tls));
|
||||
if (stack_start) {
|
||||
/* 16-byte aligned stack mandatory on AArch64 */
|
||||
if (stack_start & 15)
|
||||
return -EINVAL;
|
||||
childregs->sp = stack_start;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If a TLS pointer was passed to clone (4th argument), use it
|
||||
* for the new thread.
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
tls = regs->regs[3];
|
||||
} else {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
childregs->pstate = PSR_MODE_EL1h;
|
||||
p->thread.cpu_context.x19 = stack_start;
|
||||
p->thread.cpu_context.x20 = stk_sz;
|
||||
}
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
p->thread.cpu_context.sp = (unsigned long)childregs;
|
||||
p->thread.tp_value = tls;
|
||||
|
||||
ptrace_hw_copy_thread(p);
|
||||
@ -309,43 +323,6 @@ struct task_struct *__switch_to(struct task_struct *prev,
|
||||
return last;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shuffle the argument into the correct register before calling the
|
||||
* thread function. x1 is the thread argument, x2 is the pointer to
|
||||
* the thread function, and x3 points to the exit function.
|
||||
*/
|
||||
extern void kernel_thread_helper(void);
|
||||
asm( ".section .text\n"
|
||||
" .align\n"
|
||||
" .type kernel_thread_helper, #function\n"
|
||||
"kernel_thread_helper:\n"
|
||||
" mov x0, x1\n"
|
||||
" mov x30, x3\n"
|
||||
" br x2\n"
|
||||
" .size kernel_thread_helper, . - kernel_thread_helper\n"
|
||||
" .previous");
|
||||
|
||||
#define kernel_thread_exit do_exit
|
||||
|
||||
/*
|
||||
* Create a kernel thread.
|
||||
*/
|
||||
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.regs[1] = (unsigned long)arg;
|
||||
regs.regs[2] = (unsigned long)fn;
|
||||
regs.regs[3] = (unsigned long)kernel_thread_exit;
|
||||
regs.pc = (unsigned long)kernel_thread_helper;
|
||||
regs.pstate = PSR_MODE_EL1h;
|
||||
|
||||
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
struct stackframe frame;
|
||||
|
@ -31,80 +31,12 @@
|
||||
*/
|
||||
asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||
int __user *parent_tidptr, unsigned long tls_val,
|
||||
int __user *child_tidptr, struct pt_regs *regs)
|
||||
int __user *child_tidptr)
|
||||
{
|
||||
if (!newsp)
|
||||
newsp = regs->sp;
|
||||
/* 16-byte aligned stack mandatory on AArch64 */
|
||||
if (newsp & 15)
|
||||
return -EINVAL;
|
||||
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
|
||||
return do_fork(clone_flags, newsp, current_pt_regs(), 0,
|
||||
parent_tidptr, child_tidptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage long sys_execve(const char __user *filenamei,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
long error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(filenamei);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
struct pt_regs regs;
|
||||
int ret;
|
||||
|
||||
memset(®s, 0, sizeof(struct pt_regs));
|
||||
ret = do_execve(filename,
|
||||
(const char __user *const __user *)argv,
|
||||
(const char __user *const __user *)envp, ®s);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Save argc to the register structure for userspace.
|
||||
*/
|
||||
regs.regs[0] = ret;
|
||||
|
||||
/*
|
||||
* We were successful. We won't be returning to our caller, but
|
||||
* instead to user space by manipulating the kernel stack.
|
||||
*/
|
||||
asm( "add x0, %0, %1\n\t"
|
||||
"mov x1, %2\n\t"
|
||||
"mov x2, %3\n\t"
|
||||
"bl memmove\n\t" /* copy regs to top of stack */
|
||||
"mov x27, #0\n\t" /* not a syscall */
|
||||
"mov x28, %0\n\t" /* thread structure */
|
||||
"mov sp, x0\n\t" /* reposition stack pointer */
|
||||
"b ret_to_user"
|
||||
:
|
||||
: "r" (current_thread_info()),
|
||||
"Ir" (THREAD_START_SP - sizeof(regs)),
|
||||
"r" (®s),
|
||||
"Ir" (sizeof(regs))
|
||||
: "x0", "x1", "x2", "x27", "x28", "x30", "memory");
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_execve);
|
||||
|
||||
asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, off_t off)
|
||||
@ -118,8 +50,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
|
||||
/*
|
||||
* Wrappers to pass the pt_regs argument.
|
||||
*/
|
||||
#define sys_execve sys_execve_wrapper
|
||||
#define sys_clone sys_clone_wrapper
|
||||
#define sys_rt_sigreturn sys_rt_sigreturn_wrapper
|
||||
#define sys_sigaltstack sys_sigaltstack_wrapper
|
||||
|
||||
|
@ -26,25 +26,6 @@
|
||||
/*
|
||||
* System call wrappers for the AArch32 compatibility layer.
|
||||
*/
|
||||
compat_sys_fork_wrapper:
|
||||
mov x0, sp
|
||||
b compat_sys_fork
|
||||
ENDPROC(compat_sys_fork_wrapper)
|
||||
|
||||
compat_sys_vfork_wrapper:
|
||||
mov x0, sp
|
||||
b compat_sys_vfork
|
||||
ENDPROC(compat_sys_vfork_wrapper)
|
||||
|
||||
compat_sys_execve_wrapper:
|
||||
mov x3, sp
|
||||
b compat_sys_execve
|
||||
ENDPROC(compat_sys_execve_wrapper)
|
||||
|
||||
compat_sys_clone_wrapper:
|
||||
mov x5, sp
|
||||
b compat_sys_clone
|
||||
ENDPROC(compat_sys_clone_wrapper)
|
||||
|
||||
compat_sys_sigreturn_wrapper:
|
||||
mov x0, sp
|
||||
|
@ -28,43 +28,15 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/unistd32.h>
|
||||
|
||||
asmlinkage int compat_sys_fork(struct pt_regs *regs)
|
||||
asmlinkage int compat_sys_fork(void)
|
||||
{
|
||||
return do_fork(SIGCHLD, regs->compat_sp, regs, 0, NULL, NULL);
|
||||
return do_fork(SIGCHLD, 0, current_pt_regs(), 0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int compat_sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||
int __user *parent_tidptr, int tls_val,
|
||||
int __user *child_tidptr, struct pt_regs *regs)
|
||||
asmlinkage int compat_sys_vfork(void)
|
||||
{
|
||||
if (!newsp)
|
||||
newsp = regs->compat_sp;
|
||||
|
||||
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
|
||||
}
|
||||
|
||||
asmlinkage int compat_sys_vfork(struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->compat_sp,
|
||||
regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int compat_sys_execve(const char __user *filenamei,
|
||||
compat_uptr_t argv, compat_uptr_t envp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(filenamei);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = compat_do_execve(filename->name, compat_ptr(argv),
|
||||
compat_ptr(envp), regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
|
||||
current_pt_regs(), 0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int compat_sys_sched_rr_get_interval(compat_pid_t pid,
|
||||
|
@ -17,6 +17,8 @@ config AVR32
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
help
|
||||
AVR32 is a high-performance 32-bit RISC microprocessor core,
|
||||
designed for cost-sensitive embedded applications, with particular
|
||||
|
@ -142,9 +142,6 @@ struct task_struct;
|
||||
/* Free all resources held by a thread */
|
||||
extern void release_thread(struct task_struct *);
|
||||
|
||||
/* Create a kernel thread without removing it from tasklists */
|
||||
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
|
||||
/* Return saved PC of a blocked thread */
|
||||
#define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc)
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define __ARCH_WANT_SYS_GETPGRP
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -7,7 +7,7 @@ extra-y := head.o vmlinux.lds
|
||||
obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o
|
||||
obj-y += syscall_table.o syscall-stubs.o irq.o
|
||||
obj-y += setup.o traps.o ocd.o ptrace.o
|
||||
obj-y += signal.o sys_avr32.o process.o time.o
|
||||
obj-y += signal.o process.o time.o
|
||||
obj-y += switch_to.o cpu.o
|
||||
obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
@ -251,13 +251,15 @@ syscall_badsys:
|
||||
.global ret_from_fork
|
||||
ret_from_fork:
|
||||
call schedule_tail
|
||||
mov r12, 0
|
||||
rjmp syscall_return
|
||||
|
||||
/* check for syscall tracing */
|
||||
get_thread_info r0
|
||||
ld.w r1, r0[TI_flags]
|
||||
andl r1, _TIF_ALLWORK_MASK, COH
|
||||
brne syscall_exit_work
|
||||
rjmp syscall_exit_cont
|
||||
.global ret_from_kernel_thread
|
||||
ret_from_kernel_thread:
|
||||
call schedule_tail
|
||||
mov r12, r0
|
||||
mov lr, r2 /* syscall_return */
|
||||
mov pc, r1
|
||||
|
||||
syscall_trace_enter:
|
||||
pushm r8-r12
|
||||
|
@ -68,44 +68,6 @@ void machine_restart(char *cmd)
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* PC is actually discarded when returning from a system call -- the
|
||||
* return address must be stored in LR. This function will make sure
|
||||
* LR points to do_exit before starting the thread.
|
||||
*
|
||||
* Also, when returning from fork(), r12 is 0, so we must copy the
|
||||
* argument as well.
|
||||
*
|
||||
* r0 : The argument to the main thread function
|
||||
* r1 : The address of do_exit
|
||||
* r2 : The address of the main thread function
|
||||
*/
|
||||
asmlinkage extern void kernel_thread_helper(void);
|
||||
__asm__(" .type kernel_thread_helper, @function\n"
|
||||
"kernel_thread_helper:\n"
|
||||
" mov r12, r0\n"
|
||||
" mov lr, r2\n"
|
||||
" mov pc, r1\n"
|
||||
" .size kernel_thread_helper, . - kernel_thread_helper");
|
||||
|
||||
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.r0 = (unsigned long)arg;
|
||||
regs.r1 = (unsigned long)fn;
|
||||
regs.r2 = (unsigned long)do_exit;
|
||||
regs.lr = (unsigned long)kernel_thread_helper;
|
||||
regs.pc = (unsigned long)kernel_thread_helper;
|
||||
regs.sr = MODE_SUPERVISOR;
|
||||
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
|
||||
0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc
|
||||
*/
|
||||
@ -332,26 +294,31 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
|
||||
}
|
||||
|
||||
asmlinkage void ret_from_fork(void);
|
||||
asmlinkage void ret_from_kernel_thread(void);
|
||||
asmlinkage void syscall_return(void);
|
||||
|
||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused,
|
||||
unsigned long arg,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs;
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
|
||||
childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)task_stack_page(p))) - 1;
|
||||
*childregs = *regs;
|
||||
|
||||
if (user_mode(regs))
|
||||
if (unlikely(!regs)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
p->thread.cpu_context.r0 = arg;
|
||||
p->thread.cpu_context.r1 = usp; /* fn */
|
||||
p->thread.cpu_context.r2 = syscall_return;
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_kernel_thread;
|
||||
childregs->sr = MODE_SUPERVISOR;
|
||||
} else {
|
||||
*childregs = *regs;
|
||||
childregs->sp = usp;
|
||||
else
|
||||
childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
|
||||
|
||||
childregs->r12 = 0; /* Set return value for child */
|
||||
childregs->r12 = 0; /* Set return value for child */
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
}
|
||||
|
||||
p->thread.cpu_context.sr = MODE_SUPERVISOR | SR_GM;
|
||||
p->thread.cpu_context.ksp = (unsigned long)childregs;
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DEBUG);
|
||||
if ((clone_flags & CLONE_PTRACE) && test_thread_flag(TIF_DEBUG))
|
||||
@ -382,27 +349,6 @@ asmlinkage int sys_vfork(struct pt_regs *regs)
|
||||
0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int sys_execve(const char __user *ufilename,
|
||||
const char __user *const __user *uargv,
|
||||
const char __user *const __user *uenvp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(ufilename);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
|
||||
error = do_execve(filename->name, uargv, uenvp, regs);
|
||||
putname(filename);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function is supposed to answer the question "who called
|
||||
* schedule()?"
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/unistd.h>
|
||||
|
||||
int kernel_execve(const char *file,
|
||||
const char *const *argv,
|
||||
const char *const *envp)
|
||||
{
|
||||
register long scno asm("r8") = __NR_execve;
|
||||
register long sc1 asm("r12") = (long)file;
|
||||
register long sc2 asm("r11") = (long)argv;
|
||||
register long sc3 asm("r10") = (long)envp;
|
||||
|
||||
asm volatile("scall"
|
||||
: "=r"(sc1)
|
||||
: "r"(scno), "0"(sc1), "r"(sc2), "r"(sc3)
|
||||
: "cc", "memory");
|
||||
return sc1;
|
||||
}
|
@ -50,12 +50,6 @@ __sys_vfork:
|
||||
mov r12, sp
|
||||
rjmp sys_vfork
|
||||
|
||||
.global __sys_execve
|
||||
.type __sys_execve,@function
|
||||
__sys_execve:
|
||||
mov r9, sp
|
||||
rjmp sys_execve
|
||||
|
||||
.global __sys_mmap2
|
||||
.type __sys_mmap2,@function
|
||||
__sys_mmap2:
|
||||
|
@ -24,7 +24,7 @@ sys_call_table:
|
||||
.long sys_creat
|
||||
.long sys_link
|
||||
.long sys_unlink /* 10 */
|
||||
.long __sys_execve
|
||||
.long sys_execve
|
||||
.long sys_chdir
|
||||
.long sys_time
|
||||
.long sys_mknod
|
||||
|
@ -45,6 +45,8 @@ config BLACKFIN
|
||||
select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config GENERIC_CSUM
|
||||
def_bool y
|
||||
|
@ -75,8 +75,6 @@ static inline void release_thread(struct task_struct *dead_task)
|
||||
{
|
||||
}
|
||||
|
||||
extern int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags);
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
|
@ -446,6 +446,7 @@
|
||||
#define __ARCH_WANT_SYS_NICE
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -46,22 +46,16 @@ ENTRY(_ret_from_fork)
|
||||
SP += -12;
|
||||
pseudo_long_call _schedule_tail, p5;
|
||||
SP += 12;
|
||||
r0 = [sp + PT_IPEND];
|
||||
cc = bittst(r0,1);
|
||||
if cc jump .Lin_kernel;
|
||||
p1 = [sp++];
|
||||
r0 = [sp++];
|
||||
cc = p1 == 0;
|
||||
if cc jump .Lfork;
|
||||
sp += -12;
|
||||
call (p1);
|
||||
sp += 12;
|
||||
.Lfork:
|
||||
RESTORE_CONTEXT
|
||||
rti;
|
||||
.Lin_kernel:
|
||||
bitclr(r0,1);
|
||||
[sp + PT_IPEND] = r0;
|
||||
/* do a 'fake' RTI by jumping to [RETI]
|
||||
* to avoid clearing supervisor mode in child
|
||||
*/
|
||||
r0 = [sp + PT_PC];
|
||||
[sp + PT_P0] = r0;
|
||||
|
||||
RESTORE_ALL_SYS
|
||||
jump (p0);
|
||||
ENDPROC(_ret_from_fork)
|
||||
|
||||
ENTRY(_sys_vfork)
|
||||
|
@ -101,40 +101,6 @@ void cpu_idle(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This gets run with P1 containing the
|
||||
* function to call, and R1 containing
|
||||
* the "args". Note P0 is clobbered on the way here.
|
||||
*/
|
||||
void kernel_thread_helper(void);
|
||||
__asm__(".section .text\n"
|
||||
".align 4\n"
|
||||
"_kernel_thread_helper:\n\t"
|
||||
"\tsp += -12;\n\t"
|
||||
"\tr0 = r1;\n\t" "\tcall (p1);\n\t" "\tcall _do_exit;\n" ".previous");
|
||||
|
||||
/*
|
||||
* Create a kernel thread.
|
||||
*/
|
||||
pid_t kernel_thread(int (*fn) (void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.r1 = (unsigned long)arg;
|
||||
regs.p1 = (unsigned long)fn;
|
||||
regs.pc = (unsigned long)kernel_thread_helper;
|
||||
regs.orig_p0 = -1;
|
||||
/* Set bit 2 to tell ret_from_fork we should be returning to kernel
|
||||
mode. */
|
||||
regs.ipend = 0x8002;
|
||||
__asm__ __volatile__("%0 = syscfg;":"=da"(regs.syscfg):);
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL,
|
||||
NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/*
|
||||
* Do necessary setup to start up a newly executed thread.
|
||||
*
|
||||
@ -193,38 +159,31 @@ copy_thread(unsigned long clone_flags,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs;
|
||||
unsigned long *v;
|
||||
|
||||
childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1;
|
||||
*childregs = *regs;
|
||||
childregs->r0 = 0;
|
||||
v = ((unsigned long *)childregs) - 2;
|
||||
if (unlikely(!regs)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
v[0] = usp;
|
||||
v[1] = topstk;
|
||||
childregs->orig_p0 = -1;
|
||||
childregs->ipend = 0x8000;
|
||||
__asm__ __volatile__("%0 = syscfg;":"=da"(childregs->syscfg):);
|
||||
p->thread.usp = 0;
|
||||
} else {
|
||||
*childregs = *regs;
|
||||
childregs->r0 = 0;
|
||||
p->thread.usp = usp;
|
||||
v[0] = v[1] = 0;
|
||||
}
|
||||
|
||||
p->thread.usp = usp;
|
||||
p->thread.ksp = (unsigned long)childregs;
|
||||
p->thread.ksp = (unsigned long)v;
|
||||
p->thread.pc = (unsigned long)ret_from_fork;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage int sys_execve(const char __user *name,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
struct pt_regs *regs = (struct pt_regs *)((&name) + 6);
|
||||
|
||||
filename = getname(name);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
return error;
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
return error;
|
||||
}
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
unsigned long fp, pc;
|
||||
|
@ -530,61 +530,6 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
|
||||
jump .Lsyscall_really_exit;
|
||||
ENDPROC(_trap)
|
||||
|
||||
ENTRY(_kernel_execve)
|
||||
link SIZEOF_PTREGS;
|
||||
p0 = sp;
|
||||
r3 = SIZEOF_PTREGS / 4;
|
||||
r4 = 0(x);
|
||||
.Lclear_regs:
|
||||
[p0++] = r4;
|
||||
r3 += -1;
|
||||
cc = r3 == 0;
|
||||
if !cc jump .Lclear_regs (bp);
|
||||
|
||||
p0 = sp;
|
||||
sp += -16;
|
||||
[sp + 12] = p0;
|
||||
pseudo_long_call _do_execve, p5;
|
||||
SP += 16;
|
||||
cc = r0 == 0;
|
||||
if ! cc jump .Lexecve_failed;
|
||||
/* Success. Copy our temporary pt_regs to the top of the kernel
|
||||
* stack and do a normal exception return.
|
||||
*/
|
||||
r1 = sp;
|
||||
r0 = (-KERNEL_STACK_SIZE) (x);
|
||||
r1 = r1 & r0;
|
||||
p2 = r1;
|
||||
p3 = [p2];
|
||||
r0 = KERNEL_STACK_SIZE - 4 (z);
|
||||
p1 = r0;
|
||||
p1 = p1 + p2;
|
||||
|
||||
p0 = fp;
|
||||
r4 = [p0--];
|
||||
r3 = SIZEOF_PTREGS / 4;
|
||||
.Lcopy_regs:
|
||||
r4 = [p0--];
|
||||
[p1--] = r4;
|
||||
r3 += -1;
|
||||
cc = r3 == 0;
|
||||
if ! cc jump .Lcopy_regs (bp);
|
||||
|
||||
r0 = (KERNEL_STACK_SIZE - SIZEOF_PTREGS) (z);
|
||||
p1 = r0;
|
||||
p1 = p1 + p2;
|
||||
sp = p1;
|
||||
r0 = syscfg;
|
||||
[SP + PT_SYSCFG] = r0;
|
||||
[p3 + (TASK_THREAD + THREAD_KSP)] = sp;
|
||||
|
||||
RESTORE_CONTEXT;
|
||||
rti;
|
||||
.Lexecve_failed:
|
||||
unlink;
|
||||
rts;
|
||||
ENDPROC(_kernel_execve)
|
||||
|
||||
ENTRY(_system_call)
|
||||
/* Store IPEND */
|
||||
p2.l = lo(IPEND);
|
||||
|
@ -18,6 +18,7 @@ config C6X
|
||||
select OF_EARLY_FLATTREE
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
select MODULES_USE_ELF_RELA
|
||||
|
||||
config MMU
|
||||
|
@ -14,7 +14,6 @@
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#define __ARCH_WANT_KERNEL_EXECVE
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
/* Use the standard ABI for syscalls. */
|
||||
|
@ -413,19 +413,9 @@ ENTRY(ret_from_kernel_thread)
|
||||
0:
|
||||
B .S2 B10 /* call fn */
|
||||
LDW .D2T1 *+SP(REGS_A1+8),A4 /* get arg */
|
||||
MVKL .S2 sys_exit,B11
|
||||
MVKH .S2 sys_exit,B11
|
||||
ADDKPC .S2 0f,B3,1
|
||||
0:
|
||||
BNOP .S2 B11,5 /* jump to sys_exit */
|
||||
ADDKPC .S2 ret_from_fork_2,B3,3
|
||||
ENDPROC(ret_from_kernel_thread)
|
||||
|
||||
ENTRY(ret_from_kernel_execve)
|
||||
GET_THREAD_INFO A12
|
||||
BNOP .S2 syscall_exit,4
|
||||
ADD .D2X A4,-8,SP
|
||||
ENDPROC(ret_from_kernel_execve)
|
||||
|
||||
;;
|
||||
;; These are the interrupt handlers, responsible for calling __do_IRQ()
|
||||
;; int6 is used for syscalls (see _system_call entry)
|
||||
|
@ -49,6 +49,8 @@ config CRIS
|
||||
select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32
|
||||
select GENERIC_CMOS_UPDATE
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config HZ
|
||||
int
|
||||
|
@ -35,6 +35,7 @@
|
||||
.globl system_call
|
||||
.globl ret_from_intr
|
||||
.globl ret_from_fork
|
||||
.globl ret_from_kernel_thread
|
||||
.globl resume
|
||||
.globl multiple_interrupt
|
||||
.globl hwbreakpoint
|
||||
@ -81,7 +82,14 @@ ret_from_fork:
|
||||
jsr schedule_tail
|
||||
ba ret_from_sys_call
|
||||
nop
|
||||
|
||||
|
||||
ret_from_kernel_thread:
|
||||
jsr schedule_tail
|
||||
move.d $r2, $r10 ; argument is here
|
||||
jsr $r1 ; call the payload
|
||||
moveq 0, $r9 ; no syscall restarts, TYVM...
|
||||
ba ret_from_sys_call
|
||||
|
||||
ret_from_intr:
|
||||
;; check for resched if preemptive kernel or if we're going back to user-mode
|
||||
;; this test matches the user_regs(regs) macro
|
||||
@ -586,13 +594,6 @@ _ugdb_handle_breakpoint:
|
||||
ba do_sigtrap ; SIGTRAP the offending process.
|
||||
pop $dccr ; Restore dccr in delay slot.
|
||||
|
||||
.global kernel_execve
|
||||
kernel_execve:
|
||||
move.d __NR_execve, $r9
|
||||
break 13
|
||||
ret
|
||||
nop
|
||||
|
||||
.data
|
||||
|
||||
hw_bp_trigs:
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <arch/svinto.h>
|
||||
#include <linux/init.h>
|
||||
#include <arch/system.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#ifdef CONFIG_ETRAX_GPIO
|
||||
void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
|
||||
@ -81,31 +82,6 @@ unsigned long thread_saved_pc(struct task_struct *t)
|
||||
return task_pt_regs(t)->irp;
|
||||
}
|
||||
|
||||
static void kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
|
||||
{
|
||||
fn(arg);
|
||||
do_exit(-1); /* Should never be called, return bad exit value */
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
/* Don't use r10 since that is set to 0 in copy_thread */
|
||||
regs.r11 = (unsigned long)fn;
|
||||
regs.r12 = (unsigned long)arg;
|
||||
regs.irp = (unsigned long)kernel_thread_helper;
|
||||
regs.dccr = 1 << I_DCCR_BITNR;
|
||||
|
||||
/* Ok, create the new process.. */
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* setup the child's kernel stack with a pt_regs and switch_stack on it.
|
||||
* it will be un-nested during _resume and _ret_from_sys_call when the
|
||||
* new thread is scheduled.
|
||||
@ -115,29 +91,35 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
*
|
||||
*/
|
||||
asmlinkage void ret_from_fork(void);
|
||||
asmlinkage void ret_from_kernel_thread(void);
|
||||
|
||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused,
|
||||
unsigned long arg,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs * childregs;
|
||||
struct switch_stack *swstack;
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
struct switch_stack *swstack = ((struct switch_stack *)childregs) - 1;
|
||||
|
||||
/* put the pt_regs structure at the end of the new kernel stack page and fix it up
|
||||
* remember that the task_struct doubles as the kernel stack for the task
|
||||
*/
|
||||
|
||||
childregs = task_pt_regs(p);
|
||||
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(swstack, 0,
|
||||
sizeof(struct switch_stack) + sizeof(struct pt_regs));
|
||||
swstack->r1 = usp;
|
||||
swstack->r2 = arg;
|
||||
childregs->dccr = 1 << I_DCCR_BITNR;
|
||||
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
|
||||
p->thread.ksp = (unsigned long) swstack;
|
||||
p->thread.usp = 0;
|
||||
return 0;
|
||||
}
|
||||
*childregs = *regs; /* struct copy of pt_regs */
|
||||
|
||||
p->set_child_tid = p->clear_child_tid = NULL;
|
||||
|
||||
childregs->r10 = 0; /* child returns 0 after a fork/clone */
|
||||
|
||||
/* put the switch stack right below the pt_regs */
|
||||
|
||||
swstack = ((struct switch_stack *)childregs) - 1;
|
||||
/* put the switch stack right below the pt_regs */
|
||||
|
||||
swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */
|
||||
|
||||
@ -147,7 +129,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
|
||||
/* fix the user-mode stackpointer */
|
||||
|
||||
p->thread.usp = usp;
|
||||
p->thread.usp = usp;
|
||||
|
||||
/* and the kernel-mode one */
|
||||
|
||||
@ -161,68 +143,28 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Be aware of the "magic" 7th argument in the four system-calls below.
|
||||
* They need the latest stackframe, which is put as the 7th argument by
|
||||
* entry.S. The previous arguments are dummies or actually used, but need
|
||||
* to be defined to reach the 7th argument.
|
||||
*
|
||||
* N.B.: Another method to get the stackframe is to use current_regs(). But
|
||||
* it returns the latest stack-frame stacked when going from _user mode_ and
|
||||
* some of these (at least sys_clone) are called from kernel-mode sometimes
|
||||
* (for example during kernel_thread, above) and thus cannot use it. Thus,
|
||||
* to be sure not to get any surprises, we use the method for the other calls
|
||||
* as well.
|
||||
*/
|
||||
|
||||
asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
asmlinkage int sys_fork(void)
|
||||
{
|
||||
return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL);
|
||||
return do_fork(SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* if newusp is 0, we just grab the old usp */
|
||||
/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
|
||||
asmlinkage int sys_clone(unsigned long newusp, unsigned long flags,
|
||||
int* parent_tid, int* child_tid, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
int* parent_tid, int* child_tid)
|
||||
{
|
||||
if (!newusp)
|
||||
newusp = rdusp();
|
||||
return do_fork(flags, newusp, regs, 0, parent_tid, child_tid);
|
||||
return do_fork(flags, newusp, current_pt_regs(), 0, parent_tid, child_tid);
|
||||
}
|
||||
|
||||
/* vfork is a system call in i386 because of register-pressure - maybe
|
||||
* we can remove it and handle it in libc but we put it here until then.
|
||||
*/
|
||||
|
||||
asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
asmlinkage int sys_vfork(void)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage int sys_execve(const char *fname,
|
||||
const char *const *argv,
|
||||
const char *const *envp,
|
||||
long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(fname);
|
||||
error = PTR_ERR(filename);
|
||||
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
|
||||
}
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
|
@ -31,6 +31,7 @@
|
||||
.globl system_call
|
||||
.globl ret_from_intr
|
||||
.globl ret_from_fork
|
||||
.globl ret_from_kernel_thread
|
||||
.globl resume
|
||||
.globl multiple_interrupt
|
||||
.globl nmi_interrupt
|
||||
@ -84,6 +85,18 @@ ret_from_fork:
|
||||
nop
|
||||
.size ret_from_fork, . - ret_from_fork
|
||||
|
||||
.type ret_from_kernel_thread,@function
|
||||
ret_from_kernel_thread:
|
||||
jsr schedule_tail
|
||||
nop
|
||||
move.d $r2, $r10
|
||||
jsr $r1
|
||||
nop
|
||||
moveq 0, $r9 ; no syscall restarts, TYVM...
|
||||
ba ret_from_sys_call
|
||||
nop
|
||||
.size ret_from_kernel_thread, . - ret_from_kernel_thread
|
||||
|
||||
.type ret_from_intr,@function
|
||||
ret_from_intr:
|
||||
;; Check for resched if preemptive kernel, or if we're going back to
|
||||
@ -531,15 +544,6 @@ _ugdb_handle_exception:
|
||||
ba do_sigtrap ; SIGTRAP the offending process.
|
||||
move.d [$sp+], $r0 ; Restore R0 in delay slot.
|
||||
|
||||
.global kernel_execve
|
||||
.type kernel_execve,@function
|
||||
kernel_execve:
|
||||
move.d __NR_execve, $r9
|
||||
break 13
|
||||
ret
|
||||
nop
|
||||
.size kernel_execve, . - kernel_execve
|
||||
|
||||
.data
|
||||
|
||||
.section .rodata,"a"
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <hwregs/reg_map.h>
|
||||
#include <hwregs/timer_defs.h>
|
||||
#include <hwregs/intr_vect_defs.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
extern void stop_watchdog(void);
|
||||
|
||||
@ -94,31 +95,6 @@ unsigned long thread_saved_pc(struct task_struct *t)
|
||||
return task_pt_regs(t)->erp;
|
||||
}
|
||||
|
||||
static void
|
||||
kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
|
||||
{
|
||||
fn(arg);
|
||||
do_exit(-1); /* Should never be called, return bad exit value. */
|
||||
}
|
||||
|
||||
/* Create a kernel thread. */
|
||||
int
|
||||
kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
/* Don't use r10 since that is set to 0 in copy_thread. */
|
||||
regs.r11 = (unsigned long) fn;
|
||||
regs.r12 = (unsigned long) arg;
|
||||
regs.erp = (unsigned long) kernel_thread_helper;
|
||||
regs.ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
|
||||
|
||||
/* Create the new process. */
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the child's kernel stack with a pt_regs and call switch_stack() on it.
|
||||
* It will be unnested during _resume and _ret_from_sys_call when the new thread
|
||||
@ -129,23 +105,33 @@ kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
*/
|
||||
|
||||
extern asmlinkage void ret_from_fork(void);
|
||||
extern asmlinkage void ret_from_kernel_thread(void);
|
||||
|
||||
int
|
||||
copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused,
|
||||
unsigned long arg,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs;
|
||||
struct switch_stack *swstack;
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
struct switch_stack *swstack = ((struct switch_stack *) childregs) - 1;
|
||||
|
||||
/*
|
||||
* Put the pt_regs structure at the end of the new kernel stack page and
|
||||
* fix it up. Note: the task_struct doubles as the kernel stack for the
|
||||
* task.
|
||||
*/
|
||||
childregs = task_pt_regs(p);
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(swstack, 0,
|
||||
sizeof(struct switch_stack) + sizeof(struct pt_regs));
|
||||
swstack->r1 = usp;
|
||||
swstack->r2 = arg;
|
||||
childregs->ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
|
||||
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
|
||||
p->thread.ksp = (unsigned long) swstack;
|
||||
p->thread.usp = 0;
|
||||
return 0;
|
||||
}
|
||||
*childregs = *regs; /* Struct copy of pt_regs. */
|
||||
p->set_child_tid = p->clear_child_tid = NULL;
|
||||
childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
|
||||
|
||||
/* Set a new TLS ?
|
||||
@ -156,7 +142,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
}
|
||||
|
||||
/* Put the switch stack right below the pt_regs. */
|
||||
swstack = ((struct switch_stack *) childregs) - 1;
|
||||
|
||||
/* Parameter to ret_from_sys_call. 0 is don't restart the syscall. */
|
||||
swstack->r9 = 0;
|
||||
@ -174,35 +159,21 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Be aware of the "magic" 7th argument in the four system-calls below.
|
||||
* They need the latest stackframe, which is put as the 7th argument by
|
||||
* entry.S. The previous arguments are dummies or actually used, but need
|
||||
* to be defined to reach the 7th argument.
|
||||
*
|
||||
* N.B.: Another method to get the stackframe is to use current_regs(). But
|
||||
* it returns the latest stack-frame stacked when going from _user mode_ and
|
||||
* some of these (at least sys_clone) are called from kernel-mode sometimes
|
||||
* (for example during kernel_thread, above) and thus cannot use it. Thus,
|
||||
* to be sure not to get any surprises, we use the method for the other calls
|
||||
* as well.
|
||||
*/
|
||||
asmlinkage int
|
||||
sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
sys_fork(void)
|
||||
{
|
||||
return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL);
|
||||
return do_fork(SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
|
||||
asmlinkage int
|
||||
sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid,
|
||||
unsigned long tls, long srp, struct pt_regs *regs)
|
||||
unsigned long tls)
|
||||
{
|
||||
if (!newusp)
|
||||
newusp = rdusp();
|
||||
|
||||
return do_fork(flags, newusp, regs, 0, parent_tid, child_tid);
|
||||
return do_fork(flags, newusp, current_pt_regs(), 0, parent_tid, child_tid);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -210,32 +181,9 @@ sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child
|
||||
* we can remove it and handle it in libc but we put it here until then.
|
||||
*/
|
||||
asmlinkage int
|
||||
sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
sys_vfork(void)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* sys_execve() executes a new program. */
|
||||
asmlinkage int
|
||||
sys_execve(const char *fname,
|
||||
const char *const *argv,
|
||||
const char *const *envp, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(fname);
|
||||
error = PTR_ERR(filename);
|
||||
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), current_pt_regs(), 0, NULL, NULL);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
|
@ -49,8 +49,6 @@ struct task_struct;
|
||||
#define task_pt_regs(task) user_regs(task_thread_info(task))
|
||||
#define current_regs() task_pt_regs(current)
|
||||
|
||||
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p);
|
||||
|
||||
#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp)
|
||||
|
@ -371,6 +371,7 @@
|
||||
#define __ARCH_WANT_SYS_SIGPROCMASK
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -30,7 +30,6 @@ extern void __negdi2(void);
|
||||
extern void iounmap(volatile void * __iomem);
|
||||
|
||||
/* Platform dependent support */
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
EXPORT_SYMBOL(get_cmos_time);
|
||||
EXPORT_SYMBOL(loops_per_usec);
|
||||
|
||||
|
@ -8,6 +8,8 @@ config H8300
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_CPU_DEVICES
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config SYMBOL_PREFIX
|
||||
string
|
||||
|
@ -107,8 +107,6 @@ static inline void release_thread(struct task_struct *dead_task)
|
||||
{
|
||||
}
|
||||
|
||||
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
|
@ -60,6 +60,8 @@ struct pt_regs {
|
||||
#define user_mode(regs) (!((regs)->ccr & PS_S))
|
||||
#define instruction_pointer(regs) ((regs)->pc)
|
||||
#define profile_pc(regs) instruction_pointer(regs)
|
||||
#define current_pt_regs() ((struct pt_regs *) \
|
||||
(THREAD_SIZE + (unsigned long)current_thread_info()) - 1)
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* _H8300_PTRACE_H */
|
||||
|
@ -356,6 +356,7 @@
|
||||
#define __ARCH_WANT_SYS_SIGPROCMASK
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -158,6 +158,7 @@ INTERRUPTS = 128
|
||||
.globl SYMBOL_NAME(system_call)
|
||||
.globl SYMBOL_NAME(ret_from_exception)
|
||||
.globl SYMBOL_NAME(ret_from_fork)
|
||||
.globl SYMBOL_NAME(ret_from_kernel_thread)
|
||||
.globl SYMBOL_NAME(ret_from_interrupt)
|
||||
.globl SYMBOL_NAME(interrupt_redirect_table)
|
||||
.globl SYMBOL_NAME(sw_ksp),SYMBOL_NAME(sw_usp)
|
||||
@ -330,6 +331,14 @@ SYMBOL_NAME_LABEL(ret_from_fork)
|
||||
jsr @SYMBOL_NAME(schedule_tail)
|
||||
jmp @SYMBOL_NAME(ret_from_exception)
|
||||
|
||||
SYMBOL_NAME_LABEL(ret_from_kernel_thread)
|
||||
mov.l er2,er0
|
||||
jsr @SYMBOL_NAME(schedule_tail)
|
||||
mov.l @(LER4:16,sp),er0
|
||||
mov.l @(LER5:16,sp),er1
|
||||
jsr @er1
|
||||
jmp @SYMBOL_NAME(ret_from_exception)
|
||||
|
||||
SYMBOL_NAME_LABEL(resume)
|
||||
/*
|
||||
* Beware - when entering resume, offset of tss is in d1,
|
||||
|
@ -33,7 +33,6 @@ EXPORT_SYMBOL(strncmp);
|
||||
|
||||
EXPORT_SYMBOL(ip_fast_csum);
|
||||
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
EXPORT_SYMBOL(enable_irq);
|
||||
EXPORT_SYMBOL(disable_irq);
|
||||
|
||||
|
@ -47,6 +47,7 @@ void (*pm_power_off)(void) = NULL;
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
asmlinkage void ret_from_fork(void);
|
||||
asmlinkage void ret_from_kernel_thread(void);
|
||||
|
||||
/*
|
||||
* The idle loop on an H8/300..
|
||||
@ -122,39 +123,6 @@ void show_regs(struct pt_regs * regs)
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
{
|
||||
long retval;
|
||||
long clone_arg;
|
||||
mm_segment_t fs;
|
||||
|
||||
fs = get_fs();
|
||||
set_fs (KERNEL_DS);
|
||||
clone_arg = flags | CLONE_VM;
|
||||
__asm__("mov.l sp,er3\n\t"
|
||||
"sub.l er2,er2\n\t"
|
||||
"mov.l %2,er1\n\t"
|
||||
"mov.l %1,er0\n\t"
|
||||
"trapa #0\n\t"
|
||||
"cmp.l sp,er3\n\t"
|
||||
"beq 1f\n\t"
|
||||
"mov.l %4,er0\n\t"
|
||||
"mov.l %3,er1\n\t"
|
||||
"jsr @er1\n\t"
|
||||
"mov.l %5,er0\n\t"
|
||||
"trapa #0\n"
|
||||
"1:\n\t"
|
||||
"mov.l er0,%0"
|
||||
:"=r"(retval)
|
||||
:"i"(__NR_clone),"g"(clone_arg),"g"(fn),"g"(arg),"i"(__NR_exit)
|
||||
:"er0","er1","er2","er3");
|
||||
set_fs (fs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void flush_thread(void)
|
||||
{
|
||||
}
|
||||
@ -198,6 +166,13 @@ int copy_thread(unsigned long clone_flags,
|
||||
|
||||
childregs = (struct pt_regs *) (THREAD_SIZE + task_stack_page(p)) - 1;
|
||||
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
childregs->retpc = (unsigned long) ret_from_kernel_thread;
|
||||
childregs->er4 = topstk; /* arg */
|
||||
childregs->er5 = usp; /* fn */
|
||||
p->thread.ksp = (unsigned long)childregs;
|
||||
}
|
||||
*childregs = *regs;
|
||||
childregs->retpc = (unsigned long) ret_from_fork;
|
||||
childregs->er0 = 0;
|
||||
@ -208,27 +183,6 @@ int copy_thread(unsigned long clone_flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage int sys_execve(const char *name,
|
||||
const char *const *argv,
|
||||
const char *const *envp,
|
||||
int dummy, ...)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
struct pt_regs *regs = (struct pt_regs *) ((unsigned char *)&dummy-4);
|
||||
|
||||
filename = getname(name);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
return error;
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
return error;
|
||||
}
|
||||
|
||||
unsigned long thread_saved_pc(struct task_struct *tsk)
|
||||
{
|
||||
return ((struct pt_regs *)tsk->thread.esp0)->pc;
|
||||
|
@ -46,29 +46,3 @@ asmlinkage void syscall_print(void *dummy,...)
|
||||
((regs->pc)&0xffffff)-2,regs->orig_er0,regs->er1,regs->er2,regs->er3,regs->er0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do a system call from kernel instead of calling sys_execve so we
|
||||
* end up with proper pt_regs.
|
||||
*/
|
||||
asmlinkage
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
register long res __asm__("er0");
|
||||
register const char *const *_c __asm__("er3") = envp;
|
||||
register const char *const *_b __asm__("er2") = argv;
|
||||
register const char * _a __asm__("er1") = filename;
|
||||
__asm__ __volatile__ ("mov.l %1,er0\n\t"
|
||||
"trapa #0\n\t"
|
||||
: "=r" (res)
|
||||
: "g" (__NR_execve),
|
||||
"g" (_a),
|
||||
"g" (_b),
|
||||
"g" (_c)
|
||||
: "cc", "memory");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -31,6 +31,8 @@ config HEXAGON
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
---help---
|
||||
Qualcomm Hexagon is a processor architecture designed for high
|
||||
performance and low power across a wide variety of applications.
|
||||
|
@ -34,7 +34,6 @@
|
||||
struct task_struct;
|
||||
|
||||
/* this is defined in arch/process.c */
|
||||
extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
||||
extern unsigned long thread_saved_pc(struct task_struct *tsk);
|
||||
|
||||
extern void start_thread(struct pt_regs *, unsigned long, unsigned long);
|
||||
|
@ -32,4 +32,8 @@
|
||||
extern int regs_query_register_offset(const char *name);
|
||||
extern const char *regs_query_register_name(unsigned int offset);
|
||||
|
||||
#define current_pt_regs() \
|
||||
((struct pt_regs *) \
|
||||
((unsigned long)current_thread_info() + THREAD_SIZE) - 1)
|
||||
|
||||
#endif
|
||||
|
@ -27,5 +27,6 @@
|
||||
*/
|
||||
|
||||
#define sys_mmap2 sys_mmap_pgoff
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
#include <asm-generic/unistd.h>
|
||||
|
@ -25,33 +25,6 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Kernel thread creation. The desired kernel function is "wrapped"
|
||||
* in the kernel_thread_helper function, which does cleanup
|
||||
* afterwards.
|
||||
*/
|
||||
static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *))
|
||||
{
|
||||
do_exit(fn(arg));
|
||||
}
|
||||
|
||||
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
/*
|
||||
* Yes, we're exploting illicit knowledge of the ABI here.
|
||||
*/
|
||||
regs.r00 = (unsigned long) arg;
|
||||
regs.r01 = (unsigned long) fn;
|
||||
pt_set_elr(®s, (unsigned long)kernel_thread_helper);
|
||||
pt_set_kmode(®s);
|
||||
|
||||
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/*
|
||||
* Program thread launch. Often defined as a macro in processor.h,
|
||||
* but we're shooting for a small footprint and it's not an inner-loop
|
||||
@ -114,7 +87,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
|
||||
* Copy architecture-specific thread state
|
||||
*/
|
||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused, struct task_struct *p,
|
||||
unsigned long arg, struct task_struct *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct thread_info *ti = task_thread_info(p);
|
||||
@ -125,61 +98,50 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) -
|
||||
sizeof(*childregs));
|
||||
|
||||
memcpy(childregs, regs, sizeof(*childregs));
|
||||
ti->regs = childregs;
|
||||
|
||||
/*
|
||||
* Establish kernel stack pointer and initial PC for new thread
|
||||
* Note that unlike the usual situation, we do not copy the
|
||||
* parent's callee-saved here; those are in pt_regs and whatever
|
||||
* we leave here will be overridden on return to userland.
|
||||
*/
|
||||
ss = (struct hexagon_switch_stack *) ((unsigned long) childregs -
|
||||
sizeof(*ss));
|
||||
ss->lr = (unsigned long)ret_from_fork;
|
||||
p->thread.switch_sp = ss;
|
||||
|
||||
/* If User mode thread, set pt_reg stack pointer as per parameter */
|
||||
if (user_mode(childregs)) {
|
||||
pt_set_rte_sp(childregs, usp);
|
||||
|
||||
/* Child sees zero return value */
|
||||
childregs->r00 = 0;
|
||||
|
||||
/*
|
||||
* The clone syscall has the C signature:
|
||||
* int [r0] clone(int flags [r0],
|
||||
* void *child_frame [r1],
|
||||
* void *parent_tid [r2],
|
||||
* void *child_tid [r3],
|
||||
* void *thread_control_block [r4]);
|
||||
* ugp is used to provide TLS support.
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
childregs->ugp = childregs->r04;
|
||||
|
||||
/*
|
||||
* Parent sees new pid -- not necessary, not even possible at
|
||||
* this point in the fork process
|
||||
* Might also want to set things like ti->addr_limit
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* If kernel thread, resume stack is kernel stack base.
|
||||
* Note that this is pointer arithmetic on pt_regs *
|
||||
*/
|
||||
pt_set_rte_sp(childregs, (unsigned long)(childregs + 1));
|
||||
/*
|
||||
* We need the current thread_info fast path pointer
|
||||
* set up in pt_regs. The register to be used is
|
||||
* parametric for assembler code, but the mechanism
|
||||
* doesn't drop neatly into C. Needs to be fixed.
|
||||
*/
|
||||
childregs->THREADINFO_REG = (unsigned long) ti;
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
/* r24 <- fn, r25 <- arg */
|
||||
ss->r2524 = usp | ((u64)arg << 32);
|
||||
pt_set_kmode(childregs);
|
||||
return 0;
|
||||
}
|
||||
memcpy(childregs, regs, sizeof(*childregs));
|
||||
ss->r2524 = 0;
|
||||
|
||||
pt_set_rte_sp(childregs, usp);
|
||||
|
||||
/* Child sees zero return value */
|
||||
childregs->r00 = 0;
|
||||
|
||||
/*
|
||||
* thread_info pointer is pulled out of task_struct "stack"
|
||||
* field on switch_to.
|
||||
* The clone syscall has the C signature:
|
||||
* int [r0] clone(int flags [r0],
|
||||
* void *child_frame [r1],
|
||||
* void *parent_tid [r2],
|
||||
* void *child_tid [r3],
|
||||
* void *thread_control_block [r4]);
|
||||
* ugp is used to provide TLS support.
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
childregs->ugp = childregs->r04;
|
||||
|
||||
/*
|
||||
* Parent sees new pid -- not necessary, not even possible at
|
||||
* this point in the fork process
|
||||
* Might also want to set things like ti->addr_limit
|
||||
*/
|
||||
p->stack = (void *)ti;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -249,14 +249,14 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
|
||||
*/
|
||||
asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
|
||||
{
|
||||
struct pt_regs *regs = current_thread_info()->regs;
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
|
||||
return do_sigaltstack(uss, uoss, regs->r29);
|
||||
}
|
||||
|
||||
asmlinkage int sys_rt_sigreturn(void)
|
||||
{
|
||||
struct pt_regs *regs = current_thread_info()->regs;
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
struct rt_sigframe __user *frame;
|
||||
sigset_t blocked;
|
||||
|
||||
|
@ -35,55 +35,13 @@
|
||||
* See signal.c for signal-related system call wrappers.
|
||||
*/
|
||||
|
||||
asmlinkage int sys_execve(char __user *ufilename,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp)
|
||||
{
|
||||
struct pt_regs *pregs = current_thread_info()->regs;
|
||||
struct filename *filename;
|
||||
int retval;
|
||||
|
||||
filename = getname(ufilename);
|
||||
retval = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
return retval;
|
||||
|
||||
retval = do_execve(filename->name, argv, envp, pregs);
|
||||
putname(filename);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||
unsigned long parent_tidp, unsigned long child_tidp)
|
||||
{
|
||||
struct pt_regs *pregs = current_thread_info()->regs;
|
||||
struct pt_regs *pregs = current_pt_regs();
|
||||
|
||||
if (!newsp)
|
||||
newsp = pregs->SP;
|
||||
return do_fork(clone_flags, newsp, pregs, 0, (int __user *)parent_tidp,
|
||||
(int __user *)child_tidp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a system call from the kernel, so as to have a proper pt_regs
|
||||
* and recycle the sys_execvpe infrustructure.
|
||||
*/
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[], const char *const envp[])
|
||||
{
|
||||
register unsigned long __a0 asm("r0") = (unsigned long) filename;
|
||||
register unsigned long __a1 asm("r1") = (unsigned long) argv;
|
||||
register unsigned long __a2 asm("r2") = (unsigned long) envp;
|
||||
int retval;
|
||||
|
||||
__asm__ volatile(
|
||||
" R6 = #%4;\n"
|
||||
" trap0(#1);\n"
|
||||
" %0 = R0;\n"
|
||||
: "=r" (retval)
|
||||
: "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve)
|
||||
);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -266,4 +266,8 @@ _K_enter_machcheck:
|
||||
.globl ret_from_fork
|
||||
ret_from_fork:
|
||||
call schedule_tail
|
||||
P0 = cmp.eq(R24, #0);
|
||||
if P0 jump return_from_syscall
|
||||
R0 = R25;
|
||||
callr R24
|
||||
jump return_from_syscall
|
||||
|
@ -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);
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
#if !defined(__ASSEMBLY__) && !defined(ASSEMBLER)
|
||||
|
||||
|
@ -61,14 +61,13 @@ ENTRY(ia64_execve)
|
||||
* Allocate 8 input registers since ptrace() may clobber them
|
||||
*/
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
|
||||
alloc loc1=ar.pfs,8,2,4,0
|
||||
alloc loc1=ar.pfs,8,2,3,0
|
||||
mov loc0=rp
|
||||
.body
|
||||
mov out0=in0 // filename
|
||||
;; // stop bit between alloc and call
|
||||
mov out1=in1 // argv
|
||||
mov out2=in2 // envp
|
||||
add out3=16,sp // regs
|
||||
br.call.sptk.many rp=sys_execve
|
||||
.ret0:
|
||||
cmp4.ge p6,p7=r8,r0
|
||||
@ -76,7 +75,6 @@ ENTRY(ia64_execve)
|
||||
sxt4 r8=r8 // return 64-bit result
|
||||
;;
|
||||
stf.spill [sp]=f0
|
||||
(p6) cmp.ne pKStk,pUStk=r0,r0 // a successful execve() lands us in user-mode...
|
||||
mov rp=loc0
|
||||
(p6) mov ar.pfs=r0 // clear ar.pfs on success
|
||||
(p7) br.ret.sptk.many rp
|
||||
@ -484,19 +482,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)
|
||||
|
||||
GLOBAL_ENTRY(clone)
|
||||
mov r15=__NR_clone // put syscall number in place
|
||||
break __BREAK_SYSCALL
|
||||
br.ret.sptk.many rp
|
||||
END(clone)
|
||||
|
||||
/*
|
||||
* Invoke a system call, but do some tracing before and after the call.
|
||||
* We MUST preserve the current register frame throughout this routine
|
||||
@ -600,6 +585,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)
|
||||
{ /*
|
||||
@ -616,6 +622,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,64 +401,15 @@ copy_thread(unsigned long clone_flags,
|
||||
struct pt_regs *child_ptregs;
|
||||
int retval = 0;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* For SMP idle threads, fork_by_hand() calls do_fork with
|
||||
* NULL regs.
|
||||
*/
|
||||
if (!regs)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
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;
|
||||
rbs_size = stack->ar_bspstore - rbs;
|
||||
|
||||
/* copy the parent's register backing store to the child: */
|
||||
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
|
||||
|
||||
if (likely(user_mode(child_ptregs))) {
|
||||
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.
|
||||
*/
|
||||
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
|
||||
@ -480,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);
|
||||
@ -608,57 +617,6 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
|
||||
return 1; /* f0-f31 are always valid so we always return 1 */
|
||||
}
|
||||
|
||||
long
|
||||
sys_execve (const char __user *filename,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct filename *fname;
|
||||
int error;
|
||||
|
||||
fname = getname(filename);
|
||||
error = PTR_ERR(fname);
|
||||
if (IS_ERR(fname))
|
||||
goto out;
|
||||
error = do_execve(fname->name, argv, envp, regs);
|
||||
putname(fname);
|
||||
out:
|
||||
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.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
|
||||
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().
|
||||
*/
|
||||
|
@ -15,6 +15,8 @@ config M32R
|
||||
select GENERIC_ATOMIC64
|
||||
select ARCH_USES_GETTIMEOFFSET
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config SBUS
|
||||
bool
|
||||
|
@ -118,11 +118,6 @@ struct mm_struct;
|
||||
/* Free all resources held by a thread. */
|
||||
extern void release_thread(struct task_struct *);
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
extern void copy_segments(struct task_struct *p, struct mm_struct * mm);
|
||||
extern void release_segments(struct mm_struct * mm);
|
||||
|
@ -139,6 +139,8 @@ extern void withdraw_debug_trap(struct pt_regs *regs);
|
||||
|
||||
#define task_pt_regs(task) \
|
||||
((struct pt_regs *)(task_stack_page(task) + THREAD_SIZE) - 1)
|
||||
#define current_pt_regs() ((struct pt_regs *) \
|
||||
((unsigned long)current_thread_info() + THREAD_SIZE) - 1)
|
||||
|
||||
#endif /* __KERNEL */
|
||||
|
||||
|
@ -352,6 +352,7 @@
|
||||
#define __ARCH_WANT_SYS_OLDUMOUNT
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
#define __IGNORE_lchown
|
||||
#define __IGNORE_setuid
|
||||
|
@ -125,6 +125,15 @@
|
||||
and \reg, sp
|
||||
.endm
|
||||
|
||||
ENTRY(ret_from_kernel_thread)
|
||||
pop r0
|
||||
bl schedule_tail
|
||||
GET_THREAD_INFO(r8)
|
||||
ld r0, R0(r8)
|
||||
ld r1, R1(r8)
|
||||
jl r1
|
||||
bra syscall_exit
|
||||
|
||||
ENTRY(ret_from_fork)
|
||||
pop r0
|
||||
bl schedule_tail
|
||||
|
@ -164,41 +164,6 @@ void show_regs(struct pt_regs * regs)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 free'd until both the parent and the child have exited.
|
||||
*/
|
||||
static void kernel_thread_helper(void *nouse, int (*fn)(void *), void *arg)
|
||||
{
|
||||
fn(arg);
|
||||
do_exit(-1);
|
||||
}
|
||||
|
||||
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof (regs));
|
||||
regs.r1 = (unsigned long)fn;
|
||||
regs.r2 = (unsigned long)arg;
|
||||
|
||||
regs.bpc = (unsigned long)kernel_thread_helper;
|
||||
|
||||
regs.psw = M32R_PSW_BIE;
|
||||
|
||||
/* Ok, create the new process. */
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
@ -227,29 +192,35 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
|
||||
}
|
||||
|
||||
int copy_thread(unsigned long clone_flags, unsigned long spu,
|
||||
unsigned long unused, struct task_struct *tsk, struct pt_regs *regs)
|
||||
unsigned long arg, struct task_struct *tsk, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs = task_pt_regs(tsk);
|
||||
extern void ret_from_fork(void);
|
||||
extern void ret_from_kernel_thread(void);
|
||||
|
||||
/* Copy registers */
|
||||
*childregs = *regs;
|
||||
|
||||
childregs->spu = spu;
|
||||
childregs->r0 = 0; /* Child gets zero as return value */
|
||||
regs->r0 = tsk->pid;
|
||||
if (unlikely(tsk->flags & PF_KTHREAD)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
childregs->psw = M32R_PSW_BIE;
|
||||
childregs->r1 = spu; /* fn */
|
||||
childregs->r0 = arg;
|
||||
tsk->thread.lr = (unsigned long)ret_from_kernel_thread;
|
||||
} else {
|
||||
/* Copy registers */
|
||||
*childregs = *regs;
|
||||
childregs->spu = spu;
|
||||
childregs->r0 = 0; /* Child gets zero as return value */
|
||||
tsk->thread.lr = (unsigned long)ret_from_fork;
|
||||
}
|
||||
tsk->thread.sp = (unsigned long)childregs;
|
||||
tsk->thread.lr = (unsigned long)ret_from_fork;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2,
|
||||
unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6,
|
||||
struct pt_regs regs)
|
||||
asmlinkage int sys_fork(void)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
return do_fork(SIGCHLD, regs.spu, ®s, 0, NULL, NULL);
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
return do_fork(SIGCHLD, regs->spu, regs, 0, NULL, NULL);
|
||||
#else
|
||||
return -EINVAL;
|
||||
#endif /* CONFIG_MMU */
|
||||
@ -257,14 +228,13 @@ asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2,
|
||||
|
||||
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||
unsigned long parent_tidptr,
|
||||
unsigned long child_tidptr,
|
||||
unsigned long r4, unsigned long r5, unsigned long r6,
|
||||
struct pt_regs regs)
|
||||
unsigned long child_tidptr)
|
||||
{
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
if (!newsp)
|
||||
newsp = regs.spu;
|
||||
newsp = regs->spu;
|
||||
|
||||
return do_fork(clone_flags, newsp, ®s, 0,
|
||||
return do_fork(clone_flags, newsp, regs, 0,
|
||||
(int __user *)parent_tidptr, (int __user *)child_tidptr);
|
||||
}
|
||||
|
||||
@ -278,37 +248,13 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||
* do not have enough call-clobbered registers to hold all
|
||||
* the information you need.
|
||||
*/
|
||||
asmlinkage int sys_vfork(unsigned long r0, unsigned long r1, unsigned long r2,
|
||||
unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6,
|
||||
struct pt_regs regs)
|
||||
asmlinkage int sys_vfork(void)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.spu, ®s, 0,
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->spu, regs, 0,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage int sys_execve(const char __user *ufilename,
|
||||
const char __user *const __user *uargv,
|
||||
const char __user *const __user *uenvp,
|
||||
unsigned long r3, unsigned long r4, unsigned long r5,
|
||||
unsigned long r6, struct pt_regs regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(ufilename);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
|
||||
error = do_execve(filename->name, uargv, uenvp, ®s);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* These bracket the sleeping functions..
|
||||
*/
|
||||
|
@ -88,24 +88,3 @@ asmlinkage int sys_cachectl(char *addr, int nbytes, int op)
|
||||
/* Not implemented yet. */
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a system call from kernel instead of calling sys_execve so we
|
||||
* end up with proper pt_regs.
|
||||
*/
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
register long __scno __asm__ ("r7") = __NR_execve;
|
||||
register long __arg3 __asm__ ("r2") = (long)(envp);
|
||||
register long __arg2 __asm__ ("r1") = (long)(argv);
|
||||
register long __res __asm__ ("r0") = (long)(filename);
|
||||
__asm__ __volatile__ (
|
||||
"trap #" SYSCALL_VECTOR "|| nop"
|
||||
: "=r" (__res)
|
||||
: "r" (__scno), "0" (__res), "r" (__arg2),
|
||||
"r" (__arg3)
|
||||
: "memory");
|
||||
return __res;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ config M68K
|
||||
select ARCH_WANT_IPC_PARSE_VERSION
|
||||
select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_REL
|
||||
select MODULES_USE_ELF_RELA
|
||||
|
@ -32,7 +32,6 @@
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
#define __ARCH_WANT_KERNEL_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -115,16 +115,9 @@ ENTRY(ret_from_kernel_thread)
|
||||
| a3 contains the kernel thread payload, d7 - its argument
|
||||
movel %d1,%sp@-
|
||||
jsr schedule_tail
|
||||
GET_CURRENT(%d0)
|
||||
movel %d7,(%sp)
|
||||
jsr %a3@
|
||||
addql #4,%sp
|
||||
movel %d0,(%sp)
|
||||
jra sys_exit
|
||||
|
||||
ENTRY(ret_from_kernel_execve)
|
||||
movel 4(%sp), %sp
|
||||
GET_CURRENT(%d0)
|
||||
jra ret_from_exception
|
||||
|
||||
#if defined(CONFIG_COLDFIRE) || !defined(CONFIG_MMU)
|
||||
|
@ -26,6 +26,8 @@ config MICROBLAZE
|
||||
select GENERIC_ATOMIC64
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config SWAP
|
||||
def_bool n
|
||||
|
@ -31,6 +31,7 @@ extern const struct seq_operations cpuinfo_op;
|
||||
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp);
|
||||
|
||||
extern void ret_from_fork(void);
|
||||
extern void ret_from_kernel_thread(void);
|
||||
|
||||
# endif /* __ASSEMBLY__ */
|
||||
|
||||
@ -78,11 +79,6 @@ extern unsigned long thread_saved_pc(struct task_struct *t);
|
||||
|
||||
extern unsigned long get_wchan(struct task_struct *p);
|
||||
|
||||
/*
|
||||
* create a kernel thread without removing it from tasklists
|
||||
*/
|
||||
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
|
||||
# define KSTK_EIP(tsk) (0)
|
||||
# define KSTK_ESP(tsk) (0)
|
||||
|
||||
@ -131,8 +127,6 @@ extern inline void release_thread(struct task_struct *dead_task)
|
||||
{
|
||||
}
|
||||
|
||||
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
|
||||
/* Free current thread data structures etc. */
|
||||
static inline void exit_thread(void)
|
||||
{
|
||||
|
@ -422,6 +422,7 @@
|
||||
#define __ARCH_WANT_SYS_SIGPROCMASK
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -474,6 +474,14 @@ ENTRY(ret_from_fork)
|
||||
brid ret_to_user
|
||||
nop
|
||||
|
||||
ENTRY(ret_from_kernel_thread)
|
||||
brlid r15, schedule_tail
|
||||
addk r5, r0, r3
|
||||
brald r15, r20
|
||||
addk r5, r0, r19
|
||||
brid ret_to_user
|
||||
addk r3, r0, r0
|
||||
|
||||
work_pending:
|
||||
enable_irq
|
||||
|
||||
@ -559,10 +567,6 @@ sys_clone:
|
||||
brid microblaze_clone
|
||||
addk r7, r1, r0
|
||||
|
||||
sys_execve:
|
||||
brid microblaze_execve
|
||||
addk r8, r1, r0
|
||||
|
||||
sys_rt_sigreturn_wrapper:
|
||||
brid sys_rt_sigreturn
|
||||
addk r5, r1, r0
|
||||
|
@ -293,24 +293,6 @@ C_ENTRY(_user_exception):
|
||||
swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
|
||||
addi r14, r14, 4 /* return address is 4 byte after call */
|
||||
|
||||
mfs r1, rmsr
|
||||
nop
|
||||
andi r1, r1, MSR_UMS
|
||||
bnei r1, 1f
|
||||
|
||||
/* Kernel-mode state save - kernel execve */
|
||||
lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
|
||||
tophys(r1,r1);
|
||||
|
||||
addik r1, r1, -PT_SIZE; /* Make room on the stack. */
|
||||
SAVE_REGS
|
||||
|
||||
swi r1, r1, PT_MODE; /* pt_regs -> kernel mode */
|
||||
brid 2f;
|
||||
nop; /* Fill delay slot */
|
||||
|
||||
/* User-mode state save. */
|
||||
1:
|
||||
lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
|
||||
tophys(r1,r1);
|
||||
lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */
|
||||
@ -479,11 +461,20 @@ C_ENTRY(sys_fork_wrapper):
|
||||
saved context). */
|
||||
C_ENTRY(ret_from_fork):
|
||||
bralid r15, schedule_tail; /* ...which is schedule_tail's arg */
|
||||
add r3, r5, r0; /* switch_thread returns the prev task */
|
||||
add r5, r3, r0; /* switch_thread returns the prev task */
|
||||
/* ( in the delay slot ) */
|
||||
brid ret_from_trap; /* Do normal trap return */
|
||||
add r3, r0, r0; /* Child's fork call should return 0. */
|
||||
|
||||
C_ENTRY(ret_from_kernel_thread):
|
||||
bralid r15, schedule_tail; /* ...which is schedule_tail's arg */
|
||||
add r5, r3, r0; /* switch_thread returns the prev task */
|
||||
/* ( in the delay slot ) */
|
||||
brald r15, r20 /* fn was left in r20 */
|
||||
addk r5, r0, r19 /* ... and argument - in r19 */
|
||||
brid ret_from_trap
|
||||
add r3, r0, r0
|
||||
|
||||
C_ENTRY(sys_vfork):
|
||||
brid microblaze_vfork /* Do real work (tail-call) */
|
||||
addik r5, r1, 0
|
||||
@ -498,10 +489,6 @@ C_ENTRY(sys_clone):
|
||||
brid do_fork /* Do real work (tail-call) */
|
||||
add r8, r0, r0; /* Arg 3: (unused) */
|
||||
|
||||
C_ENTRY(sys_execve):
|
||||
brid microblaze_execve; /* Do real work (tail-call).*/
|
||||
addik r8, r1, 0; /* add user context as 4th arg */
|
||||
|
||||
C_ENTRY(sys_rt_sigreturn_wrapper):
|
||||
brid sys_rt_sigreturn /* Do real work */
|
||||
addik r5, r1, 0; /* add user context as 1st arg */
|
||||
|
@ -119,46 +119,38 @@ void flush_thread(void)
|
||||
}
|
||||
|
||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused,
|
||||
unsigned long arg,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
struct thread_info *ti = task_thread_info(p);
|
||||
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
/* if we're creating a new kernel thread then just zeroing all
|
||||
* the registers. That's OK for a brand new thread.*/
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
|
||||
ti->cpu_context.r1 = (unsigned long)childregs;
|
||||
ti->cpu_context.r20 = (unsigned long)usp; /* fn */
|
||||
ti->cpu_context.r19 = (unsigned long)arg;
|
||||
childregs->pt_mode = 1;
|
||||
local_save_flags(childregs->msr);
|
||||
#ifdef CONFIG_MMU
|
||||
ti->cpu_context.msr = childregs->msr & ~MSR_IE;
|
||||
#endif
|
||||
ti->cpu_context.r15 = (unsigned long)ret_from_kernel_thread - 8;
|
||||
return 0;
|
||||
}
|
||||
*childregs = *regs;
|
||||
if (user_mode(regs))
|
||||
childregs->r1 = usp;
|
||||
else
|
||||
childregs->r1 = ((unsigned long) ti) + THREAD_SIZE;
|
||||
childregs->r1 = usp;
|
||||
|
||||
#ifndef CONFIG_MMU
|
||||
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
|
||||
ti->cpu_context.r1 = (unsigned long)childregs;
|
||||
#ifndef CONFIG_MMU
|
||||
ti->cpu_context.msr = (unsigned long)childregs->msr;
|
||||
#else
|
||||
childregs->msr |= MSR_UMS;
|
||||
|
||||
/* if creating a kernel thread then update the current reg (we don't
|
||||
* want to use the parent's value when restoring by POP_STATE) */
|
||||
if (kernel_mode(regs))
|
||||
/* save new current on stack to use POP_STATE */
|
||||
childregs->CURRENT_TASK = (unsigned long)p;
|
||||
/* if returning to user then use the parent's value of this register */
|
||||
|
||||
/* if we're creating a new kernel thread then just zeroing all
|
||||
* the registers. That's OK for a brand new thread.*/
|
||||
/* Pls. note that some of them will be restored in POP_STATE */
|
||||
if (kernel_mode(regs))
|
||||
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
|
||||
/* if this thread is created for fork/vfork/clone, then we want to
|
||||
* restore all the parent's context */
|
||||
/* in addition to the registers which will be restored by POP_STATE */
|
||||
else {
|
||||
ti->cpu_context = *(struct cpu_context *)regs;
|
||||
childregs->msr |= MSR_UMS;
|
||||
}
|
||||
|
||||
/* FIXME STATE_SAVE_PT_OFFSET; */
|
||||
ti->cpu_context.r1 = (unsigned long)childregs;
|
||||
/* we should consider the fact that childregs is a copy of the parent
|
||||
* regs which were saved immediately after entering the kernel state
|
||||
* before enabling VM. This MSR will be restored in switch_to and
|
||||
@ -209,29 +201,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void kernel_thread_helper(int (*fn)(void *), void *arg)
|
||||
{
|
||||
fn(arg);
|
||||
do_exit(-1);
|
||||
}
|
||||
|
||||
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
/* store them in non-volatile registers */
|
||||
regs.r5 = (unsigned long)fn;
|
||||
regs.r6 = (unsigned long)arg;
|
||||
local_save_flags(regs.msr);
|
||||
regs.pc = (unsigned long)kernel_thread_helper;
|
||||
regs.pt_mode = 1;
|
||||
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
|
||||
®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kernel_thread);
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
/* TBD (used by procfs) */
|
||||
@ -246,6 +215,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp)
|
||||
regs->pt_mode = 0;
|
||||
#ifdef CONFIG_MMU
|
||||
regs->msr |= MSR_UMS;
|
||||
regs->msr &= ~MSR_VM;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -48,24 +48,6 @@ asmlinkage long microblaze_clone(int flags, unsigned long stack,
|
||||
return do_fork(flags, stack, regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage long microblaze_execve(const char __user *filenamei,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(filenamei);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, off_t pgoff)
|
||||
@ -75,24 +57,3 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
|
||||
|
||||
return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a system call from kernel instead of calling sys_execve so we
|
||||
* end up with proper pt_regs.
|
||||
*/
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
register const char *__a __asm__("r5") = filename;
|
||||
register const void *__b __asm__("r6") = argv;
|
||||
register const void *__c __asm__("r7") = envp;
|
||||
register unsigned long __syscall __asm__("r12") = __NR_execve;
|
||||
register unsigned long __ret __asm__("r3");
|
||||
__asm__ __volatile__ ("brki r14, 0x8"
|
||||
: "=r" (__ret), "=r" (__syscall)
|
||||
: "1" (__syscall), "r" (__a), "r" (__b), "r" (__c)
|
||||
: "r4", "r8", "r9",
|
||||
"r10", "r11", "r14", "cc", "memory");
|
||||
return __ret;
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ config MIPS
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_REL
|
||||
select MODULES_USE_ELF_RELA if 64BIT
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
menu "Machine selection"
|
||||
|
||||
|
@ -310,8 +310,6 @@ struct task_struct;
|
||||
/* Free all resources held by a thread. */
|
||||
#define release_thread(thread) do { } while(0)
|
||||
|
||||
extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
||||
|
||||
extern unsigned long thread_saved_pc(struct task_struct *tsk);
|
||||
|
||||
/*
|
||||
|
@ -61,4 +61,10 @@ static inline void die_if_kernel(const char *str, struct pt_regs *regs)
|
||||
die(str, regs);
|
||||
}
|
||||
|
||||
#define current_pt_regs() \
|
||||
({ \
|
||||
unsigned long sp = (unsigned long)__builtin_frame_address(0); \
|
||||
(struct pt_regs *)((sp | (THREAD_SIZE - 1)) + 1 - 32) - 1; \
|
||||
})
|
||||
|
||||
#endif /* _ASM_PTRACE_H */
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define __ARCH_OMIT_COMPAT_SYS_GETDENTS64
|
||||
#define __ARCH_WANT_OLD_READDIR
|
||||
#define __ARCH_WANT_SYS_ALARM
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
#define __ARCH_WANT_SYS_GETHOSTNAME
|
||||
#define __ARCH_WANT_SYS_IPC
|
||||
#define __ARCH_WANT_SYS_PAUSE
|
||||
|
@ -65,6 +65,12 @@ need_resched:
|
||||
b need_resched
|
||||
#endif
|
||||
|
||||
FEXPORT(ret_from_kernel_thread)
|
||||
jal schedule_tail # a0 = struct task_struct *prev
|
||||
move a0, s1
|
||||
jal s0
|
||||
j syscall_exit
|
||||
|
||||
FEXPORT(ret_from_fork)
|
||||
jal schedule_tail # a0 = struct task_struct *prev
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
*
|
||||
* Copyright (C) 2000 Silicon Graphics, Inc.
|
||||
* Written by Ulf Carlsson (ulfc@engr.sgi.com)
|
||||
* sys32_execve from ia64/ia32 code, Feb 2000, Kanoj Sarcar (kanoj@sgi.com)
|
||||
*/
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/mm.h>
|
||||
@ -77,26 +76,6 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage int sys32_execve(nabi_no_regargs struct pt_regs regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(compat_ptr(regs.regs[4]));
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = compat_do_execve(filename->name, compat_ptr(regs.regs[5]),
|
||||
compat_ptr(regs.regs[6]), ®s);
|
||||
putname(filename);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
#define RLIM_INFINITY32 0x7fffffff
|
||||
#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x)
|
||||
|
||||
|
@ -32,8 +32,6 @@ EXPORT_SYMBOL(memset);
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
EXPORT_SYMBOL(memmove);
|
||||
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/*
|
||||
* Functions that operate on entire pages. Mostly used by memory management.
|
||||
*/
|
||||
|
@ -84,6 +84,7 @@ void __noreturn cpu_idle(void)
|
||||
}
|
||||
|
||||
asmlinkage void ret_from_fork(void);
|
||||
asmlinkage void ret_from_kernel_thread(void);
|
||||
|
||||
void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
|
||||
{
|
||||
@ -113,7 +114,7 @@ void flush_thread(void)
|
||||
}
|
||||
|
||||
int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused, struct task_struct *p, struct pt_regs *regs)
|
||||
unsigned long arg, struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct thread_info *ti = task_thread_info(p);
|
||||
struct pt_regs *childregs;
|
||||
@ -136,19 +137,30 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
childregs = (struct pt_regs *) childksp - 1;
|
||||
/* Put the stack after the struct pt_regs. */
|
||||
childksp = (unsigned long) childregs;
|
||||
p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1);
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
unsigned long status = p->thread.cp0_status;
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
ti->addr_limit = KERNEL_DS;
|
||||
p->thread.reg16 = usp; /* fn */
|
||||
p->thread.reg17 = arg;
|
||||
p->thread.reg29 = childksp;
|
||||
p->thread.reg31 = (unsigned long) ret_from_kernel_thread;
|
||||
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
|
||||
status = (status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) |
|
||||
((status & (ST0_KUC | ST0_IEC)) << 2);
|
||||
#else
|
||||
status |= ST0_EXL;
|
||||
#endif
|
||||
childregs->cp0_status = status;
|
||||
return 0;
|
||||
}
|
||||
*childregs = *regs;
|
||||
childregs->regs[7] = 0; /* Clear error flag */
|
||||
|
||||
childregs->regs[2] = 0; /* Child gets zero as return value */
|
||||
childregs->regs[29] = usp;
|
||||
ti->addr_limit = USER_DS;
|
||||
|
||||
if (childregs->cp0_status & ST0_CU0) {
|
||||
childregs->regs[28] = (unsigned long) ti;
|
||||
childregs->regs[29] = childksp;
|
||||
ti->addr_limit = KERNEL_DS;
|
||||
} else {
|
||||
childregs->regs[29] = usp;
|
||||
ti->addr_limit = USER_DS;
|
||||
}
|
||||
p->thread.reg29 = (unsigned long) childregs;
|
||||
p->thread.reg31 = (unsigned long) ret_from_fork;
|
||||
|
||||
@ -156,7 +168,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
* New tasks lose permission to use the fpu. This accelerates context
|
||||
* switching for most programs since they don't use the fpu.
|
||||
*/
|
||||
p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1);
|
||||
childregs->cp0_status &= ~(ST0_CU2|ST0_CU1);
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
@ -221,35 +232,6 @@ int dump_task_fpu(struct task_struct *t, elf_fpregset_t *fpr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
static void __noreturn kernel_thread_helper(void *arg, int (*fn)(void *))
|
||||
{
|
||||
do_exit(fn(arg));
|
||||
}
|
||||
|
||||
long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.regs[4] = (unsigned long) arg;
|
||||
regs.regs[5] = (unsigned long) fn;
|
||||
regs.cp0_epc = (unsigned long) kernel_thread_helper;
|
||||
regs.cp0_status = read_c0_status();
|
||||
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
|
||||
regs.cp0_status = (regs.cp0_status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) |
|
||||
((regs.cp0_status & (ST0_KUC | ST0_IEC)) << 2);
|
||||
#else
|
||||
regs.cp0_status |= ST0_EXL;
|
||||
#endif
|
||||
|
||||
/* Ok, create the new process.. */
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -167,7 +167,7 @@ EXPORT(sysn32_call_table)
|
||||
PTR sys_getsockopt
|
||||
PTR sys_clone /* 6055 */
|
||||
PTR sys_fork
|
||||
PTR sys32_execve
|
||||
PTR compat_sys_execve
|
||||
PTR sys_exit
|
||||
PTR compat_sys_wait4
|
||||
PTR sys_kill /* 6060 */
|
||||
|
@ -203,7 +203,7 @@ sys_call_table:
|
||||
PTR sys_creat
|
||||
PTR sys_link
|
||||
PTR sys_unlink /* 4010 */
|
||||
PTR sys32_execve
|
||||
PTR compat_sys_execve
|
||||
PTR sys_chdir
|
||||
PTR compat_sys_time
|
||||
PTR sys_mknod
|
||||
|
@ -127,28 +127,6 @@ _sys_clone(nabi_no_regargs struct pt_regs regs)
|
||||
parent_tidptr, child_tidptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname((const char __user *) (long)regs.regs[4]);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = do_execve(filename->name,
|
||||
(const char __user *const __user *) (long)regs.regs[5],
|
||||
(const char __user *const __user *) (long)regs.regs[6],
|
||||
®s);
|
||||
putname(filename);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(set_thread_area, unsigned long, addr)
|
||||
{
|
||||
struct thread_info *ti = task_thread_info(current);
|
||||
@ -313,34 +291,3 @@ asmlinkage void bad_stack(void)
|
||||
{
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a system call from kernel instead of calling sys_execve so we
|
||||
* end up with proper pt_regs.
|
||||
*/
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
register unsigned long __a0 asm("$4") = (unsigned long) filename;
|
||||
register unsigned long __a1 asm("$5") = (unsigned long) argv;
|
||||
register unsigned long __a2 asm("$6") = (unsigned long) envp;
|
||||
register unsigned long __a3 asm("$7");
|
||||
unsigned long __v0;
|
||||
|
||||
__asm__ volatile (" \n"
|
||||
" .set noreorder \n"
|
||||
" li $2, %5 # __NR_execve \n"
|
||||
" syscall \n"
|
||||
" move %0, $2 \n"
|
||||
" .set reorder \n"
|
||||
: "=&r" (__v0), "=r" (__a3)
|
||||
: "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve)
|
||||
: "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24",
|
||||
"memory");
|
||||
|
||||
if (__a3 == 0)
|
||||
return __v0;
|
||||
|
||||
return -__v0;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ config MN10300
|
||||
select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
select MODULES_USE_ELF_RELA
|
||||
|
||||
config AM33_2
|
||||
|
@ -44,7 +44,6 @@
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
#define __ARCH_WANT_KERNEL_EXECVE
|
||||
|
||||
/*
|
||||
* "Conditional" syscalls
|
||||
|
@ -60,13 +60,8 @@ ENTRY(ret_from_kernel_thread)
|
||||
mov (REG_D0,fp),d0
|
||||
mov (REG_A0,fp),a0
|
||||
calls (a0)
|
||||
jmp sys_exit
|
||||
|
||||
ENTRY(ret_from_kernel_execve)
|
||||
add -12,d0 /* pt_regs -> frame */
|
||||
mov d0,sp
|
||||
GET_THREAD_INFO a2
|
||||
clr d0
|
||||
mov d0,(REG_D0,fp)
|
||||
jmp syscall_exit
|
||||
|
||||
###############################################################################
|
||||
|
@ -22,6 +22,8 @@ config OPENRISC
|
||||
select GENERIC_STRNCPY_FROM_USER
|
||||
select GENERIC_STRNLEN_USER
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config MMU
|
||||
def_bool y
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#define sys_mmap2 sys_mmap_pgoff
|
||||
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
#include <asm-generic/unistd.h>
|
||||
|
||||
#define __NR_or1k_atomic __NR_arch_specific_syscall
|
||||
|
@ -894,6 +894,16 @@ ENTRY(ret_from_fork)
|
||||
l.jal schedule_tail
|
||||
l.nop
|
||||
|
||||
/* Check if we are a kernel thread */
|
||||
l.sfeqi r20,0
|
||||
l.bf 1f
|
||||
l.nop
|
||||
|
||||
/* ...we are a kernel thread so invoke the requested callback */
|
||||
l.jalr r20
|
||||
l.or r3,r22,r0
|
||||
|
||||
1:
|
||||
/* _syscall_returns expect r11 to contain return value */
|
||||
l.lwz r11,PT_GPR11(r1)
|
||||
|
||||
@ -915,26 +925,6 @@ ENTRY(ret_from_fork)
|
||||
l.j _syscall_return
|
||||
l.nop
|
||||
|
||||
/* Since syscalls don't save call-clobbered registers, the args to
|
||||
* kernel_thread_helper will need to be passed through callee-saved
|
||||
* registers and copied to the parameter registers when the thread
|
||||
* begins running.
|
||||
*
|
||||
* See arch/openrisc/kernel/process.c:
|
||||
* The args are passed as follows:
|
||||
* arg1 (r3) : passed in r20
|
||||
* arg2 (r4) : passed in r22
|
||||
*/
|
||||
|
||||
ENTRY(_kernel_thread_helper)
|
||||
l.or r3,r20,r0
|
||||
l.or r4,r22,r0
|
||||
l.movhi r31,hi(kernel_thread_helper)
|
||||
l.ori r31,r31,lo(kernel_thread_helper)
|
||||
l.jr r31
|
||||
l.nop
|
||||
|
||||
|
||||
/* ========================================================[ switch ] === */
|
||||
|
||||
/*
|
||||
@ -1044,8 +1034,13 @@ ENTRY(_switch)
|
||||
/* Unwind stack to pre-switch state */
|
||||
l.addi r1,r1,(INT_FRAME_SIZE)
|
||||
|
||||
/* Return via the link-register back to where we 'came from', where that can be
|
||||
* either schedule() or return_from_fork()... */
|
||||
/* Return via the link-register back to where we 'came from', where
|
||||
* that may be either schedule(), ret_from_fork(), or
|
||||
* ret_from_kernel_thread(). If we are returning to a new thread,
|
||||
* we are expected to have set up the arg to schedule_tail already,
|
||||
* hence we do so here unconditionally:
|
||||
*/
|
||||
l.lwz r3,TI_STACK(r3) /* Load 'prev' as schedule_tail arg */
|
||||
l.jr r9
|
||||
l.nop
|
||||
|
||||
@ -1088,10 +1083,6 @@ ENTRY(sys_fork)
|
||||
l.j _fork_save_extra_regs_and_call
|
||||
l.addi r3,r1,0
|
||||
|
||||
ENTRY(sys_execve)
|
||||
l.j _sys_execve
|
||||
l.addi r6,r1,0
|
||||
|
||||
ENTRY(sys_sigaltstack)
|
||||
l.j _sys_sigaltstack
|
||||
l.addi r5,r1,0
|
||||
|
@ -109,66 +109,82 @@ void release_thread(struct task_struct *dead_task)
|
||||
*/
|
||||
extern asmlinkage void ret_from_fork(void);
|
||||
|
||||
/*
|
||||
* copy_thread
|
||||
* @clone_flags: flags
|
||||
* @usp: user stack pointer or fn for kernel thread
|
||||
* @arg: arg to fn for kernel thread; always NULL for userspace thread
|
||||
* @p: the newly created task
|
||||
* @regs: CPU context to copy for userspace thread; always NULL for kthread
|
||||
*
|
||||
* At the top of a newly initialized kernel stack are two stacked pt_reg
|
||||
* structures. The first (topmost) is the userspace context of the thread.
|
||||
* The second is the kernelspace context of the thread.
|
||||
*
|
||||
* A kernel thread will not be returning to userspace, so the topmost pt_regs
|
||||
* struct can be uninitialized; it _does_ need to exist, though, because
|
||||
* a kernel thread can become a userspace thread by doing a kernel_execve, in
|
||||
* which case the topmost context will be initialized and used for 'returning'
|
||||
* to userspace.
|
||||
*
|
||||
* The second pt_reg struct needs to be initialized to 'return' to
|
||||
* ret_from_fork. A kernel thread will need to set r20 to the address of
|
||||
* a function to call into (with arg in r22); userspace threads need to set
|
||||
* r20 to NULL in which case ret_from_fork will just continue a return to
|
||||
* userspace.
|
||||
*
|
||||
* A kernel thread 'fn' may return; this is effectively what happens when
|
||||
* kernel_execve is called. In that case, the userspace pt_regs must have
|
||||
* been initialized (which kernel_execve takes care of, see start_thread
|
||||
* below); ret_from_fork will then continue its execution causing the
|
||||
* 'kernel thread' to return to userspace as a userspace thread.
|
||||
*/
|
||||
|
||||
int
|
||||
copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused, struct task_struct *p, struct pt_regs *regs)
|
||||
unsigned long arg, struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs;
|
||||
struct pt_regs *userregs;
|
||||
struct pt_regs *kregs;
|
||||
unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
|
||||
struct thread_info *ti;
|
||||
unsigned long top_of_kernel_stack;
|
||||
|
||||
top_of_kernel_stack = sp;
|
||||
|
||||
p->set_child_tid = p->clear_child_tid = NULL;
|
||||
|
||||
/* Copy registers */
|
||||
/* redzone */
|
||||
sp -= STACK_FRAME_OVERHEAD;
|
||||
/* Locate userspace context on stack... */
|
||||
sp -= STACK_FRAME_OVERHEAD; /* redzone */
|
||||
sp -= sizeof(struct pt_regs);
|
||||
childregs = (struct pt_regs *)sp;
|
||||
userregs = (struct pt_regs *) sp;
|
||||
|
||||
/* Copy parent registers */
|
||||
*childregs = *regs;
|
||||
|
||||
if ((childregs->sr & SPR_SR_SM) == 1) {
|
||||
/* for kernel thread, set `current_thread_info'
|
||||
* and stackptr in new task
|
||||
*/
|
||||
childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
|
||||
childregs->gpr[10] = (unsigned long)task_thread_info(p);
|
||||
} else {
|
||||
childregs->sp = usp;
|
||||
}
|
||||
|
||||
childregs->gpr[11] = 0; /* Result from fork() */
|
||||
|
||||
/*
|
||||
* The way this works is that at some point in the future
|
||||
* some task will call _switch to switch to the new task.
|
||||
* That will pop off the stack frame created below and start
|
||||
* the new task running at ret_from_fork. The new task will
|
||||
* do some house keeping and then return from the fork or clone
|
||||
* system call, using the stack frame created above.
|
||||
*/
|
||||
/* redzone */
|
||||
sp -= STACK_FRAME_OVERHEAD;
|
||||
/* ...and kernel context */
|
||||
sp -= STACK_FRAME_OVERHEAD; /* redzone */
|
||||
sp -= sizeof(struct pt_regs);
|
||||
kregs = (struct pt_regs *)sp;
|
||||
|
||||
ti = task_thread_info(p);
|
||||
ti->ksp = sp;
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(kregs, 0, sizeof(struct pt_regs));
|
||||
kregs->gpr[20] = usp; /* fn, kernel thread */
|
||||
kregs->gpr[22] = arg;
|
||||
} else {
|
||||
*userregs = *regs;
|
||||
|
||||
/* kregs->sp must store the location of the 'pre-switch' kernel stack
|
||||
* pointer... for a newly forked process, this is simply the top of
|
||||
* the kernel stack.
|
||||
userregs->sp = usp;
|
||||
userregs->gpr[11] = 0; /* Result from fork() */
|
||||
|
||||
kregs->gpr[20] = 0; /* Userspace thread */
|
||||
}
|
||||
|
||||
/*
|
||||
* _switch wants the kernel stack page in pt_regs->sp so that it
|
||||
* can restore it to thread_info->ksp... see _switch for details.
|
||||
*/
|
||||
kregs->sp = top_of_kernel_stack;
|
||||
kregs->gpr[3] = (unsigned long)current; /* arg to schedule_tail */
|
||||
kregs->gpr[10] = (unsigned long)task_thread_info(p);
|
||||
kregs->gpr[9] = (unsigned long)ret_from_fork;
|
||||
|
||||
task_thread_info(p)->ksp = (unsigned long)kregs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -177,16 +193,14 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
*/
|
||||
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
|
||||
{
|
||||
unsigned long sr = regs->sr & ~SPR_SR_SM;
|
||||
unsigned long sr = mfspr(SPR_SR) & ~SPR_SR_SM;
|
||||
|
||||
set_fs(USER_DS);
|
||||
memset(regs->gpr, 0, sizeof(regs->gpr));
|
||||
memset(regs, 0, sizeof(struct pt_regs));
|
||||
|
||||
regs->pc = pc;
|
||||
regs->sr = sr;
|
||||
regs->sp = sp;
|
||||
|
||||
/* printk("start thread, ksp = %lx\n", current_thread_info()->ksp);*/
|
||||
}
|
||||
|
||||
/* Fill in the fpu structure for a core dump. */
|
||||
@ -237,74 +251,9 @@ void dump_elf_thread(elf_greg_t *dest, struct pt_regs* regs)
|
||||
dest[35] = 0;
|
||||
}
|
||||
|
||||
extern void _kernel_thread_helper(void);
|
||||
|
||||
void __noreturn kernel_thread_helper(int (*fn) (void *), void *arg)
|
||||
{
|
||||
do_exit(fn(arg));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a kernel thread.
|
||||
*/
|
||||
int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.gpr[20] = (unsigned long)fn;
|
||||
regs.gpr[22] = (unsigned long)arg;
|
||||
regs.sr = mfspr(SPR_SR);
|
||||
regs.pc = (unsigned long)_kernel_thread_helper;
|
||||
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
|
||||
0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
asmlinkage long _sys_execve(const char __user *name,
|
||||
const char __user * const __user *argv,
|
||||
const char __user * const __user *envp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname(name);
|
||||
error = PTR_ERR(filename);
|
||||
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
|
||||
error = do_execve(filename->name, argv, envp, regs);
|
||||
putname(filename);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
/* TODO */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kernel_execve(const char *filename, char *const argv[], char *const envp[])
|
||||
{
|
||||
register long __res asm("r11") = __NR_execve;
|
||||
register long __a asm("r3") = (long)(filename);
|
||||
register long __b asm("r4") = (long)(argv);
|
||||
register long __c asm("r5") = (long)(envp);
|
||||
__asm__ volatile ("l.sys 1"
|
||||
: "=r" (__res), "=r"(__a), "=r"(__b), "=r"(__c)
|
||||
: "0"(__res), "1"(__a), "2"(__b), "3"(__c)
|
||||
: "r6", "r7", "r8", "r12", "r13", "r15",
|
||||
"r17", "r19", "r21", "r23", "r25", "r27",
|
||||
"r29", "r31");
|
||||
__asm__ volatile ("l.nop");
|
||||
return __res;
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ config PARISC
|
||||
select GENERIC_STRNCPY_FROM_USER
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
help
|
||||
The PA-RISC microprocessor is designed by Hewlett-Packard and used
|
||||
|
@ -163,6 +163,7 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
|
||||
#define __ARCH_WANT_SYS_RT_SIGACTION
|
||||
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
|
||||
#define __ARCH_WANT_SYS_EXECVE
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
|
@ -707,60 +707,10 @@ ENTRY(end_fault_vector)
|
||||
.import handle_interruption,code
|
||||
.import do_cpu_irq_mask,code
|
||||
|
||||
/*
|
||||
* r26 = function to be called
|
||||
* r25 = argument to pass in
|
||||
* r24 = flags for do_fork()
|
||||
*
|
||||
* Kernel threads don't ever return, so they don't need
|
||||
* a true register context. We just save away the arguments
|
||||
* for copy_thread/ret_ to properly set up the child.
|
||||
*/
|
||||
|
||||
#define CLONE_VM 0x100 /* Must agree with <linux/sched.h> */
|
||||
#define CLONE_UNTRACED 0x00800000
|
||||
|
||||
.import do_fork
|
||||
ENTRY(__kernel_thread)
|
||||
STREG %r2, -RP_OFFSET(%r30)
|
||||
|
||||
copy %r30, %r1
|
||||
ldo PT_SZ_ALGN(%r30),%r30
|
||||
#ifdef CONFIG_64BIT
|
||||
/* Yo, function pointers in wide mode are little structs... -PB */
|
||||
ldd 24(%r26), %r2
|
||||
STREG %r2, PT_GR27(%r1) /* Store childs %dp */
|
||||
ldd 16(%r26), %r26
|
||||
|
||||
STREG %r22, PT_GR22(%r1) /* save r22 (arg5) */
|
||||
copy %r0, %r22 /* user_tid */
|
||||
#endif
|
||||
STREG %r26, PT_GR26(%r1) /* Store function & argument for child */
|
||||
STREG %r25, PT_GR25(%r1)
|
||||
ldil L%CLONE_UNTRACED, %r26
|
||||
ldo CLONE_VM(%r26), %r26 /* Force CLONE_VM since only init_mm */
|
||||
or %r26, %r24, %r26 /* will have kernel mappings. */
|
||||
ldi 1, %r25 /* stack_start, signals kernel thread */
|
||||
stw %r0, -52(%r30) /* user_tid */
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
#endif
|
||||
BL do_fork, %r2
|
||||
copy %r1, %r24 /* pt_regs */
|
||||
|
||||
/* Parent Returns here */
|
||||
|
||||
LDREG -PT_SZ_ALGN-RP_OFFSET(%r30), %r2
|
||||
ldo -PT_SZ_ALGN(%r30), %r30
|
||||
bv %r0(%r2)
|
||||
nop
|
||||
ENDPROC(__kernel_thread)
|
||||
|
||||
/*
|
||||
* Child Returns here
|
||||
*
|
||||
* copy_thread moved args from temp save area set up above
|
||||
* into task save area.
|
||||
* copy_thread moved args into task save area.
|
||||
*/
|
||||
|
||||
ENTRY(ret_from_kernel_thread)
|
||||
@ -769,51 +719,17 @@ ENTRY(ret_from_kernel_thread)
|
||||
BL schedule_tail, %r2
|
||||
nop
|
||||
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN(%r30), %r1
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
|
||||
LDREG TASK_PT_GR25(%r1), %r26
|
||||
#ifdef CONFIG_64BIT
|
||||
LDREG TASK_PT_GR27(%r1), %r27
|
||||
LDREG TASK_PT_GR22(%r1), %r22
|
||||
#endif
|
||||
LDREG TASK_PT_GR26(%r1), %r1
|
||||
ble 0(%sr7, %r1)
|
||||
copy %r31, %r2
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
loadgp /* Thread could have been in a module */
|
||||
#endif
|
||||
#ifndef CONFIG_64BIT
|
||||
b sys_exit
|
||||
#else
|
||||
load32 sys_exit, %r1
|
||||
bv %r0(%r1)
|
||||
#endif
|
||||
ldi 0, %r26
|
||||
ENDPROC(ret_from_kernel_thread)
|
||||
|
||||
.import sys_execve, code
|
||||
ENTRY(__execve)
|
||||
copy %r2, %r15
|
||||
copy %r30, %r16
|
||||
ldo PT_SZ_ALGN(%r30), %r30
|
||||
STREG %r26, PT_GR26(%r16)
|
||||
STREG %r25, PT_GR25(%r16)
|
||||
STREG %r24, PT_GR24(%r16)
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
#endif
|
||||
BL sys_execve, %r2
|
||||
copy %r16, %r26
|
||||
|
||||
cmpib,=,n 0,%r28,intr_return /* forward */
|
||||
|
||||
/* yes, this will trap and die. */
|
||||
copy %r15, %r2
|
||||
copy %r16, %r30
|
||||
bv %r0(%r2)
|
||||
b finish_child_return
|
||||
nop
|
||||
ENDPROC(__execve)
|
||||
ENDPROC(ret_from_kernel_thread)
|
||||
|
||||
|
||||
/*
|
||||
@ -1776,49 +1692,27 @@ ENTRY(sys_fork_wrapper)
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
|
||||
ldo TASK_REGS(%r1),%r1
|
||||
reg_save %r1
|
||||
mfctl %cr27, %r3
|
||||
STREG %r3, PT_CR27(%r1)
|
||||
|
||||
STREG %r2,-RP_OFFSET(%r30)
|
||||
ldo FRAME_SIZE(%r30),%r30
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
#endif
|
||||
|
||||
/* These are call-clobbered registers and therefore
|
||||
also syscall-clobbered (we hope). */
|
||||
STREG %r2,PT_GR19(%r1) /* save for child */
|
||||
STREG %r30,PT_GR21(%r1)
|
||||
mfctl %cr27, %r28
|
||||
STREG %r28, PT_CR27(%r1)
|
||||
|
||||
LDREG PT_GR30(%r1),%r25
|
||||
copy %r1,%r24
|
||||
BL sys_clone,%r2
|
||||
b sys_clone
|
||||
ldi SIGCHLD,%r26
|
||||
|
||||
LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
|
||||
wrapper_exit:
|
||||
ldo -FRAME_SIZE(%r30),%r30 /* get the stackframe */
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
|
||||
ldo TASK_REGS(%r1),%r1 /* get pt regs */
|
||||
|
||||
LDREG PT_CR27(%r1), %r3
|
||||
mtctl %r3, %cr27
|
||||
reg_restore %r1
|
||||
|
||||
/* strace expects syscall # to be preserved in r20 */
|
||||
ldi __NR_fork,%r20
|
||||
bv %r0(%r2)
|
||||
STREG %r20,PT_GR20(%r1)
|
||||
ENDPROC(sys_fork_wrapper)
|
||||
|
||||
/* Set the return value for the child */
|
||||
ENTRY(child_return)
|
||||
BL schedule_tail, %r2
|
||||
nop
|
||||
finish_child_return:
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30), %r1
|
||||
ldo TASK_REGS(%r1),%r1 /* get pt regs */
|
||||
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE-FRAME_SIZE(%r30), %r1
|
||||
LDREG TASK_PT_GR19(%r1),%r2
|
||||
b wrapper_exit
|
||||
LDREG PT_CR27(%r1), %r3
|
||||
mtctl %r3, %cr27
|
||||
reg_restore %r1
|
||||
b syscall_exit
|
||||
copy %r0,%r28
|
||||
ENDPROC(child_return)
|
||||
|
||||
@ -1827,23 +1721,10 @@ ENTRY(sys_clone_wrapper)
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
|
||||
ldo TASK_REGS(%r1),%r1 /* get pt regs */
|
||||
reg_save %r1
|
||||
mfctl %cr27, %r3
|
||||
STREG %r3, PT_CR27(%r1)
|
||||
|
||||
STREG %r2,-RP_OFFSET(%r30)
|
||||
ldo FRAME_SIZE(%r30),%r30
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
#endif
|
||||
|
||||
/* WARNING - Clobbers r19 and r21, userspace must save these! */
|
||||
STREG %r2,PT_GR19(%r1) /* save for child */
|
||||
STREG %r30,PT_GR21(%r1)
|
||||
BL sys_clone,%r2
|
||||
mfctl %cr27, %r28
|
||||
STREG %r28, PT_CR27(%r1)
|
||||
b sys_clone
|
||||
copy %r1,%r24
|
||||
|
||||
b wrapper_exit
|
||||
LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
|
||||
ENDPROC(sys_clone_wrapper)
|
||||
|
||||
|
||||
@ -1851,72 +1732,14 @@ ENTRY(sys_vfork_wrapper)
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
|
||||
ldo TASK_REGS(%r1),%r1 /* get pt regs */
|
||||
reg_save %r1
|
||||
mfctl %cr27, %r3
|
||||
STREG %r3, PT_CR27(%r1)
|
||||
mfctl %cr27, %r28
|
||||
STREG %r28, PT_CR27(%r1)
|
||||
|
||||
STREG %r2,-RP_OFFSET(%r30)
|
||||
ldo FRAME_SIZE(%r30),%r30
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
#endif
|
||||
|
||||
STREG %r2,PT_GR19(%r1) /* save for child */
|
||||
STREG %r30,PT_GR21(%r1)
|
||||
|
||||
BL sys_vfork,%r2
|
||||
b sys_vfork
|
||||
copy %r1,%r26
|
||||
|
||||
b wrapper_exit
|
||||
LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2
|
||||
ENDPROC(sys_vfork_wrapper)
|
||||
|
||||
|
||||
.macro execve_wrapper execve
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1
|
||||
ldo TASK_REGS(%r1),%r1 /* get pt regs */
|
||||
|
||||
/*
|
||||
* Do we need to save/restore r3-r18 here?
|
||||
* I don't think so. why would new thread need old
|
||||
* threads registers?
|
||||
*/
|
||||
|
||||
/* %arg0 - %arg3 are already saved for us. */
|
||||
|
||||
STREG %r2,-RP_OFFSET(%r30)
|
||||
ldo FRAME_SIZE(%r30),%r30
|
||||
#ifdef CONFIG_64BIT
|
||||
ldo -16(%r30),%r29 /* Reference param save area */
|
||||
#endif
|
||||
BL \execve,%r2
|
||||
copy %r1,%arg0
|
||||
|
||||
ldo -FRAME_SIZE(%r30),%r30
|
||||
LDREG -RP_OFFSET(%r30),%r2
|
||||
|
||||
/* If exec succeeded we need to load the args */
|
||||
|
||||
ldo -1024(%r0),%r1
|
||||
cmpb,>>= %r28,%r1,error_\execve
|
||||
copy %r2,%r19
|
||||
|
||||
error_\execve:
|
||||
bv %r0(%r19)
|
||||
nop
|
||||
.endm
|
||||
|
||||
.import sys_execve
|
||||
ENTRY(sys_execve_wrapper)
|
||||
execve_wrapper sys_execve
|
||||
ENDPROC(sys_execve_wrapper)
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
.import sys32_execve
|
||||
ENTRY(sys32_execve_wrapper)
|
||||
execve_wrapper sys32_execve
|
||||
ENDPROC(sys32_execve_wrapper)
|
||||
#endif
|
||||
|
||||
ENTRY(sys_rt_sigreturn_wrapper)
|
||||
LDREG TI_TASK-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r26
|
||||
ldo TASK_REGS(%r26),%r26 /* get pt regs */
|
||||
|
@ -52,6 +52,7 @@
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/assembly.h>
|
||||
#include <asm/pdc.h>
|
||||
#include <asm/pdc_chassis.h>
|
||||
#include <asm/pgalloc.h>
|
||||
@ -164,23 +165,6 @@ void machine_power_off(void)
|
||||
void (*pm_power_off)(void) = machine_power_off;
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
/*
|
||||
* Create a kernel thread
|
||||
*/
|
||||
|
||||
extern pid_t __kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
|
||||
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
|
||||
/*
|
||||
* FIXME: Once we are sure we don't need any debug here,
|
||||
* kernel_thread can become a #define.
|
||||
*/
|
||||
|
||||
return __kernel_thread(fn, arg, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
@ -256,8 +240,8 @@ sys_vfork(struct pt_regs *regs)
|
||||
|
||||
int
|
||||
copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused, /* in ia64 this is "user_stack_size" */
|
||||
struct task_struct * p, struct pt_regs * pregs)
|
||||
unsigned long arg,
|
||||
struct task_struct *p, struct pt_regs *pregs)
|
||||
{
|
||||
struct pt_regs * cregs = &(p->thread.regs);
|
||||
void *stack = task_stack_page(p);
|
||||
@ -270,48 +254,32 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
#ifdef CONFIG_HPUX
|
||||
extern void * const hpux_child_return;
|
||||
#endif
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(cregs, 0, sizeof(struct pt_regs));
|
||||
if (!usp) /* idle thread */
|
||||
return 0;
|
||||
|
||||
*cregs = *pregs;
|
||||
|
||||
/* Set the return value for the child. Note that this is not
|
||||
actually restored by the syscall exit path, but we put it
|
||||
here for consistency in case of signals. */
|
||||
cregs->gr[28] = 0; /* child */
|
||||
|
||||
/*
|
||||
* We need to differentiate between a user fork and a
|
||||
* kernel fork. We can't use user_mode, because the
|
||||
* the syscall path doesn't save iaoq. Right now
|
||||
* We rely on the fact that kernel_thread passes
|
||||
* in zero for usp.
|
||||
*/
|
||||
if (usp == 1) {
|
||||
/* kernel thread */
|
||||
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN;
|
||||
/* Must exit via ret_from_kernel_thread in order
|
||||
* to call schedule_tail()
|
||||
*/
|
||||
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
|
||||
cregs->kpc = (unsigned long) &ret_from_kernel_thread;
|
||||
/*
|
||||
* Copy function and argument to be called from
|
||||
* ret_from_kernel_thread.
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
cregs->gr[27] = pregs->gr[27];
|
||||
cregs->gr[27] = ((unsigned long *)usp)[3];
|
||||
cregs->gr[26] = ((unsigned long *)usp)[2];
|
||||
#else
|
||||
cregs->gr[26] = usp;
|
||||
#endif
|
||||
cregs->gr[26] = pregs->gr[26];
|
||||
cregs->gr[25] = pregs->gr[25];
|
||||
cregs->gr[25] = arg;
|
||||
} else {
|
||||
/* user thread */
|
||||
/*
|
||||
* Note that the fork wrappers are responsible
|
||||
* for setting gr[21].
|
||||
*/
|
||||
|
||||
/* Use same stack depth as parent */
|
||||
cregs->ksp = (unsigned long)stack
|
||||
+ (pregs->gr[21] & (THREAD_SIZE - 1));
|
||||
cregs->gr[30] = usp;
|
||||
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
|
||||
if (personality(p->personality) == PER_HPUX) {
|
||||
#ifdef CONFIG_HPUX
|
||||
cregs->kpc = (unsigned long) &hpux_child_return;
|
||||
@ -323,8 +291,7 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
}
|
||||
/* Setup thread TLS area from the 4th parameter in clone */
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
cregs->cr27 = pregs->gr[23];
|
||||
|
||||
cregs->cr27 = pregs->gr[23];
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -335,39 +302,6 @@ unsigned long thread_saved_pc(struct task_struct *t)
|
||||
return t->thread.regs.kpc;
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
|
||||
asmlinkage int sys_execve(struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
filename = getname((const char __user *) regs->gr[26]);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = do_execve(filename->name,
|
||||
(const char __user *const __user *) regs->gr[25],
|
||||
(const char __user *const __user *) regs->gr[24],
|
||||
regs);
|
||||
putname(filename);
|
||||
out:
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
extern int __execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[], struct task_struct *task);
|
||||
int kernel_execve(const char *filename,
|
||||
const char *const argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
return __execve(filename, argv, envp, current);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_wchan(struct task_struct *p)
|
||||
{
|
||||
|
@ -53,28 +53,6 @@
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sys32_execve() executes a new program.
|
||||
*/
|
||||
|
||||
asmlinkage int sys32_execve(struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
struct filename *filename;
|
||||
|
||||
DBG(("sys32_execve(%p) r26 = 0x%lx\n", regs, regs->gr[26]));
|
||||
filename = getname((const char __user *) regs->gr[26]);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = compat_do_execve(filename->name, compat_ptr(regs->gr[25]),
|
||||
compat_ptr(regs->gr[24]), regs);
|
||||
putname(filename);
|
||||
out:
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23,
|
||||
int r22, int r21, int r20)
|
||||
{
|
||||
|
@ -66,7 +66,7 @@
|
||||
ENTRY_SAME(creat)
|
||||
ENTRY_SAME(link)
|
||||
ENTRY_SAME(unlink) /* 10 */
|
||||
ENTRY_DIFF(execve_wrapper)
|
||||
ENTRY_COMP(execve)
|
||||
ENTRY_SAME(chdir)
|
||||
/* See comments in kernel/time.c!!! Maybe we don't need this? */
|
||||
ENTRY_COMP(time)
|
||||
|
@ -144,6 +144,7 @@ config PPC
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select HAVE_MOD_ARCH_SPECIFIC
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_KERNEL_EXECVE
|
||||
|
||||
config EARLY_PRINTK
|
||||
bool
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user