2005-04-17 02:20:36 +04:00
/* $Id: process.c,v 1.28 2004/05/05 16:54:23 lethal Exp $
*
* linux / arch / sh / kernel / process . c
*
* Copyright ( C ) 1995 Linus Torvalds
*
* SuperH version : Copyright ( C ) 1999 , 2000 Niibe Yutaka & Kaz Kojima
2006-10-12 07:16:13 +04:00
* Copyright ( C ) 2006 Lineo Solutions Inc . support SH4A UBC
2005-04-17 02:20:36 +04:00
*/
/*
* This file handles the architecture - dependent parts of process handling . .
*/
# include <linux/module.h>
# include <linux/unistd.h>
# include <linux/mm.h>
# include <linux/elfcore.h>
# include <linux/a.out.h>
2006-02-01 14:06:08 +03:00
# include <linux/slab.h>
# include <linux/pm.h>
2005-04-17 02:20:36 +04:00
# include <linux/ptrace.h>
# include <linux/kallsyms.h>
2006-02-01 14:06:08 +03:00
# include <linux/kexec.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/uaccess.h>
# include <asm/mmu_context.h>
# include <asm/elf.h>
2006-09-19 22:25:34 +04:00
# include <asm/ubc.h>
2005-04-17 02:20:36 +04:00
static int hlt_counter = 0 ;
int ubc_usercnt = 0 ;
# define HARD_IDLE_TIMEOUT (HZ / 3)
2006-02-01 14:06:08 +03:00
void ( * pm_idle ) ( void ) ;
void ( * pm_power_off ) ( void ) ;
EXPORT_SYMBOL ( pm_power_off ) ;
2005-04-17 02:20:36 +04:00
void disable_hlt ( void )
{
hlt_counter + + ;
}
EXPORT_SYMBOL ( disable_hlt ) ;
void enable_hlt ( void )
{
hlt_counter - - ;
}
EXPORT_SYMBOL ( enable_hlt ) ;
2006-02-01 14:06:08 +03:00
void default_idle ( void )
{
if ( ! hlt_counter )
cpu_sleep ( ) ;
else
cpu_relax ( ) ;
}
[PATCH] sched: resched and cpu_idle rework
Make some changes to the NEED_RESCHED and POLLING_NRFLAG to reduce
confusion, and make their semantics rigid. Improves efficiency of
resched_task and some cpu_idle routines.
* In resched_task:
- TIF_NEED_RESCHED is only cleared with the task's runqueue lock held,
and as we hold it during resched_task, then there is no need for an
atomic test and set there. The only other time this should be set is
when the task's quantum expires, in the timer interrupt - this is
protected against because the rq lock is irq-safe.
- If TIF_NEED_RESCHED is set, then we don't need to do anything. It
won't get unset until the task get's schedule()d off.
- If we are running on the same CPU as the task we resched, then set
TIF_NEED_RESCHED and no further action is required.
- If we are running on another CPU, and TIF_POLLING_NRFLAG is *not* set
after TIF_NEED_RESCHED has been set, then we need to send an IPI.
Using these rules, we are able to remove the test and set operation in
resched_task, and make clear the previously vague semantics of
POLLING_NRFLAG.
* In idle routines:
- Enter cpu_idle with preempt disabled. When the need_resched() condition
becomes true, explicitly call schedule(). This makes things a bit clearer
(IMO), but haven't updated all architectures yet.
- Many do a test and clear of TIF_NEED_RESCHED for some reason. According
to the resched_task rules, this isn't needed (and actually breaks the
assumption that TIF_NEED_RESCHED is only cleared with the runqueue lock
held). So remove that. Generally one less locked memory op when switching
to the idle thread.
- Many idle routines clear TIF_POLLING_NRFLAG, and only set it in the inner
most polling idle loops. The above resched_task semantics allow it to be
set until before the last time need_resched() is checked before going into
a halt requiring interrupt wakeup.
Many idle routines simply never enter such a halt, and so POLLING_NRFLAG
can be always left set, completely eliminating resched IPIs when rescheduling
the idle task.
POLLING_NRFLAG width can be increased, to reduce the chance of resched IPIs.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Con Kolivas <kernel@kolivas.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 08:39:04 +03:00
void cpu_idle ( void )
2005-04-17 02:20:36 +04:00
{
/* endless idle loop with no priority at all */
while ( 1 ) {
2006-02-01 14:06:08 +03:00
void ( * idle ) ( void ) = pm_idle ;
if ( ! idle )
idle = default_idle ;
while ( ! need_resched ( ) )
idle ( ) ;
2005-04-17 02:20:36 +04:00
2005-11-09 08:39:01 +03:00
preempt_enable_no_resched ( ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2005-11-09 08:39:01 +03:00
preempt_disable ( ) ;
2005-04-17 02:20:36 +04: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 ) ) ;
}
void machine_halt ( void )
{
2006-02-01 14:06:08 +03:00
local_irq_disable ( ) ;
2005-04-17 02:20:36 +04:00
while ( 1 )
cpu_sleep ( ) ;
}
void machine_power_off ( void )
{
2006-02-01 14:06:08 +03:00
if ( pm_power_off )
pm_power_off ( ) ;
2005-04-17 02:20:36 +04:00
}
void show_regs ( struct pt_regs * regs )
{
printk ( " \n " ) ;
printk ( " Pid : %d, Comm: %20s \n " , current - > pid , current - > comm ) ;
2006-10-12 12:07:45 +04:00
print_symbol ( " PC is at %s \n " , instruction_pointer ( regs ) ) ;
2005-04-17 02:20:36 +04:00
printk ( " PC : %08lx SP : %08lx SR : %08lx " ,
regs - > pc , regs - > regs [ 15 ] , regs - > sr ) ;
# ifdef CONFIG_MMU
printk ( " TEA : %08x " , ctrl_inl ( MMU_TEA ) ) ;
# else
printk ( " " ) ;
# endif
printk ( " %s \n " , print_tainted ( ) ) ;
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 12:07:45 +04:00
show_trace ( NULL , ( unsigned long * ) regs - > regs [ 15 ] , regs ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Create a kernel thread
*/
/*
* This is the mechanism for creating a new kernel thread .
*
*/
extern void kernel_thread_helper ( void ) ;
__asm__ ( " .align 5 \n "
" kernel_thread_helper: \n \t "
" jsr @r5 \n \t "
" nop \n \t "
" mov.l 1f, r1 \n \t "
" jsr @r1 \n \t "
" mov r0, r4 \n \t "
" .align 2 \n \t "
" 1:.long do_exit " ) ;
int kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{ /* Don't use this in BL=1(cli). Or else, CPU resets! */
struct pt_regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . regs [ 4 ] = ( unsigned long ) arg ;
regs . regs [ 5 ] = ( unsigned long ) fn ;
regs . pc = ( unsigned long ) kernel_thread_helper ;
regs . sr = ( 1 < < 30 ) ;
/* Ok, create the new process.. */
return do_fork ( flags | CLONE_VM | CLONE_UNTRACED , 0 , & regs , 0 , NULL , NULL ) ;
}
/*
* Free current thread data structures etc . .
*/
void exit_thread ( void )
{
if ( current - > thread . ubc_pc ) {
current - > thread . ubc_pc = 0 ;
ubc_usercnt - = 1 ;
}
}
void flush_thread ( void )
{
# if defined(CONFIG_SH_FPU)
struct task_struct * tsk = current ;
/* Forget lazy FPU state */
2006-01-12 12:05:44 +03:00
clear_fpu ( tsk , task_pt_regs ( tsk ) ) ;
2005-04-17 02:20:36 +04: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 ) ;
if ( fpvalid ) {
unlazy_fpu ( tsk , regs ) ;
memcpy ( fpu , & tsk - > thread . fpu . hard , sizeof ( * fpu ) ) ;
}
# endif
return fpvalid ;
}
/*
* Capture the user space registers if the task is not running ( in user space )
*/
int dump_task_regs ( struct task_struct * tsk , elf_gregset_t * regs )
{
struct pt_regs ptregs ;
2006-01-12 12:05:44 +03:00
ptregs = * task_pt_regs ( tsk ) ;
2005-04-17 02:20:36 +04:00
elf_core_copy_regs ( regs , & ptregs ) ;
return 1 ;
}
int
dump_task_fpu ( struct task_struct * tsk , elf_fpregset_t * fpu )
{
int fpvalid = 0 ;
# if defined(CONFIG_SH_FPU)
fpvalid = ! ! tsk_used_math ( tsk ) ;
if ( fpvalid ) {
2006-01-12 12:05:44 +03:00
unlazy_fpu ( tsk , task_pt_regs ( tsk ) ) ;
2005-04-17 02:20:36 +04:00
memcpy ( fpu , & tsk - > thread . fpu . hard , sizeof ( * fpu ) ) ;
}
# endif
return fpvalid ;
}
asmlinkage void ret_from_fork ( void ) ;
int copy_thread ( int nr , unsigned long clone_flags , unsigned long usp ,
unsigned long unused ,
struct task_struct * p , struct pt_regs * regs )
{
2006-09-27 12:07:07 +04:00
struct thread_info * ti = task_thread_info ( p ) ;
2005-04-17 02:20:36 +04:00
struct pt_regs * childregs ;
# if defined(CONFIG_SH_FPU)
struct task_struct * tsk = current ;
unlazy_fpu ( tsk , regs ) ;
p - > thread . fpu = tsk - > thread . fpu ;
copy_to_stopped_child_used_math ( p ) ;
# endif
2006-01-12 12:05:44 +03:00
childregs = task_pt_regs ( p ) ;
2005-04-17 02:20:36 +04:00
* childregs = * regs ;
if ( user_mode ( regs ) ) {
childregs - > regs [ 15 ] = usp ;
2006-09-27 12:07:07 +04:00
ti - > addr_limit = USER_DS ;
2005-04-17 02:20:36 +04:00
} else {
2006-01-12 12:05:45 +03:00
childregs - > regs [ 15 ] = ( unsigned long ) task_stack_page ( p ) + THREAD_SIZE ;
2006-09-27 12:07:07 +04:00
ti - > addr_limit = KERNEL_DS ;
2005-04-17 02:20:36 +04:00
}
if ( clone_flags & CLONE_SETTLS ) {
childregs - > gbr = childregs - > regs [ 0 ] ;
}
childregs - > regs [ 0 ] = 0 ; /* Set return value for child */
p - > thread . sp = ( unsigned long ) childregs ;
p - > thread . pc = ( unsigned long ) ret_from_fork ;
p - > thread . ubc_pc = 0 ;
return 0 ;
}
/* Tracing by user break controller. */
static void
ubc_set_tracing ( int asid , unsigned long pc )
{
2006-10-12 07:16:13 +04:00
# if defined(CONFIG_CPU_SH4A)
unsigned long val ;
val = ( UBC_CBR_ID_INST | UBC_CBR_RW_READ | UBC_CBR_CE ) ;
val | = ( UBC_CBR_AIE | UBC_CBR_AIV_SET ( asid ) ) ;
ctrl_outl ( val , UBC_CBR0 ) ;
ctrl_outl ( pc , UBC_CAR0 ) ;
ctrl_outl ( 0x0 , UBC_CAMR0 ) ;
ctrl_outl ( 0x0 , UBC_CBCR ) ;
val = ( UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE ) ;
ctrl_outl ( val , UBC_CRR0 ) ;
/* Read UBC register that we writed last. For chekking UBC Register changed */
val = ctrl_inl ( UBC_CRR0 ) ;
# else /* CONFIG_CPU_SH4A */
2005-04-17 02:20:36 +04:00
ctrl_outl ( pc , UBC_BARA ) ;
2006-09-27 12:25:07 +04:00
# ifdef CONFIG_MMU
2005-04-17 02:20:36 +04:00
/* We don't have any ASID settings for the SH-2! */
if ( cpu_data - > type ! = CPU_SH7604 )
ctrl_outb ( asid , UBC_BASRA ) ;
2006-09-27 12:25:07 +04:00
# endif
2005-04-17 02:20:36 +04:00
ctrl_outl ( 0 , UBC_BAMRA ) ;
2006-09-27 12:38:11 +04:00
if ( cpu_data - > type = = CPU_SH7729 | | cpu_data - > type = = CPU_SH7710 ) {
2005-04-17 02:20:36 +04:00
ctrl_outw ( BBR_INST | BBR_READ | BBR_CPU , UBC_BBRA ) ;
ctrl_outl ( BRCR_PCBA | BRCR_PCTE , UBC_BRCR ) ;
} else {
ctrl_outw ( BBR_INST | BBR_READ , UBC_BBRA ) ;
ctrl_outw ( BRCR_PCBA , UBC_BRCR ) ;
}
2006-10-12 07:16:13 +04:00
# endif /* CONFIG_CPU_SH4A */
2005-04-17 02:20:36 +04:00
}
/*
* switch_to ( x , y ) should switch tasks from x to y .
*
*/
struct task_struct * __switch_to ( struct task_struct * prev , struct task_struct * next )
{
# if defined(CONFIG_SH_FPU)
2006-01-12 12:05:44 +03:00
unlazy_fpu ( prev , task_pt_regs ( prev ) ) ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_PREEMPT
{
unsigned long flags ;
struct pt_regs * regs ;
local_irq_save ( flags ) ;
2006-01-12 12:05:44 +03:00
regs = task_pt_regs ( prev ) ;
2005-04-17 02:20:36 +04:00
if ( user_mode ( regs ) & & regs - > regs [ 15 ] > = 0xc0000000 ) {
int offset = ( int ) regs - > regs [ 15 ] ;
/* Reset stack pointer: clear critical region mark */
regs - > regs [ 15 ] = regs - > regs [ 1 ] ;
if ( regs - > pc < regs - > regs [ 0 ] )
/* Go to rewind point */
regs - > pc = regs - > regs [ 0 ] + offset ;
}
local_irq_restore ( flags ) ;
}
# endif
2006-09-27 12:25:07 +04:00
# ifdef CONFIG_MMU
2005-04-17 02:20:36 +04:00
/*
* Restore the kernel mode register
* k7 ( r7_bank1 )
*/
asm volatile ( " ldc %0, r7_bank "
: /* no output */
2006-01-12 12:05:45 +03:00
: " r " ( task_thread_info ( next ) ) ) ;
2006-09-27 12:25:07 +04:00
# endif
2005-04-17 02:20:36 +04:00
/* If no tasks are using the UBC, we're done */
if ( ubc_usercnt = = 0 )
/* If no tasks are using the UBC, we're done */ ;
else if ( next - > thread . ubc_pc & & next - > mm ) {
2006-09-27 12:25:07 +04:00
int asid = 0 ;
# ifdef CONFIG_MMU
2006-09-27 13:33:49 +04:00
asid | = next - > mm - > context . id & MMU_CONTEXT_ASID_MASK ;
2006-09-27 12:25:07 +04:00
# endif
ubc_set_tracing ( asid , next - > thread . ubc_pc ) ;
2005-04-17 02:20:36 +04:00
} else {
2006-10-12 07:16:13 +04:00
# if defined(CONFIG_CPU_SH4A)
ctrl_outl ( UBC_CBR_INIT , UBC_CBR0 ) ;
ctrl_outl ( UBC_CRR_INIT , UBC_CRR0 ) ;
# else
2005-04-17 02:20:36 +04:00
ctrl_outw ( 0 , UBC_BBRA ) ;
ctrl_outw ( 0 , UBC_BBRB ) ;
2006-10-12 07:16:13 +04:00
# endif
2005-04-17 02:20:36 +04:00
}
return prev ;
}
asmlinkage int sys_fork ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
2006-11-21 05:16:57 +03:00
struct pt_regs __regs )
2005-04-17 02:20:36 +04:00
{
2006-11-21 05:16:57 +03:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MMU
2006-11-21 05:16:57 +03:00
return do_fork ( SIGCHLD , regs - > regs [ 15 ] , regs , 0 , NULL , NULL ) ;
2005-04-17 02:20:36 +04: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 05:16:57 +03:00
struct pt_regs __regs )
2005-04-17 02:20:36 +04:00
{
2006-11-21 05:16:57 +03:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! newsp )
2006-11-21 05:16:57 +03:00
newsp = regs - > regs [ 15 ] ;
return do_fork ( clone_flags , newsp , regs , 0 ,
2005-04-17 02:20:36 +04:00
( int __user * ) parent_tidptr , ( int __user * ) child_tidptr ) ;
}
/*
* 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 05:16:57 +03:00
struct pt_regs __regs )
2005-04-17 02:20:36 +04:00
{
2006-11-21 05:16:57 +03:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
return do_fork ( CLONE_VFORK | CLONE_VM | SIGCHLD , regs - > regs [ 15 ] , regs ,
2005-04-17 02:20:36 +04:00
0 , NULL , NULL ) ;
}
/*
* sys_execve ( ) executes a new program .
*/
asmlinkage int sys_execve ( char * ufilename , char * * uargv ,
char * * uenvp , unsigned long r7 ,
2006-11-21 05:16:57 +03:00
struct pt_regs __regs )
2005-04-17 02:20:36 +04:00
{
2006-11-21 05:16:57 +03:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2005-04-17 02:20:36 +04:00
int error ;
char * filename ;
filename = getname ( ( char __user * ) ufilename ) ;
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
error = do_execve ( filename ,
( char __user * __user * ) uargv ,
( char __user * __user * ) uenvp ,
2006-11-21 05:16:57 +03:00
regs ) ;
2005-04-17 02:20:36 +04:00
if ( error = = 0 ) {
task_lock ( current ) ;
current - > ptrace & = ~ PT_DTRACE ;
task_unlock ( current ) ;
}
putname ( filename ) ;
out :
return error ;
}
unsigned long get_wchan ( struct task_struct * p )
{
unsigned long schedule_frame ;
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 ) ;
if ( in_sched_functions ( pc ) ) {
2006-12-08 11:46:29 +03:00
schedule_frame = ( unsigned long ) p - > thread . sp ;
return ( ( unsigned long * ) schedule_frame ) [ 21 ] ;
2005-04-17 02:20:36 +04:00
}
2006-12-08 11:46:29 +03:00
2005-04-17 02:20:36 +04:00
return pc ;
}
2006-11-21 05:16:57 +03:00
asmlinkage void break_point_trap ( void )
2005-04-17 02:20:36 +04:00
{
/* Clear tracing. */
2006-10-12 07:16:13 +04:00
# if defined(CONFIG_CPU_SH4A)
ctrl_outl ( UBC_CBR_INIT , UBC_CBR0 ) ;
ctrl_outl ( UBC_CRR_INIT , UBC_CRR0 ) ;
# else
2005-04-17 02:20:36 +04:00
ctrl_outw ( 0 , UBC_BBRA ) ;
ctrl_outw ( 0 , UBC_BBRB ) ;
2006-10-12 07:16:13 +04:00
# endif
2005-04-17 02:20:36 +04:00
current - > thread . ubc_pc = 0 ;
ubc_usercnt - = 1 ;
force_sig ( SIGTRAP , current ) ;
}
2006-12-13 11:40:05 +03:00
/*
* Generic trap handler .
*/
asmlinkage void debug_trap_handler ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
struct pt_regs __regs )
{
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
/* Rewind */
regs - > pc - = 2 ;
force_sig ( SIGTRAP , current ) ;
}
/*
* Special handler for BUG ( ) traps .
*/
asmlinkage void bug_trap_handler ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
struct pt_regs __regs )
2005-04-17 02:20:36 +04:00
{
2006-11-21 05:16:57 +03:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
2006-12-08 11:41:43 +03:00
/* Rewind */
2006-11-21 05:16:57 +03:00
regs - > pc - = 2 ;
2006-12-08 11:41:43 +03:00
# ifdef CONFIG_BUG
if ( __kernel_text_address ( instruction_pointer ( regs ) ) ) {
u16 insn = * ( u16 * ) instruction_pointer ( regs ) ;
if ( insn = = TRAPA_BUG_OPCODE )
handle_BUG ( regs ) ;
}
# endif
2005-04-17 02:20:36 +04:00
force_sig ( SIGTRAP , current ) ;
}