2008-04-27 13:26:36 +04:00
/* arch/sparc64/kernel/process.c
2005-04-17 02:20:36 +04:00
*
2008-05-20 10:46:00 +04:00
* Copyright ( C ) 1995 , 1996 , 2008 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1996 Eddie C . Dost ( ecd @ skynet . be )
* Copyright ( C ) 1997 , 1998 Jakub Jelinek ( jj @ sunsite . mff . cuni . cz )
*/
/*
* This file handles the architecture - dependent parts of process handling . .
*/
# include <stdarg.h>
# include <linux/errno.h>
2011-07-22 21:18:16 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
2007-07-30 02:36:13 +04:00
# include <linux/fs.h>
2005-04-17 02:20:36 +04:00
# include <linux/smp.h>
# include <linux/stddef.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/user.h>
# include <linux/delay.h>
# include <linux/compat.h>
2007-02-22 17:24:45 +03:00
# include <linux/tick.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2007-07-16 14:49:40 +04:00
# include <linux/cpu.h>
2008-02-20 08:25:50 +03:00
# include <linux/elfcore.h>
2008-05-20 10:46:00 +04:00
# include <linux/sysrq.h>
2009-02-03 08:57:48 +03:00
# include <linux/nmi.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/system.h>
# include <asm/page.h>
# include <asm/pgalloc.h>
# include <asm/pgtable.h>
# include <asm/processor.h>
# include <asm/pstate.h>
# include <asm/elf.h>
# include <asm/fpumacro.h>
# include <asm/head.h>
# include <asm/cpudata.h>
2006-02-01 05:29:18 +03:00
# include <asm/mmu_context.h>
2005-04-17 02:20:36 +04:00
# include <asm/unistd.h>
2006-02-22 03:55:23 +03:00
# include <asm/hypervisor.h>
2008-02-20 08:25:50 +03:00
# include <asm/syscalls.h>
2008-05-20 10:46:00 +04:00
# include <asm/irq_regs.h>
# include <asm/smp.h>
2005-04-17 02:20:36 +04:00
2008-08-13 05:33:56 +04:00
# include "kstack.h"
2007-07-16 14:49:40 +04:00
static void sparc64_yield ( int cpu )
2005-04-17 02:20:36 +04:00
{
2009-02-03 08:57:48 +03:00
if ( tlb_type ! = hypervisor ) {
touch_nmi_watchdog ( ) ;
2006-02-22 03:55:23 +03:00
return ;
2009-02-03 08:57:48 +03:00
}
2006-02-22 03:55:23 +03:00
clear_thread_flag ( TIF_POLLING_NRFLAG ) ;
smp_mb__after_clear_bit ( ) ;
2007-07-16 14:49:40 +04:00
while ( ! need_resched ( ) & & ! cpu_is_offline ( cpu ) ) {
2006-02-22 03:55:23 +03:00
unsigned long pstate ;
/* Disable interrupts. */
__asm__ __volatile__ (
" rdpr %%pstate, %0 \n \t "
" andn %0, %1, %0 \n \t "
" wrpr %0, %%g0, %%pstate "
: " =&r " ( pstate )
: " i " ( PSTATE_IE ) ) ;
2007-07-16 14:49:40 +04:00
if ( ! need_resched ( ) & & ! cpu_is_offline ( cpu ) )
2006-02-22 03:55:23 +03:00
sun4v_cpu_yield ( ) ;
/* Re-enable interrupts. */
__asm__ __volatile__ (
" rdpr %%pstate, %0 \n \t "
" or %0, %1, %0 \n \t "
" wrpr %0, %%g0, %%pstate "
: " =&r " ( pstate )
: " i " ( PSTATE_IE ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-02-22 03:55:23 +03:00
set_thread_flag ( TIF_POLLING_NRFLAG ) ;
}
2005-04-17 02:20:36 +04:00
2006-02-22 03:55:23 +03:00
/* The idle loop on sparc64. */
2005-04-17 02:20:36 +04:00
void cpu_idle ( void )
{
2007-07-16 14:49:40 +04:00
int cpu = smp_processor_id ( ) ;
2005-04-17 02:20:36 +04:00
set_thread_flag ( TIF_POLLING_NRFLAG ) ;
[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
2005-04-17 02:20:36 +04:00
while ( 1 ) {
2011-10-08 18:01:00 +04:00
tick_nohz_idle_enter_norcu ( ) ;
2007-07-16 14:49:40 +04:00
while ( ! need_resched ( ) & & ! cpu_is_offline ( cpu ) )
sparc64_yield ( cpu ) ;
2011-10-08 18:01:00 +04:00
tick_nohz_idle_exit_norcu ( ) ;
2007-02-22 17:24:45 +03:00
preempt_enable_no_resched ( ) ;
2007-07-16 14:49:40 +04:00
# ifdef CONFIG_HOTPLUG_CPU
if ( cpu_is_offline ( cpu ) )
cpu_play_dead ( ) ;
# endif
2007-02-22 17:24:45 +03:00
schedule ( ) ;
preempt_disable ( ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-01-19 01:58:05 +03:00
# ifdef CONFIG_COMPAT
2005-04-17 02:20:36 +04:00
static void show_regwindow32 ( struct pt_regs * regs )
{
struct reg_window32 __user * rw ;
struct reg_window32 r_w ;
mm_segment_t old_fs ;
__asm__ __volatile__ ( " flushw " ) ;
rw = compat_ptr ( ( unsigned ) regs - > u_regs [ 14 ] ) ;
old_fs = get_fs ( ) ;
set_fs ( USER_DS ) ;
if ( copy_from_user ( & r_w , rw , sizeof ( r_w ) ) ) {
set_fs ( old_fs ) ;
return ;
}
set_fs ( old_fs ) ;
printk ( " l0: %08x l1: %08x l2: %08x l3: %08x "
" l4: %08x l5: %08x l6: %08x l7: %08x \n " ,
r_w . locals [ 0 ] , r_w . locals [ 1 ] , r_w . locals [ 2 ] , r_w . locals [ 3 ] ,
r_w . locals [ 4 ] , r_w . locals [ 5 ] , r_w . locals [ 6 ] , r_w . locals [ 7 ] ) ;
printk ( " i0: %08x i1: %08x i2: %08x i3: %08x "
" i4: %08x i5: %08x i6: %08x i7: %08x \n " ,
r_w . ins [ 0 ] , r_w . ins [ 1 ] , r_w . ins [ 2 ] , r_w . ins [ 3 ] ,
r_w . ins [ 4 ] , r_w . ins [ 5 ] , r_w . ins [ 6 ] , r_w . ins [ 7 ] ) ;
}
2006-01-19 01:58:05 +03:00
# else
# define show_regwindow32(regs) do { } while (0)
# endif
2005-04-17 02:20:36 +04:00
static void show_regwindow ( struct pt_regs * regs )
{
struct reg_window __user * rw ;
struct reg_window * rwk ;
struct reg_window r_w ;
mm_segment_t old_fs ;
if ( ( regs - > tstate & TSTATE_PRIV ) | | ! ( test_thread_flag ( TIF_32BIT ) ) ) {
__asm__ __volatile__ ( " flushw " ) ;
rw = ( struct reg_window __user * )
( regs - > u_regs [ 14 ] + STACK_BIAS ) ;
rwk = ( struct reg_window * )
( regs - > u_regs [ 14 ] + STACK_BIAS ) ;
if ( ! ( regs - > tstate & TSTATE_PRIV ) ) {
old_fs = get_fs ( ) ;
set_fs ( USER_DS ) ;
if ( copy_from_user ( & r_w , rw , sizeof ( r_w ) ) ) {
set_fs ( old_fs ) ;
return ;
}
rwk = & r_w ;
set_fs ( old_fs ) ;
}
} else {
show_regwindow32 ( regs ) ;
return ;
}
printk ( " l0: %016lx l1: %016lx l2: %016lx l3: %016lx \n " ,
rwk - > locals [ 0 ] , rwk - > locals [ 1 ] , rwk - > locals [ 2 ] , rwk - > locals [ 3 ] ) ;
printk ( " l4: %016lx l5: %016lx l6: %016lx l7: %016lx \n " ,
rwk - > locals [ 4 ] , rwk - > locals [ 5 ] , rwk - > locals [ 6 ] , rwk - > locals [ 7 ] ) ;
printk ( " i0: %016lx i1: %016lx i2: %016lx i3: %016lx \n " ,
rwk - > ins [ 0 ] , rwk - > ins [ 1 ] , rwk - > ins [ 2 ] , rwk - > ins [ 3 ] ) ;
printk ( " i4: %016lx i5: %016lx i6: %016lx i7: %016lx \n " ,
rwk - > ins [ 4 ] , rwk - > ins [ 5 ] , rwk - > ins [ 6 ] , rwk - > ins [ 7 ] ) ;
if ( regs - > tstate & TSTATE_PRIV )
2008-07-18 09:11:32 +04:00
printk ( " I7: <%pS> \n " , ( void * ) rwk - > ins [ 7 ] ) ;
2005-04-17 02:20:36 +04:00
}
2008-08-01 07:33:43 +04:00
void show_regs ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
printk ( " TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x %s \n " , regs - > tstate ,
regs - > tpc , regs - > tnpc , regs - > y , print_tainted ( ) ) ;
2008-07-18 09:11:32 +04:00
printk ( " TPC: <%pS> \n " , ( void * ) regs - > tpc ) ;
2005-04-17 02:20:36 +04:00
printk ( " g0: %016lx g1: %016lx g2: %016lx g3: %016lx \n " ,
regs - > u_regs [ 0 ] , regs - > u_regs [ 1 ] , regs - > u_regs [ 2 ] ,
regs - > u_regs [ 3 ] ) ;
printk ( " g4: %016lx g5: %016lx g6: %016lx g7: %016lx \n " ,
regs - > u_regs [ 4 ] , regs - > u_regs [ 5 ] , regs - > u_regs [ 6 ] ,
regs - > u_regs [ 7 ] ) ;
printk ( " o0: %016lx o1: %016lx o2: %016lx o3: %016lx \n " ,
regs - > u_regs [ 8 ] , regs - > u_regs [ 9 ] , regs - > u_regs [ 10 ] ,
regs - > u_regs [ 11 ] ) ;
printk ( " o4: %016lx o5: %016lx sp: %016lx ret_pc: %016lx \n " ,
regs - > u_regs [ 12 ] , regs - > u_regs [ 13 ] , regs - > u_regs [ 14 ] ,
regs - > u_regs [ 15 ] ) ;
2008-07-18 09:11:32 +04:00
printk ( " RPC: <%pS> \n " , ( void * ) regs - > u_regs [ 15 ] ) ;
2005-04-17 02:20:36 +04:00
show_regwindow ( regs ) ;
2010-04-21 13:31:50 +04:00
show_stack ( current , ( unsigned long * ) regs - > u_regs [ UREG_FP ] ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-20 10:46:00 +04:00
struct global_reg_snapshot global_reg_snapshot [ NR_CPUS ] ;
static DEFINE_SPINLOCK ( global_reg_snapshot_lock ) ;
static void __global_reg_self ( struct thread_info * tp , struct pt_regs * regs ,
int this_cpu )
{
flushw_all ( ) ;
global_reg_snapshot [ this_cpu ] . tstate = regs - > tstate ;
global_reg_snapshot [ this_cpu ] . tpc = regs - > tpc ;
global_reg_snapshot [ this_cpu ] . tnpc = regs - > tnpc ;
global_reg_snapshot [ this_cpu ] . o7 = regs - > u_regs [ UREG_I7 ] ;
if ( regs - > tstate & TSTATE_PRIV ) {
struct reg_window * rw ;
rw = ( struct reg_window * )
( regs - > u_regs [ UREG_FP ] + STACK_BIAS ) ;
2008-08-13 05:33:56 +04:00
if ( kstack_valid ( tp , ( unsigned long ) rw ) ) {
2008-07-31 08:57:59 +04:00
global_reg_snapshot [ this_cpu ] . i7 = rw - > ins [ 7 ] ;
rw = ( struct reg_window * )
( rw - > ins [ 6 ] + STACK_BIAS ) ;
2008-08-13 05:33:56 +04:00
if ( kstack_valid ( tp , ( unsigned long ) rw ) )
2008-07-31 08:57:59 +04:00
global_reg_snapshot [ this_cpu ] . rpc = rw - > ins [ 7 ] ;
}
} else {
2008-05-20 10:46:00 +04:00
global_reg_snapshot [ this_cpu ] . i7 = 0 ;
2008-07-31 08:57:59 +04:00
global_reg_snapshot [ this_cpu ] . rpc = 0 ;
}
2008-05-20 10:46:00 +04:00
global_reg_snapshot [ this_cpu ] . thread = tp ;
}
/* In order to avoid hangs we do not try to synchronize with the
* global register dump client cpus . The last store they make is to
* the thread pointer , so do a short poll waiting for that to become
* non - NULL .
*/
static void __global_reg_poll ( struct global_reg_snapshot * gp )
{
int limit = 0 ;
while ( ! gp - > thread & & + + limit < 100 ) {
barrier ( ) ;
udelay ( 1 ) ;
}
}
2009-08-03 11:31:54 +04:00
void arch_trigger_all_cpu_backtrace ( void )
2008-05-20 10:46:00 +04:00
{
struct thread_info * tp = current_thread_info ( ) ;
struct pt_regs * regs = get_irq_regs ( ) ;
unsigned long flags ;
int this_cpu , cpu ;
if ( ! regs )
regs = tp - > kregs ;
spin_lock_irqsave ( & global_reg_snapshot_lock , flags ) ;
memset ( global_reg_snapshot , 0 , sizeof ( global_reg_snapshot ) ) ;
this_cpu = raw_smp_processor_id ( ) ;
__global_reg_self ( tp , regs , this_cpu ) ;
smp_fetch_global_regs ( ) ;
for_each_online_cpu ( cpu ) {
struct global_reg_snapshot * gp = & global_reg_snapshot [ cpu ] ;
__global_reg_poll ( gp ) ;
tp = gp - > thread ;
printk ( " %c CPU[%3d]: TSTATE[%016lx] TPC[%016lx] TNPC[%016lx] TASK[%s:%d] \n " ,
( cpu = = this_cpu ? ' * ' : ' ' ) , cpu ,
gp - > tstate , gp - > tpc , gp - > tnpc ,
( ( tp & & tp - > task ) ? tp - > task - > comm : " NULL " ) ,
( ( tp & & tp - > task ) ? tp - > task - > pid : - 1 ) ) ;
2008-07-18 09:11:32 +04:00
2008-05-20 10:46:00 +04:00
if ( gp - > tstate & TSTATE_PRIV ) {
2008-07-31 08:57:59 +04:00
printk ( " TPC[%pS] O7[%pS] I7[%pS] RPC[%pS] \n " ,
2008-07-18 09:11:32 +04:00
( void * ) gp - > tpc ,
( void * ) gp - > o7 ,
2008-07-31 08:57:59 +04:00
( void * ) gp - > i7 ,
( void * ) gp - > rpc ) ;
2008-07-18 09:11:32 +04:00
} else {
2008-07-31 08:57:59 +04:00
printk ( " TPC[%lx] O7[%lx] I7[%lx] RPC[%lx] \n " ,
gp - > tpc , gp - > o7 , gp - > i7 , gp - > rpc ) ;
2008-05-20 10:46:00 +04:00
}
}
memset ( global_reg_snapshot , 0 , sizeof ( global_reg_snapshot ) ) ;
spin_unlock_irqrestore ( & global_reg_snapshot_lock , flags ) ;
}
2008-07-31 09:35:00 +04:00
# ifdef CONFIG_MAGIC_SYSRQ
2010-08-18 08:15:46 +04:00
static void sysrq_handle_globreg ( int key )
2008-07-31 09:35:00 +04:00
{
2009-08-03 11:31:54 +04:00
arch_trigger_all_cpu_backtrace ( ) ;
2008-07-31 09:35:00 +04:00
}
2008-05-20 10:46:00 +04:00
static struct sysrq_key_op sparc_globalreg_op = {
. handler = sysrq_handle_globreg ,
. help_msg = " Globalregs " ,
. action_msg = " Show Global CPU Regs " ,
} ;
static int __init sparc_globreg_init ( void )
{
return register_sysrq_key ( ' y ' , & sparc_globalreg_op ) ;
}
core_initcall ( sparc_globreg_init ) ;
# endif
2005-04-17 02:20:36 +04:00
unsigned long thread_saved_pc ( struct task_struct * tsk )
{
2006-01-12 12:05:42 +03:00
struct thread_info * ti = task_thread_info ( tsk ) ;
2005-04-17 02:20:36 +04:00
unsigned long ret = 0xdeadbeefUL ;
if ( ti & & ti - > ksp ) {
unsigned long * sp ;
sp = ( unsigned long * ) ( ti - > ksp + STACK_BIAS ) ;
if ( ( ( unsigned long ) sp & ( sizeof ( long ) - 1 ) ) = = 0UL & &
sp [ 14 ] ) {
unsigned long * fp ;
fp = ( unsigned long * ) ( sp [ 14 ] + STACK_BIAS ) ;
if ( ( ( unsigned long ) fp & ( sizeof ( long ) - 1 ) ) = = 0UL )
ret = fp [ 15 ] ;
}
}
return ret ;
}
/* Free current thread data structures etc.. */
void exit_thread ( void )
{
struct thread_info * t = current_thread_info ( ) ;
if ( t - > utraps ) {
if ( t - > utraps [ 0 ] < 2 )
kfree ( t - > utraps ) ;
else
t - > utraps [ 0 ] - - ;
}
}
void flush_thread ( void )
{
struct thread_info * t = current_thread_info ( ) ;
2006-02-01 05:29:18 +03:00
struct mm_struct * mm ;
2005-04-17 02:20:36 +04:00
2006-02-01 05:29:18 +03:00
mm = t - > task - > mm ;
if ( mm )
2006-02-01 05:31:20 +03:00
tsb_context_switch ( mm ) ;
2005-04-17 02:20:36 +04:00
set_thread_wsaved ( 0 ) ;
/* Clear FPU register state. */
t - > fpsaved [ 0 ] = 0 ;
}
/* It's a bit more tricky when 64-bit tasks are involved... */
static unsigned long clone_stackframe ( unsigned long csp , unsigned long psp )
{
unsigned long fp , distance , rval ;
if ( ! ( test_thread_flag ( TIF_32BIT ) ) ) {
csp + = STACK_BIAS ;
psp + = STACK_BIAS ;
__get_user ( fp , & ( ( ( struct reg_window __user * ) psp ) - > ins [ 6 ] ) ) ;
fp + = STACK_BIAS ;
} else
__get_user ( fp , & ( ( ( struct reg_window32 __user * ) psp ) - > ins [ 6 ] ) ) ;
2010-02-10 03:18:40 +03:00
/* Now align the stack as this is mandatory in the Sparc ABI
* due to how register windows work . This hides the
* restriction from thread libraries etc .
2005-04-17 02:20:36 +04:00
*/
2010-02-10 03:18:40 +03:00
csp & = ~ 15UL ;
2005-04-17 02:20:36 +04:00
distance = fp - psp ;
rval = ( csp - distance ) ;
if ( copy_in_user ( ( void __user * ) rval , ( void __user * ) psp , distance ) )
rval = 0 ;
else if ( test_thread_flag ( TIF_32BIT ) ) {
if ( put_user ( ( ( u32 ) csp ) ,
& ( ( ( struct reg_window32 __user * ) rval ) - > ins [ 6 ] ) ) )
rval = 0 ;
} else {
if ( put_user ( ( ( u64 ) csp - STACK_BIAS ) ,
& ( ( ( struct reg_window __user * ) rval ) - > ins [ 6 ] ) ) )
rval = 0 ;
else
rval = rval - STACK_BIAS ;
}
return rval ;
}
/* Standard stuff. */
static inline void shift_window_buffer ( int first_win , int last_win ,
struct thread_info * t )
{
int i ;
for ( i = first_win ; i < last_win ; i + + ) {
t - > rwbuf_stkptrs [ i ] = t - > rwbuf_stkptrs [ i + 1 ] ;
memcpy ( & t - > reg_window [ i ] , & t - > reg_window [ i + 1 ] ,
sizeof ( struct reg_window ) ) ;
}
}
void synchronize_user_stack ( void )
{
struct thread_info * t = current_thread_info ( ) ;
unsigned long window ;
flush_user_windows ( ) ;
if ( ( window = get_thread_wsaved ( ) ) ! = 0 ) {
int winsize = sizeof ( struct reg_window ) ;
int bias = 0 ;
if ( test_thread_flag ( TIF_32BIT ) )
winsize = sizeof ( struct reg_window32 ) ;
else
bias = STACK_BIAS ;
window - = 1 ;
do {
unsigned long sp = ( t - > rwbuf_stkptrs [ window ] + bias ) ;
struct reg_window * rwin = & t - > reg_window [ window ] ;
if ( ! copy_to_user ( ( char __user * ) sp , rwin , winsize ) ) {
shift_window_buffer ( window , get_thread_wsaved ( ) - 1 , t ) ;
set_thread_wsaved ( get_thread_wsaved ( ) - 1 ) ;
}
} while ( window - - ) ;
}
}
2006-02-04 11:10:01 +03:00
static void stack_unaligned ( unsigned long sp )
{
siginfo_t info ;
info . si_signo = SIGBUS ;
info . si_errno = 0 ;
info . si_code = BUS_ADRALN ;
info . si_addr = ( void __user * ) sp ;
info . si_trapno = 0 ;
force_sig_info ( SIGBUS , & info , current ) ;
}
2005-04-17 02:20:36 +04:00
void fault_in_user_windows ( void )
{
struct thread_info * t = current_thread_info ( ) ;
unsigned long window ;
int winsize = sizeof ( struct reg_window ) ;
int bias = 0 ;
if ( test_thread_flag ( TIF_32BIT ) )
winsize = sizeof ( struct reg_window32 ) ;
else
bias = STACK_BIAS ;
flush_user_windows ( ) ;
window = get_thread_wsaved ( ) ;
2006-02-04 11:10:01 +03:00
if ( likely ( window ! = 0 ) ) {
2005-04-17 02:20:36 +04:00
window - = 1 ;
do {
unsigned long sp = ( t - > rwbuf_stkptrs [ window ] + bias ) ;
struct reg_window * rwin = & t - > reg_window [ window ] ;
2006-02-04 11:10:01 +03:00
if ( unlikely ( sp & 0x7UL ) )
stack_unaligned ( sp ) ;
if ( unlikely ( copy_to_user ( ( char __user * ) sp ,
rwin , winsize ) ) )
2005-04-17 02:20:36 +04:00
goto barf ;
} while ( window - - ) ;
}
set_thread_wsaved ( 0 ) ;
return ;
barf :
set_thread_wsaved ( window + 1 ) ;
do_exit ( SIGILL ) ;
}
asmlinkage long sparc_do_fork ( unsigned long clone_flags ,
unsigned long stack_start ,
struct pt_regs * regs ,
unsigned long stack_size )
{
int __user * parent_tid_ptr , * child_tid_ptr ;
2008-05-08 03:21:28 +04:00
unsigned long orig_i1 = regs - > u_regs [ UREG_I1 ] ;
long ret ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_COMPAT
if ( test_thread_flag ( TIF_32BIT ) ) {
parent_tid_ptr = compat_ptr ( regs - > u_regs [ UREG_I2 ] ) ;
child_tid_ptr = compat_ptr ( regs - > u_regs [ UREG_I4 ] ) ;
} else
# endif
{
parent_tid_ptr = ( int __user * ) regs - > u_regs [ UREG_I2 ] ;
child_tid_ptr = ( int __user * ) regs - > u_regs [ UREG_I4 ] ;
}
2008-05-08 03:21:28 +04:00
ret = do_fork ( clone_flags , stack_start ,
regs , stack_size ,
parent_tid_ptr , child_tid_ptr ) ;
/* If we get an error and potentially restart the system
* call , we ' re screwed because copy_thread ( ) clobbered
* the parent ' s % o1 . So detect that case and restore it
* here .
*/
if ( ( unsigned long ) ret > = - ERESTART_RESTARTBLOCK )
regs - > u_regs [ UREG_I1 ] = orig_i1 ;
return ret ;
2005-04-17 02:20:36 +04:00
}
/* Copy a Sparc thread. The fork() return value conventions
* under SunOS are nothing short of bletcherous :
* Parent - - > % o0 = = childs pid , % o1 = = 0
* Child - - > % o0 = = parents pid , % o1 = = 1
*/
2009-04-03 03:56:59 +04:00
int copy_thread ( unsigned long clone_flags , unsigned long sp ,
2005-04-17 02:20:36 +04:00
unsigned long unused ,
struct task_struct * p , struct pt_regs * regs )
{
2006-01-12 12:05:43 +03:00
struct thread_info * t = task_thread_info ( p ) ;
2008-05-22 05:14:28 +04:00
struct sparc_stackf * parent_sf ;
unsigned long child_stack_sz ;
2005-04-17 02:20:36 +04:00
char * child_trap_frame ;
2008-05-22 05:14:28 +04:00
int kernel_thread ;
2005-04-17 02:20:36 +04:00
2008-05-22 05:14:28 +04:00
kernel_thread = ( regs - > tstate & TSTATE_PRIV ) ? 1 : 0 ;
parent_sf = ( ( struct sparc_stackf * ) regs ) - 1 ;
2005-04-17 02:20:36 +04:00
2008-05-22 05:14:28 +04:00
/* Calculate offset to stack_frame & pt_regs */
child_stack_sz = ( ( STACKFRAME_SZ + TRACEREG_SZ ) +
( kernel_thread ? STACKFRAME_SZ : 0 ) ) ;
child_trap_frame = ( task_stack_page ( p ) +
( THREAD_SIZE - child_stack_sz ) ) ;
memcpy ( child_trap_frame , parent_sf , child_stack_sz ) ;
t - > flags = ( t - > flags & ~ ( ( 0xffUL < < TI_FLAG_CWP_SHIFT ) |
( 0xffUL < < TI_FLAG_CURRENT_DS_SHIFT ) ) ) |
2005-04-17 02:20:36 +04:00
( ( ( regs - > tstate + 1 ) & TSTATE_CWP ) < < TI_FLAG_CWP_SHIFT ) ;
2005-07-25 06:36:26 +04:00
t - > new_child = 1 ;
2005-04-17 02:20:36 +04:00
t - > ksp = ( ( unsigned long ) child_trap_frame ) - STACK_BIAS ;
2008-05-22 05:14:28 +04:00
t - > kregs = ( struct pt_regs * ) ( child_trap_frame +
sizeof ( struct sparc_stackf ) ) ;
2005-04-17 02:20:36 +04:00
t - > fpsaved [ 0 ] = 0 ;
2008-05-22 05:14:28 +04:00
if ( kernel_thread ) {
struct sparc_stackf * child_sf = ( struct sparc_stackf * )
( child_trap_frame + ( STACKFRAME_SZ + TRACEREG_SZ ) ) ;
/* Zero terminate the stack backtrace. */
child_sf - > fp = NULL ;
t - > kregs - > u_regs [ UREG_FP ] =
( ( unsigned long ) child_sf ) - STACK_BIAS ;
2005-04-17 02:20:36 +04:00
t - > flags | = ( ( long ) ASI_P < < TI_FLAG_CURRENT_DS_SHIFT ) ;
t - > kregs - > u_regs [ UREG_G6 ] = ( unsigned long ) t ;
t - > kregs - > u_regs [ UREG_G4 ] = ( unsigned long ) t - > task ;
} else {
if ( t - > flags & _TIF_32BIT ) {
sp & = 0x00000000ffffffffUL ;
regs - > u_regs [ UREG_FP ] & = 0x00000000ffffffffUL ;
}
t - > kregs - > u_regs [ UREG_FP ] = sp ;
t - > flags | = ( ( long ) ASI_AIUS < < TI_FLAG_CURRENT_DS_SHIFT ) ;
if ( sp ! = regs - > u_regs [ UREG_FP ] ) {
unsigned long csp ;
csp = clone_stackframe ( sp , regs - > u_regs [ UREG_FP ] ) ;
if ( ! csp )
return - EFAULT ;
t - > kregs - > u_regs [ UREG_FP ] = csp ;
}
if ( t - > utraps )
t - > utraps [ 0 ] + + ;
}
/* Set the return value for the child. */
t - > kregs - > u_regs [ UREG_I0 ] = current - > pid ;
t - > kregs - > u_regs [ UREG_I1 ] = 1 ;
/* Set the second return value for the parent. */
regs - > u_regs [ UREG_I1 ] = 0 ;
if ( clone_flags & CLONE_SETTLS )
t - > kregs - > u_regs [ UREG_G7 ] = regs - > u_regs [ UREG_I3 ] ;
return 0 ;
}
/*
* 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
2007-05-12 00:52:08 +04:00
* not be freed until both the parent and the child have exited .
2005-04-17 02:20:36 +04:00
*/
pid_t kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{
long retval ;
/* If the parent runs before fn(arg) is called by the child,
* the input registers of this function can be clobbered .
* So we stash ' fn ' and ' arg ' into global registers which
* will not be modified by the parent .
*/
__asm__ __volatile__ ( " mov %4, %%g2 \n \t " /* Save FN into global */
" mov %5, %%g3 \n \t " /* Save ARG into global */
" mov %1, %%g1 \n \t " /* Clone syscall nr. */
" mov %2, %%o0 \n \t " /* Clone flags. */
" mov 0, %%o1 \n \t " /* usp arg == 0 */
" t 0x6d \n \t " /* Linux/Sparc clone(). */
" brz,a,pn %%o1, 1f \n \t " /* Parent, just return. */
" mov %%o0, %0 \n \t "
" jmpl %%g2, %%o7 \n \t " /* Call the function. */
" mov %%g3, %%o0 \n \t " /* Set arg in delay. */
" mov %3, %%g1 \n \t "
" t 0x6d \n \t " /* Linux/Sparc exit(). */
/* Notreached by child. */
" 1: " :
" =r " ( retval ) :
" i " ( __NR_clone ) , " r " ( flags | CLONE_VM | CLONE_UNTRACED ) ,
" i " ( __NR_exit ) , " r " ( fn ) , " r " ( arg ) :
" g1 " , " g2 " , " g3 " , " o0 " , " o1 " , " memory " , " cc " ) ;
return retval ;
}
2009-01-09 03:58:20 +03:00
EXPORT_SYMBOL ( kernel_thread ) ;
2005-04-17 02:20:36 +04:00
typedef struct {
union {
unsigned int pr_regs [ 32 ] ;
unsigned long pr_dregs [ 16 ] ;
} pr_fr ;
unsigned int __unused ;
unsigned int pr_fsr ;
unsigned char pr_qcnt ;
unsigned char pr_q_entrysize ;
unsigned char pr_en ;
unsigned int pr_q [ 64 ] ;
} elf_fpregset_t32 ;
/*
* fill in the fpu structure for a core dump .
*/
int dump_fpu ( struct pt_regs * regs , elf_fpregset_t * fpregs )
{
unsigned long * kfpregs = current_thread_info ( ) - > fpregs ;
unsigned long fprs = current_thread_info ( ) - > fpsaved [ 0 ] ;
if ( test_thread_flag ( TIF_32BIT ) ) {
elf_fpregset_t32 * fpregs32 = ( elf_fpregset_t32 * ) fpregs ;
if ( fprs & FPRS_DL )
memcpy ( & fpregs32 - > pr_fr . pr_regs [ 0 ] , kfpregs ,
sizeof ( unsigned int ) * 32 ) ;
else
memset ( & fpregs32 - > pr_fr . pr_regs [ 0 ] , 0 ,
sizeof ( unsigned int ) * 32 ) ;
fpregs32 - > pr_qcnt = 0 ;
fpregs32 - > pr_q_entrysize = 8 ;
memset ( & fpregs32 - > pr_q [ 0 ] , 0 ,
( sizeof ( unsigned int ) * 64 ) ) ;
if ( fprs & FPRS_FEF ) {
fpregs32 - > pr_fsr = ( unsigned int ) current_thread_info ( ) - > xfsr [ 0 ] ;
fpregs32 - > pr_en = 1 ;
} else {
fpregs32 - > pr_fsr = 0 ;
fpregs32 - > pr_en = 0 ;
}
} else {
if ( fprs & FPRS_DL )
memcpy ( & fpregs - > pr_regs [ 0 ] , kfpregs ,
sizeof ( unsigned int ) * 32 ) ;
else
memset ( & fpregs - > pr_regs [ 0 ] , 0 ,
sizeof ( unsigned int ) * 32 ) ;
if ( fprs & FPRS_DU )
memcpy ( & fpregs - > pr_regs [ 16 ] , kfpregs + 16 ,
sizeof ( unsigned int ) * 32 ) ;
else
memset ( & fpregs - > pr_regs [ 16 ] , 0 ,
sizeof ( unsigned int ) * 32 ) ;
if ( fprs & FPRS_FEF ) {
fpregs - > pr_fsr = current_thread_info ( ) - > xfsr [ 0 ] ;
fpregs - > pr_gsr = current_thread_info ( ) - > gsr [ 0 ] ;
} else {
fpregs - > pr_fsr = fpregs - > pr_gsr = 0 ;
}
fpregs - > pr_fprs = fprs ;
}
return 1 ;
}
2009-01-09 03:58:20 +03:00
EXPORT_SYMBOL ( dump_fpu ) ;
2005-04-17 02:20:36 +04:00
/*
* sparc_execve ( ) executes a new program after the asm stub has set
* things up for us . This should basically do what I want it to .
*/
asmlinkage int sparc_execve ( struct pt_regs * regs )
{
int error , base = 0 ;
char * filename ;
/* User register window flush is done by entry.S */
/* Check for indirect call. */
if ( regs - > u_regs [ UREG_G1 ] = = 0 )
base = 1 ;
filename = getname ( ( char __user * ) regs - > u_regs [ base + UREG_I0 ] ) ;
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
error = do_execve ( filename ,
2010-08-18 02:52:56 +04:00
( const char __user * const __user * )
2005-04-17 02:20:36 +04:00
regs - > u_regs [ base + UREG_I1 ] ,
2010-08-18 02:52:56 +04:00
( const char __user * const __user * )
2005-04-17 02:20:36 +04:00
regs - > u_regs [ base + UREG_I2 ] , regs ) ;
putname ( filename ) ;
if ( ! error ) {
fprs_write ( 0 ) ;
current_thread_info ( ) - > xfsr [ 0 ] = 0 ;
current_thread_info ( ) - > fpsaved [ 0 ] = 0 ;
regs - > tstate & = ~ TSTATE_PEF ;
}
out :
return error ;
}
unsigned long get_wchan ( struct task_struct * task )
{
unsigned long pc , fp , bias = 0 ;
2008-08-13 05:33:56 +04:00
struct thread_info * tp ;
2005-04-17 02:20:36 +04:00
struct reg_window * rw ;
unsigned long ret = 0 ;
int count = 0 ;
if ( ! task | | task = = current | |
task - > state = = TASK_RUNNING )
goto out ;
2008-08-13 05:33:56 +04:00
tp = task_thread_info ( task ) ;
2005-04-17 02:20:36 +04:00
bias = STACK_BIAS ;
2006-01-12 12:05:42 +03:00
fp = task_thread_info ( task ) - > ksp + bias ;
2005-04-17 02:20:36 +04:00
do {
2008-08-13 05:33:56 +04:00
if ( ! kstack_valid ( tp , fp ) )
2005-04-17 02:20:36 +04:00
break ;
rw = ( struct reg_window * ) fp ;
pc = rw - > ins [ 7 ] ;
if ( ! in_sched_functions ( pc ) ) {
ret = pc ;
goto out ;
}
fp = rw - > ins [ 6 ] + bias ;
} while ( + + count < 16 ) ;
out :
return ret ;
}