2005-04-17 02:20:36 +04:00
/*
2011-03-22 06:39:27 +03:00
* linux / arch / m68k / kernel / process . c
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1995 Hamish Macdonald
*
* 68060 fixes by Jesper Skov
*/
/*
* This file handles the architecture - dependent parts of process handling . .
*/
# include <linux/errno.h>
2011-03-22 06:39:27 +03:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
2011-03-22 06:39:27 +03:00
# include <linux/slab.h>
# include <linux/fs.h>
2005-04-17 02:20:36 +04:00
# include <linux/smp.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/ptrace.h>
# include <linux/user.h>
# include <linux/reboot.h>
2011-03-22 06:39:27 +03:00
# include <linux/init_task.h>
# include <linux/mqueue.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/system.h>
# include <asm/traps.h>
# include <asm/machdep.h>
# include <asm/setup.h>
# include <asm/pgtable.h>
2011-03-22 06:39:27 +03:00
/*
* Initial task / thread structure . Make this a per - architecture thing ,
* because different architectures tend to have different
* alignment requirements and potentially different initial
* setup .
*/
static struct signal_struct init_signals = INIT_SIGNALS ( init_signals ) ;
static struct sighand_struct init_sighand = INIT_SIGHAND ( init_sighand ) ;
union thread_union init_thread_union __init_task_data
__attribute__ ( ( aligned ( THREAD_SIZE ) ) ) =
{ INIT_THREAD_INFO ( init_task ) } ;
/* initial task structure */
struct task_struct init_task = INIT_TASK ( init_task ) ;
EXPORT_SYMBOL ( init_task ) ;
2005-04-17 02:20:36 +04:00
asmlinkage void ret_from_fork ( void ) ;
2011-03-22 06:39:27 +03:00
2006-02-08 02:19:17 +03:00
/*
2011-03-22 06:39:27 +03:00
* Return saved PC from a blocked thread
2006-02-08 02:19:17 +03:00
*/
2011-03-22 06:39:27 +03:00
unsigned long thread_saved_pc ( struct task_struct * tsk )
{
struct switch_stack * sw = ( struct switch_stack * ) tsk - > thread . ksp ;
/* Check whether the thread is blocked in resume() */
if ( in_sched_functions ( sw - > retpc ) )
return ( ( unsigned long * ) sw - > a6 ) [ 1 ] ;
else
return sw - > retpc ;
}
2005-04-17 02:20:36 +04:00
/*
2011-03-22 06:39:27 +03:00
* The idle loop on an m68k . .
2005-04-17 02:20:36 +04:00
*/
2006-03-24 14:15:57 +03:00
static void default_idle ( void )
2005-04-17 02:20:36 +04:00
{
2011-03-22 06:39:27 +03:00
if ( ! need_resched ( ) )
# if defined(MACH_ATARI_ONLY)
/* block out HSYNC on the atari (falcon) */
__asm__ ( " stop #0x2200 " : : : " cc " ) ;
# else
__asm__ ( " stop #0x2000 " : : : " cc " ) ;
# endif
2005-04-17 02:20:36 +04: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-06-03 05:35:20 +04:00
while ( 1 ) {
2011-03-22 06:39:27 +03:00
while ( ! need_resched ( ) )
idle ( ) ;
2005-06-03 05:35:20 +04:00
preempt_enable_no_resched ( ) ;
schedule ( ) ;
preempt_disable ( ) ;
}
2005-04-17 02:20:36 +04:00
}
void machine_restart ( char * __unused )
{
if ( mach_reset )
mach_reset ( ) ;
for ( ; ; ) ;
}
void machine_halt ( void )
{
if ( mach_halt )
mach_halt ( ) ;
for ( ; ; ) ;
}
void machine_power_off ( void )
{
if ( mach_power_off )
mach_power_off ( ) ;
for ( ; ; ) ;
}
2011-03-22 06:39:27 +03:00
void ( * pm_power_off ) ( void ) = machine_power_off ;
EXPORT_SYMBOL ( pm_power_off ) ;
2005-04-17 02:20:36 +04:00
void show_regs ( struct pt_regs * regs )
{
2011-03-22 06:39:27 +03:00
printk ( " \n " ) ;
printk ( " Format %02x Vector: %04x PC: %08lx Status: %04x %s \n " ,
2005-04-17 02:20:36 +04:00
regs - > format , regs - > vector , regs - > pc , regs - > sr , print_tainted ( ) ) ;
2011-03-22 06:39:27 +03:00
printk ( " ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx \n " ,
2005-04-17 02:20:36 +04:00
regs - > orig_d0 , regs - > d0 , regs - > a2 , regs - > a1 ) ;
2011-03-22 06:39:27 +03:00
printk ( " A0: %08lx D5: %08lx D4: %08lx \n " ,
2005-04-17 02:20:36 +04:00
regs - > a0 , regs - > d5 , regs - > d4 ) ;
2011-03-22 06:39:27 +03:00
printk ( " D3: %08lx D2: %08lx D1: %08lx \n " ,
2005-04-17 02:20:36 +04:00
regs - > d3 , regs - > d2 , regs - > d1 ) ;
if ( ! ( regs - > sr & PS_S ) )
2011-03-22 06:39:27 +03:00
printk ( " USP: %08lx \n " , rdusp ( ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Create a kernel thread
*/
int kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{
2011-03-22 06:39:27 +03:00
int pid ;
2005-04-17 02:20:36 +04:00
mm_segment_t fs ;
fs = get_fs ( ) ;
2011-03-22 06:39:27 +03:00
set_fs ( KERNEL_DS ) ;
{
register long retval __asm__ ( " d0 " ) ;
register long clone_arg __asm__ ( " d1 " ) = flags | CLONE_VM | CLONE_UNTRACED ;
retval = __NR_clone ;
__asm__ __volatile__
( " clrl %%d2 \n \t "
" trap #0 \n \t " /* Linux/m68k system call */
" tstl %0 \n \t " /* child or parent */
" jne 1f \n \t " /* parent - jump */
" lea %%sp@(%c7),%6 \n \t " /* reload current */
" movel %6@,%6 \n \t "
" movel %3,%%sp@- \n \t " /* push argument */
" jsr %4@ \n \t " /* call fn */
" movel %0,%%d1 \n \t " /* pass exit value */
" movel %2,%%d0 \n \t " /* exit */
" trap #0 \n "
" 1: "
: " +d " ( retval )
: " i " ( __NR_clone ) , " i " ( __NR_exit ) ,
" r " ( arg ) , " a " ( fn ) , " d " ( clone_arg ) , " r " ( current ) ,
" i " ( - THREAD_SIZE )
: " d2 " ) ;
pid = retval ;
}
set_fs ( fs ) ;
return pid ;
2005-04-17 02:20:36 +04:00
}
2011-03-22 06:39:27 +03:00
EXPORT_SYMBOL ( kernel_thread ) ;
2005-04-17 02:20:36 +04:00
void flush_thread ( void )
{
unsigned long zero = 0 ;
2011-07-26 04:12:42 +04:00
2005-04-17 02:20:36 +04:00
current - > thread . fs = __USER_DS ;
if ( ! FPU_IS_EMU )
asm volatile ( " .chip 68k/68881 \n \t "
" frestore %0@ \n \t "
" .chip 68k " : : " a " ( & zero ) ) ;
}
/*
* " m68k_fork() " . . By the time we get here , the
* non - volatile registers have also been saved on the
* stack . We do some ugly pointer stuff here . . ( see
* also copy_thread )
*/
asmlinkage int m68k_fork ( struct pt_regs * regs )
{
2011-03-22 06:39:27 +03:00
return do_fork ( SIGCHLD , rdusp ( ) , regs , 0 , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
}
asmlinkage int m68k_vfork ( struct pt_regs * regs )
{
2011-03-22 06:39:27 +03:00
return do_fork ( CLONE_VFORK | CLONE_VM | SIGCHLD , rdusp ( ) , regs , 0 ,
NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
}
asmlinkage int m68k_clone ( struct pt_regs * regs )
{
unsigned long clone_flags ;
unsigned long newsp ;
2011-03-22 06:39:27 +03:00
int __user * parent_tidptr , * child_tidptr ;
2005-04-17 02:20:36 +04:00
/* syscall2 puts clone_flags in d1 and usp in d2 */
clone_flags = regs - > d1 ;
newsp = regs - > d2 ;
2011-03-22 06:39:27 +03:00
parent_tidptr = ( int __user * ) regs - > d3 ;
child_tidptr = ( int __user * ) regs - > d4 ;
2005-04-17 02:20:36 +04:00
if ( ! newsp )
newsp = rdusp ( ) ;
2011-03-22 06:39:27 +03:00
return do_fork ( clone_flags , newsp , regs , 0 ,
parent_tidptr , child_tidptr ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-22 06:39:27 +03:00
int copy_thread ( unsigned long clone_flags , unsigned long usp ,
unsigned long unused ,
struct task_struct * p , struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
struct pt_regs * childregs ;
struct switch_stack * childstack , * stack ;
2006-01-12 12:05:54 +03:00
unsigned long * retp ;
2005-04-17 02:20:36 +04:00
2006-01-12 12:05:54 +03:00
childregs = ( struct pt_regs * ) ( task_stack_page ( p ) + THREAD_SIZE ) - 1 ;
2005-04-17 02:20:36 +04:00
* childregs = * regs ;
childregs - > d0 = 0 ;
retp = ( ( unsigned long * ) regs ) ;
stack = ( ( struct switch_stack * ) retp ) - 1 ;
childstack = ( ( struct switch_stack * ) childregs ) - 1 ;
* childstack = * stack ;
childstack - > retpc = ( unsigned long ) ret_from_fork ;
p - > thread . usp = usp ;
p - > thread . ksp = ( unsigned long ) childstack ;
2009-12-06 21:08:14 +03:00
if ( clone_flags & CLONE_SETTLS )
task_thread_info ( p ) - > tp_value = regs - > d5 ;
2005-04-17 02:20:36 +04:00
/*
* Must save the current SFC / DFC value , NOT the value when
* the parent was last descheduled - RGH 10 - 08 - 96
*/
p - > thread . fs = get_fs ( ) . seg ;
if ( ! FPU_IS_EMU ) {
/* Copy the current fpu state */
asm volatile ( " fsave %0 " : : " m " ( p - > thread . fpstate [ 0 ] ) : " memory " ) ;
2011-03-22 06:39:27 +03:00
if ( ! CPU_IS_060 ? p - > thread . fpstate [ 0 ] : p - > thread . fpstate [ 2 ] )
2005-04-17 02:20:36 +04:00
asm volatile ( " fmovemx %/fp0-%/fp7,%0 \n \t "
" fmoveml %/fpiar/%/fpcr/%/fpsr,%1 "
: : " m " ( p - > thread . fp [ 0 ] ) , " m " ( p - > thread . fpcntl [ 0 ] )
: " memory " ) ;
/* Restore the state in case the fpu was busy */
asm volatile ( " frestore %0 " : : " m " ( p - > thread . fpstate [ 0 ] ) ) ;
}
return 0 ;
}
/* Fill in the fpu structure for a core dump. */
2011-03-22 06:39:27 +03:00
int dump_fpu ( struct pt_regs * regs , struct user_m68kfp_struct * fpu )
2005-04-17 02:20:36 +04:00
{
char fpustate [ 216 ] ;
if ( FPU_IS_EMU ) {
int i ;
memcpy ( fpu - > fpcntl , current - > thread . fpcntl , 12 ) ;
memcpy ( fpu - > fpregs , current - > thread . fp , 96 ) ;
/* Convert internal fpu reg representation
* into long double format
*/
for ( i = 0 ; i < 24 ; i + = 3 )
fpu - > fpregs [ i ] = ( ( fpu - > fpregs [ i ] & 0xffff0000 ) < < 15 ) |
( ( fpu - > fpregs [ i ] & 0x0000ffff ) < < 16 ) ;
return 1 ;
}
/* First dump the fpu context to avoid protocol violation. */
asm volatile ( " fsave %0 " : : " m " ( fpustate [ 0 ] ) : " memory " ) ;
2011-03-22 06:39:27 +03:00
if ( ! CPU_IS_060 ? ! fpustate [ 0 ] : ! fpustate [ 2 ] )
2005-04-17 02:20:36 +04:00
return 0 ;
asm volatile ( " fmovem %/fpiar/%/fpcr/%/fpsr,%0 "
: : " m " ( fpu - > fpcntl [ 0 ] )
: " memory " ) ;
asm volatile ( " fmovemx %/fp0-%/fp7,%0 "
: : " m " ( fpu - > fpregs [ 0 ] )
: " memory " ) ;
return 1 ;
}
2011-03-22 06:39:27 +03:00
EXPORT_SYMBOL ( dump_fpu ) ;
2005-04-17 02:20:36 +04:00
/*
* sys_execve ( ) executes a new program .
*/
2011-03-22 06:39:27 +03:00
asmlinkage int sys_execve ( const char __user * name ,
const char __user * const __user * argv ,
const char __user * const __user * envp )
2005-04-17 02:20:36 +04:00
{
int error ;
char * filename ;
struct pt_regs * regs = ( struct pt_regs * ) & name ;
filename = getname ( name ) ;
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
2009-10-13 01:37:28 +04:00
return error ;
2005-04-17 02:20:36 +04:00
error = do_execve ( filename , argv , envp , regs ) ;
putname ( filename ) ;
return error ;
}
unsigned long get_wchan ( struct task_struct * p )
{
unsigned long fp , pc ;
unsigned long stack_page ;
int count = 0 ;
if ( ! p | | p = = current | | p - > state = = TASK_RUNNING )
return 0 ;
2011-03-22 06:39:27 +03:00
stack_page = ( unsigned long ) task_stack_page ( p ) ;
2005-04-17 02:20:36 +04:00
fp = ( ( struct switch_stack * ) p - > thread . ksp ) - > a6 ;
do {
if ( fp < stack_page + sizeof ( struct thread_info ) | |
2011-03-22 06:39:27 +03:00
fp > = 8184 + stack_page )
2005-04-17 02:20:36 +04:00
return 0 ;
pc = ( ( unsigned long * ) fp ) [ 1 ] ;
if ( ! in_sched_functions ( pc ) )
return pc ;
fp = * ( unsigned long * ) fp ;
} while ( count + + < 16 ) ;
return 0 ;
}