2005-04-16 15:20:36 -07:00
/*
* arch / v850 / kernel / process . c - - Arch - dependent process handling
*
* Copyright ( C ) 2001 , 02 , 03 NEC Electronics Corporation
* Copyright ( C ) 2001 , 02 , 03 Miles Bader < miles @ gnu . org >
*
* This file is subject to the terms and conditions of the GNU General
* Public License . See the file COPYING in the main directory of this
* archive for more details .
*
* Written by Miles Bader < miles @ gnu . org >
*/
# include <linux/config.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/user.h>
# include <linux/a.out.h>
# include <linux/reboot.h>
# include <asm/uaccess.h>
# include <asm/system.h>
# include <asm/pgtable.h>
2006-03-06 15:42:47 -08:00
void ( * pm_power_off ) ( void ) = NULL ;
EXPORT_SYMBOL ( pm_power_off ) ;
2005-04-16 15:20:36 -07:00
extern void ret_from_fork ( void ) ;
/* The idle loop. */
2006-03-24 03:15:57 -08:00
static void default_idle ( void )
2005-04-16 15:20:36 -07:00
{
2005-11-08 21:39:01 -08:00
while ( ! need_resched ( ) )
asm ( " halt; nop; nop; nop; nop; nop " : : : " cc " ) ;
2005-04-16 15:20:36 -07:00
}
void ( * idle ) ( void ) = default_idle ;
/*
* The idle thread . There ' s no useful work to be
* done , so just try to conserve power and have a
* low exit latency ( ie sit in a loop waiting for
* somebody to say that they ' d like to reschedule )
*/
void cpu_idle ( void )
{
/* endless idle loop with no priority at all */
2005-11-08 21:39:01 -08:00
while ( 1 ) {
while ( ! need_resched ( ) )
( * idle ) ( ) ;
preempt_enable_no_resched ( ) ;
schedule ( ) ;
preempt_disable ( ) ;
}
2005-04-16 15:20:36 -07:00
}
/*
* 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 .
*/
int kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{
register mm_segment_t fs = get_fs ( ) ;
register unsigned long syscall asm ( SYSCALL_NUM ) ;
register unsigned long arg0 asm ( SYSCALL_ARG0 ) ;
register unsigned long ret asm ( SYSCALL_RET ) ;
set_fs ( KERNEL_DS ) ;
/* Clone this thread. Note that we don't pass the clone syscall's
second argument - - it ' s ignored for calls from kernel mode ( the
child ' s SP is always set to the top of the kernel stack ) . */
arg0 = flags | CLONE_VM ;
syscall = __NR_clone ;
asm volatile ( " trap " SYSCALL_SHORT_TRAP
: " =r " ( ret ) , " =r " ( syscall )
: " 1 " ( syscall ) , " r " ( arg0 )
: SYSCALL_SHORT_CLOBBERS ) ;
if ( ret = = 0 ) {
/* In child thread, call FN and exit. */
arg0 = ( * fn ) ( arg ) ;
syscall = __NR_exit ;
asm volatile ( " trap " SYSCALL_SHORT_TRAP
: " =r " ( ret ) , " =r " ( syscall )
: " 1 " ( syscall ) , " r " ( arg0 )
: SYSCALL_SHORT_CLOBBERS ) ;
}
/* In parent. */
set_fs ( fs ) ;
return ret ;
}
void flush_thread ( void )
{
set_fs ( USER_DS ) ;
}
int copy_thread ( int nr , unsigned long clone_flags ,
unsigned long stack_start , unsigned long stack_size ,
struct task_struct * p , struct pt_regs * regs )
{
/* Start pushing stuff from the top of the child's kernel stack. */
2006-01-12 01:05:51 -08:00
unsigned long orig_ksp = task_tos ( p ) ;
2005-04-16 15:20:36 -07:00
unsigned long ksp = orig_ksp ;
/* We push two `state save' stack fames (see entry.S) on the new
kernel stack :
1 ) The innermost one is what switch_thread would have
pushed , and is used when we context switch to the child
thread for the first time . It ' s set up to return to
ret_from_fork in entry . S .
2 ) The outermost one ( nearest the top ) is what a syscall
trap would have pushed , and is set up to return to the
same location as the parent thread , but with a return
value of 0. */
struct pt_regs * child_switch_regs , * child_trap_regs ;
/* Trap frame. */
ksp - = STATE_SAVE_SIZE ;
child_trap_regs = ( struct pt_regs * ) ( ksp + STATE_SAVE_PT_OFFSET ) ;
/* Switch frame. */
ksp - = STATE_SAVE_SIZE ;
child_switch_regs = ( struct pt_regs * ) ( ksp + STATE_SAVE_PT_OFFSET ) ;
/* First copy parent's register state to child. */
* child_switch_regs = * regs ;
* child_trap_regs = * regs ;
/* switch_thread returns to the restored value of the lp
register ( r31 ) , so we make that the place where we want to
jump when the child thread begins running . */
child_switch_regs - > gpr [ GPR_LP ] = ( v850_reg_t ) ret_from_fork ;
if ( regs - > kernel_mode )
/* Since we're returning to kernel-mode, make sure the child's
stored kernel stack pointer agrees with what the actual
stack pointer will be at that point ( the trap return code
always restores the SP , even when returning to
kernel - mode ) . */
child_trap_regs - > gpr [ GPR_SP ] = orig_ksp ;
else
/* Set the child's user-mode stack-pointer (the name
` stack_start ' is a misnomer , it ' s just the initial SP
value ) . */
child_trap_regs - > gpr [ GPR_SP ] = stack_start ;
/* Thread state for the child (everything else is on the stack). */
p - > thread . ksp = ksp ;
return 0 ;
}
/*
* sys_execve ( ) executes a new program .
*/
int sys_execve ( char * name , char * * argv , char * * envp , struct pt_regs * regs )
{
char * filename = getname ( name ) ;
int error = PTR_ERR ( filename ) ;
if ( ! IS_ERR ( filename ) ) {
error = do_execve ( filename , argv , envp , regs ) ;
putname ( filename ) ;
}
return error ;
}
/*
* These bracket the sleeping functions . .
*/
# define first_sched ((unsigned long)__sched_text_start)
# define last_sched ((unsigned long)__sched_text_end)
unsigned long get_wchan ( struct task_struct * p )
{
#if 0 /* Barf. Figure out the stack-layout later. XXX */
unsigned long fp , pc ;
int count = 0 ;
if ( ! p | | p = = current | | p - > state = = TASK_RUNNING )
return 0 ;
pc = thread_saved_pc ( p ) ;
/* This quite disgusting function walks up the stack, following
saved return address , until it something that ' s out of bounds
( as defined by ` first_sched ' and ` last_sched ' ) . It then
returns the last PC that was in - bounds . */
do {
if ( fp < stack_page + sizeof ( struct task_struct ) | |
fp > = 8184 + stack_page )
return 0 ;
pc = ( ( unsigned long * ) fp ) [ 1 ] ;
if ( pc < first_sched | | pc > = last_sched )
return pc ;
fp = * ( unsigned long * ) fp ;
} while ( count + + < 16 ) ;
# endif
return 0 ;
}