2006-12-25 09:51:47 +09:00
/*
* arch / sh / kernel / process . c
2005-04-16 15:20:36 -07:00
*
2006-12-25 09:51:47 +09:00
* This file handles the architecture - dependent parts of process handling . .
2005-04-16 15:20:36 -07:00
*
* Copyright ( C ) 1995 Linus Torvalds
*
* SuperH version : Copyright ( C ) 1999 , 2000 Niibe Yutaka & Kaz Kojima
2006-10-12 12:16:13 +09:00
* Copyright ( C ) 2006 Lineo Solutions Inc . support SH4A UBC
2008-09-21 19:04:55 +09:00
* Copyright ( C ) 2002 - 2008 Paul Mundt
*
* 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 .
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h>
# include <linux/mm.h>
# include <linux/elfcore.h>
2006-02-01 03:06:08 -08:00
# include <linux/pm.h>
2005-04-16 15:20:36 -07:00
# include <linux/kallsyms.h>
2006-02-01 03:06:08 -08:00
# include <linux/kexec.h>
2007-05-09 10:55:38 +09:00
# include <linux/kdebug.h>
2007-05-09 17:33:24 +09:00
# include <linux/tick.h>
2007-05-14 12:52:56 +09:00
# include <linux/reboot.h>
2007-07-31 13:01:43 +09:00
# include <linux/fs.h>
2009-07-11 00:29:04 +00:00
# include <linux/ftrace.h>
2007-09-28 15:48:06 +09:00
# include <linux/preempt.h>
2009-11-09 16:27:40 +09:00
# include <linux/hw_breakpoint.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include <asm/mmu_context.h>
2007-05-08 11:55:21 +09:00
# include <asm/pgalloc.h>
2007-05-08 14:50:59 +09:00
# include <asm/system.h>
2006-09-20 03:25:34 +09:00
# include <asm/ubc.h>
2008-03-26 19:02:47 +09:00
# include <asm/fpu.h>
2008-09-04 18:53:58 +09:00
# include <asm/syscalls.h>
2009-08-24 16:20:44 +09:00
# include <asm/watchdog.h>
2005-04-16 15:20:36 -07:00
2009-08-24 16:20:44 +09:00
# ifdef CONFIG_32BIT
static void watchdog_trigger_immediate ( void )
{
sh_wdt_write_cnt ( 0xFF ) ;
sh_wdt_write_csr ( 0xC2 ) ;
}
void machine_restart ( char * __unused )
{
local_irq_disable ( ) ;
/* Use watchdog timer to trigger reset */
watchdog_trigger_immediate ( ) ;
while ( 1 )
cpu_sleep ( ) ;
}
# else
2005-04-16 15:20:36 -07:00
void machine_restart ( char * __unused )
{
/* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
asm volatile ( " ldc %0, sr \n \t "
" mov.l @%1, %0 " : : " r " ( 0x10000000 ) , " r " ( 0x80000001 ) ) ;
}
2009-08-24 16:20:44 +09:00
# endif
2005-04-16 15:20:36 -07:00
void machine_halt ( void )
{
2006-02-01 03:06:08 -08:00
local_irq_disable ( ) ;
2005-04-16 15:20:36 -07:00
while ( 1 )
cpu_sleep ( ) ;
}
void machine_power_off ( void )
{
2006-02-01 03:06:08 -08:00
if ( pm_power_off )
pm_power_off ( ) ;
2005-04-16 15:20:36 -07:00
}
void show_regs ( struct pt_regs * regs )
{
printk ( " \n " ) ;
2008-11-26 15:20:35 +09:00
printk ( " Pid : %d, Comm: \t \t %s \n " , task_pid_nr ( current ) , current - > comm ) ;
printk ( " CPU : %d \t \t %s (%s %.*s) \n \n " ,
2008-08-08 01:23:34 +09:00
smp_processor_id ( ) , print_tainted ( ) , init_utsname ( ) - > release ,
( int ) strcspn ( init_utsname ( ) - > version , " " ) ,
init_utsname ( ) - > version ) ;
2006-10-12 17:07:45 +09:00
print_symbol ( " PC is at %s \n " , instruction_pointer ( regs ) ) ;
2008-08-08 01:23:34 +09:00
print_symbol ( " PR is at %s \n " , regs - > pr ) ;
2005-04-16 15:20:36 -07:00
printk ( " PC : %08lx SP : %08lx SR : %08lx " ,
regs - > pc , regs - > regs [ 15 ] , regs - > sr ) ;
# ifdef CONFIG_MMU
2008-08-08 01:23:34 +09:00
printk ( " TEA : %08x \n " , ctrl_inl ( MMU_TEA ) ) ;
2005-04-16 15:20:36 -07:00
# else
2008-08-08 01:23:34 +09:00
printk ( " \n " ) ;
2005-04-16 15:20:36 -07:00
# endif
printk ( " R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx \n " ,
regs - > regs [ 0 ] , regs - > regs [ 1 ] ,
regs - > regs [ 2 ] , regs - > regs [ 3 ] ) ;
printk ( " R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx \n " ,
regs - > regs [ 4 ] , regs - > regs [ 5 ] ,
regs - > regs [ 6 ] , regs - > regs [ 7 ] ) ;
printk ( " R8 : %08lx R9 : %08lx R10 : %08lx R11 : %08lx \n " ,
regs - > regs [ 8 ] , regs - > regs [ 9 ] ,
regs - > regs [ 10 ] , regs - > regs [ 11 ] ) ;
printk ( " R12 : %08lx R13 : %08lx R14 : %08lx \n " ,
regs - > regs [ 12 ] , regs - > regs [ 13 ] ,
regs - > regs [ 14 ] ) ;
printk ( " MACH: %08lx MACL: %08lx GBR : %08lx PR : %08lx \n " ,
regs - > mach , regs - > macl , regs - > gbr , regs - > pr ) ;
2006-10-12 17:07:45 +09:00
show_trace ( NULL , ( unsigned long * ) regs - > regs [ 15 ] , regs ) ;
2008-11-26 14:31:03 +09:00
show_code ( regs ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Create a kernel thread
*/
2008-12-17 12:20:15 +09:00
ATTRIB_NORET void kernel_thread_helper ( void * arg , int ( * fn ) ( void * ) )
{
do_exit ( fn ( arg ) ) ;
}
2005-04-16 15:20:36 -07:00
2006-12-25 09:51:47 +09:00
/* Don't use this in BL=1(cli). Or else, CPU resets! */
2005-04-16 15:20:36 -07:00
int kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
2006-12-25 09:51:47 +09:00
{
2005-04-16 15:20:36 -07:00
struct pt_regs regs ;
2008-09-21 13:56:39 +09:00
int pid ;
2005-04-16 15:20:36 -07:00
memset ( & regs , 0 , sizeof ( regs ) ) ;
2006-12-25 09:51:47 +09:00
regs . regs [ 4 ] = ( unsigned long ) arg ;
regs . regs [ 5 ] = ( unsigned long ) fn ;
2005-04-16 15:20:36 -07:00
2006-12-25 09:51:47 +09:00
regs . pc = ( unsigned long ) kernel_thread_helper ;
2005-04-16 15:20:36 -07:00
regs . sr = ( 1 < < 30 ) ;
/* Ok, create the new process.. */
2008-09-21 13:56:39 +09:00
pid = do_fork ( flags | CLONE_VM | CLONE_UNTRACED , 0 ,
& regs , 0 , NULL , NULL ) ;
return pid ;
2005-04-16 15:20:36 -07:00
}
/*
* Free current thread data structures etc . .
*/
void exit_thread ( void )
{
}
void flush_thread ( void )
{
struct task_struct * tsk = current ;
2009-11-09 16:27:40 +09:00
flush_ptrace_hw_breakpoint ( tsk ) ;
# if defined(CONFIG_SH_FPU)
2005-04-16 15:20:36 -07:00
/* Forget lazy FPU state */
2006-01-12 01:05:44 -08:00
clear_fpu ( tsk , task_pt_regs ( tsk ) ) ;
2005-04-16 15:20:36 -07:00
clear_used_math ( ) ;
# endif
}
void release_thread ( struct task_struct * dead_task )
{
/* do nothing */
}
/* Fill in the fpu structure for a core dump.. */
int dump_fpu ( struct pt_regs * regs , elf_fpregset_t * fpu )
{
int fpvalid = 0 ;
# if defined(CONFIG_SH_FPU)
struct task_struct * tsk = current ;
fpvalid = ! ! tsk_used_math ( tsk ) ;
2008-09-21 19:04:55 +09:00
if ( fpvalid )
fpvalid = ! fpregs_get ( tsk , NULL , 0 ,
sizeof ( struct user_fpu_struct ) ,
fpu , NULL ) ;
2005-04-16 15:20:36 -07:00
# endif
return fpvalid ;
}
asmlinkage void ret_from_fork ( void ) ;
2009-04-02 16:56:59 -07:00
int copy_thread ( unsigned long clone_flags , unsigned long usp ,
2005-04-16 15:20:36 -07:00
unsigned long unused ,
struct task_struct * p , struct pt_regs * regs )
{
2006-09-27 17:07:07 +09:00
struct thread_info * ti = task_thread_info ( p ) ;
2005-04-16 15:20:36 -07:00
struct pt_regs * childregs ;
struct task_struct * tsk = current ;
2009-04-03 17:32:33 +00:00
# if defined(CONFIG_SH_FPU)
2005-04-16 15:20:36 -07:00
unlazy_fpu ( tsk , regs ) ;
p - > thread . fpu = tsk - > thread . fpu ;
copy_to_stopped_child_used_math ( p ) ;
# endif
2009-04-03 17:32:33 +00:00
# if defined(CONFIG_SH_DSP)
if ( is_dsp_enabled ( tsk ) ) {
/* We can use the __save_dsp or just copy the struct:
* __save_dsp ( p ) ;
* p - > thread . dsp_status . status | = SR_DSP
*/
p - > thread . dsp_status = tsk - > thread . dsp_status ;
}
# endif
2006-01-12 01:05:44 -08:00
childregs = task_pt_regs ( p ) ;
2005-04-16 15:20:36 -07:00
* childregs = * regs ;
if ( user_mode ( regs ) ) {
childregs - > regs [ 15 ] = usp ;
2006-09-27 17:07:07 +09:00
ti - > addr_limit = USER_DS ;
2005-04-16 15:20:36 -07:00
} else {
2007-02-28 18:35:42 +09:00
childregs - > regs [ 15 ] = ( unsigned long ) childregs ;
2006-09-27 17:07:07 +09:00
ti - > addr_limit = KERNEL_DS ;
2005-04-16 15:20:36 -07:00
}
2006-12-25 09:51:47 +09:00
2007-02-28 18:35:42 +09:00
if ( clone_flags & CLONE_SETTLS )
2005-04-16 15:20:36 -07:00
childregs - > gbr = childregs - > regs [ 0 ] ;
2006-12-25 09:51:47 +09:00
2005-04-16 15:20:36 -07:00
childregs - > regs [ 0 ] = 0 ; /* Set return value for child */
p - > thread . sp = ( unsigned long ) childregs ;
p - > thread . pc = ( unsigned long ) ret_from_fork ;
2009-11-09 16:27:40 +09:00
memset ( p - > thread . ptrace_bps , 0 , sizeof ( p - > thread . ptrace_bps ) ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/*
* switch_to ( x , y ) should switch tasks from x to y .
*
*/
2009-07-11 00:29:04 +00:00
__notrace_funcgraph struct task_struct *
__switch_to ( struct task_struct * prev , struct task_struct * next )
2005-04-16 15:20:36 -07:00
{
# if defined(CONFIG_SH_FPU)
2006-01-12 01:05:44 -08:00
unlazy_fpu ( prev , task_pt_regs ( prev ) ) ;
2005-04-16 15:20:36 -07:00
# endif
2006-09-27 17:25:07 +09:00
# ifdef CONFIG_MMU
2005-04-16 15:20:36 -07:00
/*
* Restore the kernel mode register
2006-12-25 09:51:47 +09:00
* k7 ( r7_bank1 )
2005-04-16 15:20:36 -07:00
*/
asm volatile ( " ldc %0, r7_bank "
: /* no output */
2006-01-12 01:05:45 -08:00
: " r " ( task_thread_info ( next ) ) ) ;
2006-09-27 17:25:07 +09:00
# endif
2005-04-16 15:20:36 -07:00
return prev ;
}
asmlinkage int sys_fork ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
# ifdef CONFIG_MMU
2007-05-14 17:26:34 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2006-11-21 11:16:57 +09:00
return do_fork ( SIGCHLD , regs - > regs [ 15 ] , regs , 0 , NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
# else
/* fork almost works, enough to trick you into looking elsewhere :-( */
return - EINVAL ;
# endif
}
asmlinkage int sys_clone ( unsigned long clone_flags , unsigned long newsp ,
unsigned long parent_tidptr ,
unsigned long child_tidptr ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
2006-11-21 11:16:57 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( ! newsp )
2006-11-21 11:16:57 +09:00
newsp = regs - > regs [ 15 ] ;
return do_fork ( clone_flags , newsp , regs , 0 ,
2006-12-25 09:51:47 +09:00
( int __user * ) parent_tidptr ,
( int __user * ) child_tidptr ) ;
2005-04-16 15:20:36 -07:00
}
/*
* This is trivial , and on the face of it looks like it
* could equally well be done in user mode .
*
* Not so , for quite unobvious reasons - register pressure .
* In user mode vfork ( ) cannot have a stack frame , and if
* done by calling the " clone() " system call directly , you
* do not have enough call - clobbered registers to hold all
* the information you need .
*/
asmlinkage int sys_vfork ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
2006-11-21 11:16:57 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
return do_fork ( CLONE_VFORK | CLONE_VM | SIGCHLD , regs - > regs [ 15 ] , regs ,
2005-04-16 15:20:36 -07:00
0 , NULL , NULL ) ;
}
/*
* sys_execve ( ) executes a new program .
*/
2007-05-14 12:52:56 +09:00
asmlinkage int sys_execve ( char __user * ufilename , char __user * __user * uargv ,
char __user * __user * uenvp , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
2006-11-21 11:16:57 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2005-04-16 15:20:36 -07:00
int error ;
char * filename ;
2007-05-14 12:52:56 +09:00
filename = getname ( ufilename ) ;
2005-04-16 15:20:36 -07:00
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
2007-05-14 12:52:56 +09:00
error = do_execve ( filename , uargv , uenvp , regs ) ;
2005-04-16 15:20:36 -07:00
putname ( filename ) ;
out :
return error ;
}
unsigned long get_wchan ( struct task_struct * p )
{
unsigned long pc ;
if ( ! p | | p = = current | | p - > state = = TASK_RUNNING )
return 0 ;
/*
* The same comment as on the Alpha applies here , too . . .
*/
pc = thread_saved_pc ( p ) ;
2007-07-26 17:46:07 +09:00
# ifdef CONFIG_FRAME_POINTER
2005-04-16 15:20:36 -07:00
if ( in_sched_functions ( pc ) ) {
2007-07-26 17:46:07 +09:00
unsigned long schedule_frame = ( unsigned long ) p - > thread . sp ;
2006-12-08 17:46:29 +09:00
return ( ( unsigned long * ) schedule_frame ) [ 21 ] ;
2005-04-16 15:20:36 -07:00
}
2007-07-26 17:46:07 +09:00
# endif
2006-12-08 17:46:29 +09:00
2005-04-16 15:20:36 -07:00
return pc ;
}