Commit 0dc18742 authored by Ian Molton's avatar Ian Molton Committed by Linus Torvalds

[PATCH] arm26: new execve code

This pulls in the execve changes made in arm32.

Also, drops some old arm32 power management stuff thats no longer relevant
in process.c.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3544d7de
...@@ -67,42 +67,22 @@ static int __init hlt_setup(char *__unused) ...@@ -67,42 +67,22 @@ static int __init hlt_setup(char *__unused)
__setup("nohlt", nohlt_setup); __setup("nohlt", nohlt_setup);
__setup("hlt", hlt_setup); __setup("hlt", hlt_setup);
/*
* The following aren't currently used.
*/
void (*pm_idle)(void);
void (*pm_power_off)(void);
/* /*
* This is our default idle handler. We need to disable * This is our default idle handler. We need to disable
* interrupts here to ensure we don't miss a wakeup call. * interrupts here to ensure we don't miss a wakeup call.
*/ */
void default_idle(void)
{
local_irq_disable();
if (!need_resched() && !hlt_counter)
local_irq_enable();
}
/*
* The idle thread. We try to conserve power, while trying to keep
* overall latency low. The architecture specific idle is passed
* a value to indicate the level of "idleness" of the system.
*/
void cpu_idle(void) void cpu_idle(void)
{ {
/* endless idle loop with no priority at all */ /* endless idle loop with no priority at all */
preempt_disable(); preempt_disable();
while (1) { while (1) {
void (*idle)(void) = pm_idle; while (!need_resched()) {
if (!idle) local_irq_disable();
idle = default_idle; if (!need_resched() && !hlt_counter)
leds_event(led_idle_start); local_irq_enable();
while (!need_resched()) }
idle();
leds_event(led_idle_end);
schedule();
} }
schedule();
} }
static char reboot_mode = 'h'; static char reboot_mode = 'h';
...@@ -115,20 +95,15 @@ int __init reboot_setup(char *str) ...@@ -115,20 +95,15 @@ int __init reboot_setup(char *str)
__setup("reboot=", reboot_setup); __setup("reboot=", reboot_setup);
/* ARM26 cant do these but we still need to define them. */
void machine_halt(void) void machine_halt(void)
{ {
leds_event(led_halted);
} }
EXPORT_SYMBOL(machine_halt);
void machine_power_off(void) void machine_power_off(void)
{ {
leds_event(led_halted);
if (pm_power_off)
pm_power_off();
} }
EXPORT_SYMBOL(machine_halt);
EXPORT_SYMBOL(machine_power_off); EXPORT_SYMBOL(machine_power_off);
void machine_restart(char * __unused) void machine_restart(char * __unused)
...@@ -306,7 +281,7 @@ void release_thread(struct task_struct *dead_task) ...@@ -306,7 +281,7 @@ void release_thread(struct task_struct *dead_task)
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
int int
copy_thread(int nr, unsigned long clone_flags, unsigned long esp, copy_thread(int nr, unsigned long clone_flags, unsigned long stack_start,
unsigned long unused, struct task_struct *p, struct pt_regs *regs) unsigned long unused, struct task_struct *p, struct pt_regs *regs)
{ {
struct thread_info *thread = p->thread_info; struct thread_info *thread = p->thread_info;
...@@ -315,7 +290,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long esp, ...@@ -315,7 +290,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
childregs = __get_user_regs(thread); childregs = __get_user_regs(thread);
*childregs = *regs; *childregs = *regs;
childregs->ARM_r0 = 0; childregs->ARM_r0 = 0;
childregs->ARM_sp = esp; childregs->ARM_sp = stack_start;
memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
thread->cpu_context.sp = (unsigned long)childregs; thread->cpu_context.sp = (unsigned long)childregs;
...@@ -367,35 +342,42 @@ void dump_thread(struct pt_regs * regs, struct user * dump) ...@@ -367,35 +342,42 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
} }
/* /*
* This is the mechanism for creating a new kernel thread. * Shuffle the argument into the correct register before calling the
* * thread function. r1 is the thread argument, r2 is the pointer to
* NOTE! Only a kernel-only process(ie the swapper or direct descendants * the thread function, and r3 points to the exit function.
* who haven't done an "execve()") should use this: it will work within * FIXME - make sure this is right - the older code used to zero fp
* a system call from a "real" process, but the process memory space will * and cause the parent to call sys_exit (do_exit in this version)
* not be free'd until both the parent and the child have exited. */
* FIXME - taken from arm32 extern void kernel_thread_helper(void);
asm( ".section .text\n"
" .align\n"
" .type kernel_thread_helper, #function\n"
"kernel_thread_helper:\n"
" mov r0, r1\n"
" mov lr, r3\n"
" mov pc, r2\n"
" .size kernel_thread_helper, . - kernel_thread_helper\n"
" .previous");
/*
* Create a kernel thread.
*/ */
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{ {
register unsigned int r0 asm("r0") = flags | CLONE_VM | CLONE_UNTRACED; struct pt_regs regs;
register unsigned int r1 asm("r1") = 0;
register pid_t __ret asm("r0"); memset(&regs, 0, sizeof(regs));
__asm__ __volatile__( regs.ARM_r1 = (unsigned long)arg;
__syscall(clone)" @ kernel_thread sys_clone \n\ regs.ARM_r2 = (unsigned long)fn;
movs %0, r0 @ if we are the child \n\ regs.ARM_r3 = (unsigned long)do_exit;
bne 1f \n\ regs.ARM_pc = (unsigned long)kernel_thread_helper | MODE_SVC26;
mov fp, #0 @ ensure that fp is zero \n\
mov r0, %4 \n\ return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
mov lr, pc \n\
mov pc, %3 \n\
b sys_exit \n\
1: "
: "=r" (__ret)
: "0" (r0), "r" (r1), "r" (fn), "r" (arg)
: "lr");
return __ret;
} }
EXPORT_SYMBOL(kernel_thread);
unsigned long get_wchan(struct task_struct *p) unsigned long get_wchan(struct task_struct *p)
{ {
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
* have a non-standard calling sequence on the Linux/arm * have a non-standard calling sequence on the Linux/arm
* platform. * platform.
*/ */
#include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -281,3 +282,43 @@ asmlinkage int sys_execve(char *filenamei, char **argv, char **envp, struct pt_r ...@@ -281,3 +282,43 @@ asmlinkage int sys_execve(char *filenamei, char **argv, char **envp, struct pt_r
out: out:
return error; return error;
} }
/* FIXME - see if this is correct for arm26 */
long execve(const char *filename, char **argv, char **envp)
{
struct pt_regs regs;
int ret;
memset(&regs, 0, sizeof(struct pt_regs));
ret = do_execve((char *)filename, (char __user * __user *)argv, (char __user * __user *)envp, &regs);
if (ret < 0)
goto out;
/*
* Save argc to the register structure for userspace.
*/
regs.ARM_r0 = ret;
/*
* We were successful. We won't be returning to our caller, but
* instead to user space by manipulating the kernel stack.
*/
asm( "add r0, %0, %1\n\t"
"mov r1, %2\n\t"
"mov r2, %3\n\t"
"bl memmove\n\t" /* copy regs to top of stack */
"mov r8, #0\n\t" /* not a syscall */
"mov r9, %0\n\t" /* thread structure */
"mov sp, r0\n\t" /* reposition stack pointer */
"b ret_to_user"
:
: "r" (current_thread_info()),
"Ir" (THREAD_SIZE - 8 - sizeof(regs)),
"r" (&regs),
"Ir" (sizeof(regs))
: "r0", "r1", "r2", "r3", "ip", "memory");
out:
return ret;
}
EXPORT_SYMBOL(execve);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment