2005-04-17 02:20:36 +04:00
/*
* 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 .
*
* Copyright ( C ) 1995 , 1996 , 1997 , 2000 , 2001 , 05 by Ralf Baechle
* Copyright ( C ) 1999 , 2000 Silicon Graphics , Inc .
* Copyright ( C ) 2001 MIPS Technologies , Inc .
*/
2006-01-11 23:17:48 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/linkage.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/mman.h>
# include <linux/ptrace.h>
# include <linux/sched.h>
# include <linux/string.h>
# include <linux/syscalls.h>
# include <linux/file.h>
# include <linux/slab.h>
# include <linux/utsname.h>
# include <linux/unistd.h>
# include <linux/sem.h>
# include <linux/msg.h>
# include <linux/shm.h>
# include <linux/compiler.h>
2005-03-08 17:39:39 +03:00
# include <linux/module.h>
2007-10-17 10:29:24 +04:00
# include <linux/ipc.h>
2009-09-17 04:25:05 +04:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
2009-09-17 04:25:05 +04:00
# include <asm/asm.h>
2005-04-17 02:20:36 +04:00
# include <asm/branch.h>
# include <asm/cachectl.h>
# include <asm/cacheflush.h>
2005-09-10 00:32:31 +04:00
# include <asm/asm-offsets.h>
2005-04-17 02:20:36 +04:00
# include <asm/signal.h>
# include <asm/sim.h>
# include <asm/shmparam.h>
# include <asm/sysmips.h>
# include <asm/uaccess.h>
2008-07-20 16:16:46 +04:00
/*
* For historic reasons the pipe ( 2 ) syscall on MIPS has an unusual calling
* convention . It returns results in registers $ v0 / $ v1 which means there
* is no need for it to do verify the validity of a userspace pointer
* argument . Historically that used to be expensive in Linux . These days
* the performance advantage is negligible .
*/
asmlinkage int sysm_pipe ( nabi_no_regargs volatile struct pt_regs regs )
2005-04-17 02:20:36 +04:00
{
int fd [ 2 ] ;
int error , res ;
2008-07-24 08:29:30 +04:00
error = do_pipe_flags ( fd , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
res = error ;
goto out ;
}
regs . regs [ 3 ] = fd [ 1 ] ;
res = fd [ 0 ] ;
out :
return res ;
}
unsigned long shm_align_mask = PAGE_SIZE - 1 ; /* Sane caches */
2005-03-08 17:39:39 +03:00
EXPORT_SYMBOL ( shm_align_mask ) ;
2005-04-17 02:20:36 +04:00
# define COLOUR_ALIGN(addr,pgoff) \
( ( ( ( addr ) + shm_align_mask ) & ~ shm_align_mask ) + \
( ( ( pgoff ) < < PAGE_SHIFT ) & shm_align_mask ) )
unsigned long arch_get_unmapped_area ( struct file * filp , unsigned long addr ,
unsigned long len , unsigned long pgoff , unsigned long flags )
{
struct vm_area_struct * vmm ;
int do_color_align ;
unsigned long task_size ;
task_size = STACK_TOP ;
2007-10-28 09:10:20 +03:00
if ( len > task_size )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
if ( flags & MAP_FIXED ) {
2007-10-28 09:10:20 +03:00
/* Even MAP_FIXED mappings must reside within task_size. */
if ( task_size - len < addr )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/*
* We do not accept a shared mapping if it would violate
* cache aliasing constraints .
*/
2009-12-05 23:10:44 +03:00
if ( ( flags & MAP_SHARED ) & &
( ( addr - ( pgoff < < PAGE_SHIFT ) ) & shm_align_mask ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
return addr ;
}
do_color_align = 0 ;
if ( filp | | ( flags & MAP_SHARED ) )
do_color_align = 1 ;
if ( addr ) {
if ( do_color_align )
addr = COLOUR_ALIGN ( addr , pgoff ) ;
else
addr = PAGE_ALIGN ( addr ) ;
vmm = find_vma ( current - > mm , addr ) ;
if ( task_size - len > = addr & &
( ! vmm | | addr + len < = vmm - > vm_start ) )
return addr ;
}
addr = TASK_UNMAPPED_BASE ;
if ( do_color_align )
addr = COLOUR_ALIGN ( addr , pgoff ) ;
else
addr = PAGE_ALIGN ( addr ) ;
for ( vmm = find_vma ( current - > mm , addr ) ; ; vmm = vmm - > vm_next ) {
/* At this point: (!vmm || addr < vmm->vm_end). */
if ( task_size - len < addr )
return - ENOMEM ;
if ( ! vmm | | addr + len < = vmm - > vm_start )
return addr ;
addr = vmm - > vm_end ;
if ( do_color_align )
addr = COLOUR_ALIGN ( addr , pgoff ) ;
}
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( mips_mmap , unsigned long , addr , unsigned long , len ,
unsigned long , prot , unsigned long , flags , unsigned long ,
fd , off_t , offset )
2005-04-17 02:20:36 +04:00
{
unsigned long result ;
result = - EINVAL ;
if ( offset & ~ PAGE_MASK )
goto out ;
2009-12-01 01:37:04 +03:00
result = sys_mmap_pgoff ( addr , len , prot , flags , fd , offset > > PAGE_SHIFT ) ;
2005-04-17 02:20:36 +04:00
out :
return result ;
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( mips_mmap2 , unsigned long , addr , unsigned long , len ,
unsigned long , prot , unsigned long , flags , unsigned long , fd ,
unsigned long , pgoff )
2005-04-17 02:20:36 +04:00
{
2006-02-25 08:20:29 +03:00
if ( pgoff & ( ~ PAGE_MASK > > 12 ) )
return - EINVAL ;
2009-12-01 01:37:04 +03:00
return sys_mmap_pgoff ( addr , len , prot , flags , fd , pgoff > > ( PAGE_SHIFT - 12 ) ) ;
2005-04-17 02:20:36 +04:00
}
save_static_function ( sys_fork ) ;
2007-07-22 12:01:39 +04:00
static int __used noinline
2005-04-17 02:20:36 +04:00
_sys_fork ( nabi_no_regargs struct pt_regs regs )
{
return do_fork ( SIGCHLD , regs . regs [ 29 ] , & regs , 0 , NULL , NULL ) ;
}
save_static_function ( sys_clone ) ;
2007-07-22 12:01:39 +04:00
static int __used noinline
2005-04-17 02:20:36 +04:00
_sys_clone ( nabi_no_regargs struct pt_regs regs )
{
unsigned long clone_flags ;
unsigned long newsp ;
2005-04-13 21:43:59 +04:00
int __user * parent_tidptr , * child_tidptr ;
2005-04-17 02:20:36 +04:00
clone_flags = regs . regs [ 4 ] ;
newsp = regs . regs [ 5 ] ;
if ( ! newsp )
newsp = regs . regs [ 29 ] ;
2005-04-13 21:43:59 +04:00
parent_tidptr = ( int __user * ) regs . regs [ 6 ] ;
# ifdef CONFIG_32BIT
/* We need to fetch the fifth argument off the stack. */
child_tidptr = NULL ;
if ( clone_flags & ( CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID ) ) {
int __user * __user * usp = ( int __user * __user * ) regs . regs [ 29 ] ;
if ( regs . regs [ 2 ] = = __NR_syscall ) {
if ( get_user ( child_tidptr , & usp [ 5 ] ) )
return - EFAULT ;
}
else if ( get_user ( child_tidptr , & usp [ 4 ] ) )
return - EFAULT ;
}
# else
child_tidptr = ( int __user * ) regs . regs [ 8 ] ;
# endif
2005-04-17 02:20:36 +04:00
return do_fork ( clone_flags , newsp , & regs , 0 ,
parent_tidptr , child_tidptr ) ;
}
/*
* sys_execve ( ) executes a new program .
*/
asmlinkage int sys_execve ( nabi_no_regargs struct pt_regs regs )
{
int error ;
char * filename ;
2006-02-08 17:39:49 +03:00
filename = getname ( ( char __user * ) ( long ) regs . regs [ 4 ] ) ;
2005-04-17 02:20:36 +04:00
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
2006-02-08 17:39:49 +03:00
error = do_execve ( filename , ( char __user * __user * ) ( long ) regs . regs [ 5 ] ,
( char __user * __user * ) ( long ) regs . regs [ 6 ] , & regs ) ;
2005-04-17 02:20:36 +04:00
putname ( filename ) ;
out :
return error ;
}
/*
* Compacrapability . . .
*/
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE1 ( uname , struct old_utsname __user * , name )
2005-04-17 02:20:36 +04:00
{
2006-10-02 13:18:11 +04:00
if ( name & & ! copy_to_user ( name , utsname ( ) , sizeof ( * name ) ) )
2005-04-17 02:20:36 +04:00
return 0 ;
return - EFAULT ;
}
/*
* Compacrapability . . .
*/
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE1 ( olduname , struct oldold_utsname __user * , name )
2005-04-17 02:20:36 +04:00
{
int error ;
if ( ! name )
return - EFAULT ;
2007-10-12 02:46:15 +04:00
if ( ! access_ok ( VERIFY_WRITE , name , sizeof ( struct oldold_utsname ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-10-02 13:18:11 +04:00
error = __copy_to_user ( & name - > sysname , & utsname ( ) - > sysname ,
__OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > sysname + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > nodename , & utsname ( ) - > nodename ,
__OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > nodename + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > release , & utsname ( ) - > release ,
__OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > release + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > version , & utsname ( ) - > version ,
__OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > version + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > machine , & utsname ( ) - > machine ,
__OLD_UTS_LEN ) ;
error = __put_user ( 0 , name - > machine + __OLD_UTS_LEN ) ;
2005-04-17 02:20:36 +04:00
error = error ? - EFAULT : 0 ;
return error ;
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE1 ( set_thread_area , unsigned long , addr )
2005-04-13 21:43:59 +04:00
{
2006-01-12 12:06:07 +03:00
struct thread_info * ti = task_thread_info ( current ) ;
2005-04-13 21:43:59 +04:00
ti - > tp_value = addr ;
2007-07-10 20:33:02 +04:00
if ( cpu_has_userlocal )
write_c0_userlocal ( addr ) ;
2006-09-19 20:18:53 +04:00
return 0 ;
2005-04-13 21:43:59 +04:00
}
2009-09-17 04:25:05 +04:00
static inline int mips_atomic_set ( struct pt_regs * regs ,
unsigned long addr , unsigned long new )
2005-04-17 02:20:36 +04:00
{
2009-09-17 04:25:05 +04:00
unsigned long old , tmp ;
unsigned int err ;
if ( unlikely ( addr & 3 ) )
return - EINVAL ;
if ( unlikely ( ! access_ok ( VERIFY_WRITE , addr , 4 ) ) )
return - EINVAL ;
if ( cpu_has_llsc & & R10000_LLSC_WAR ) {
__asm__ __volatile__ (
2009-12-02 14:33:03 +03:00
" .set mips3 \n "
2009-09-17 04:25:05 +04:00
" li %[err], 0 \n "
" 1: ll %[old], (%[addr]) \n "
" move %[tmp], %[new] \n "
" 2: sc %[tmp], (%[addr]) \n "
" beqzl %[tmp], 1b \n "
" 3: \n "
" .section .fixup, \" ax \" \n "
" 4: li %[err], %[efault] \n "
" j 3b \n "
" .previous \n "
" .section __ex_table, \" a \" \n "
" " STR ( PTR ) " 1b, 4b \n "
" " STR ( PTR ) " 2b, 4b \n "
" .previous \n "
2009-12-02 14:33:03 +03:00
" .set mips0 \n "
2009-09-17 04:25:05 +04:00
: [ old ] " =&r " ( old ) ,
[ err ] " =&r " ( err ) ,
[ tmp ] " =&r " ( tmp )
: [ addr ] " r " ( addr ) ,
[ new ] " r " ( new ) ,
[ efault ] " i " ( - EFAULT )
: " memory " ) ;
} else if ( cpu_has_llsc ) {
__asm__ __volatile__ (
2009-12-02 14:33:03 +03:00
" .set mips3 \n "
2009-09-17 04:25:05 +04:00
" li %[err], 0 \n "
" 1: ll %[old], (%[addr]) \n "
" move %[tmp], %[new] \n "
" 2: sc %[tmp], (%[addr]) \n "
" bnez %[tmp], 4f \n "
" 3: \n "
" .subsection 2 \n "
" 4: b 1b \n "
" .previous \n "
" \n "
" .section .fixup, \" ax \" \n "
" 5: li %[err], %[efault] \n "
" j 3b \n "
" .previous \n "
" .section __ex_table, \" a \" \n "
" " STR ( PTR ) " 1b, 5b \n "
" " STR ( PTR ) " 2b, 5b \n "
" .previous \n "
2009-12-02 14:33:03 +03:00
" .set mips0 \n "
2009-09-17 04:25:05 +04:00
: [ old ] " =&r " ( old ) ,
[ err ] " =&r " ( err ) ,
[ tmp ] " =&r " ( tmp )
: [ addr ] " r " ( addr ) ,
[ new ] " r " ( new ) ,
[ efault ] " i " ( - EFAULT )
: " memory " ) ;
} else {
do {
preempt_disable ( ) ;
ll_bit = 1 ;
ll_task = current ;
preempt_enable ( ) ;
err = __get_user ( old , ( unsigned int * ) addr ) ;
err | = __put_user ( new , ( unsigned int * ) addr ) ;
if ( err )
break ;
rmb ( ) ;
} while ( ! ll_bit ) ;
}
if ( unlikely ( err ) )
return err ;
regs - > regs [ 2 ] = old ;
regs - > regs [ 7 ] = 0 ; /* No error */
/*
* Don ' t let your children do this . . .
*/
__asm__ __volatile__ (
" move $29, %0 \n "
" j syscall_exit \n "
: /* no outputs */
: " r " ( regs ) ) ;
/* unreached. Honestly. */
while ( 1 ) ;
}
save_static_function ( sys_sysmips ) ;
static int __used noinline
_sys_sysmips ( nabi_no_regargs struct pt_regs regs )
{
long cmd , arg1 , arg2 , arg3 ;
cmd = regs . regs [ 4 ] ;
arg1 = regs . regs [ 5 ] ;
arg2 = regs . regs [ 6 ] ;
arg3 = regs . regs [ 7 ] ;
2007-07-25 19:19:33 +04:00
switch ( cmd ) {
2005-04-17 02:20:36 +04:00
case MIPS_ATOMIC_SET :
2009-09-17 04:25:05 +04:00
return mips_atomic_set ( & regs , arg1 , arg2 ) ;
2005-04-17 02:20:36 +04:00
case MIPS_FIXADE :
2007-07-25 19:19:33 +04:00
if ( arg1 & ~ 3 )
return - EINVAL ;
if ( arg1 & 1 )
set_thread_flag ( TIF_FIXADE ) ;
else
clear_thread_flag ( TIF_FIXADE ) ;
if ( arg1 & 2 )
set_thread_flag ( TIF_LOGADE ) ;
else
clear_thread_flag ( TIF_FIXADE ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
case FLUSH_CACHE :
__flush_cache_all ( ) ;
return 0 ;
}
return - EINVAL ;
}
/*
* sys_ipc ( ) is the de - multiplexer for the SysV IPC calls . .
*
* This is really horribly ugly .
*/
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( ipc , unsigned int , call , int , first , int , second ,
unsigned long , third , void __user * , ptr , long , fifth )
2005-04-17 02:20:36 +04:00
{
int version , ret ;
version = call > > 16 ; /* hack for backward compatibility */
call & = 0xffff ;
switch ( call ) {
case SEMOP :
2007-10-12 02:46:15 +04:00
return sys_semtimedop ( first , ( struct sembuf __user * ) ptr ,
second , NULL ) ;
2005-04-17 02:20:36 +04:00
case SEMTIMEDOP :
2007-10-12 02:46:15 +04:00
return sys_semtimedop ( first , ( struct sembuf __user * ) ptr ,
second ,
( const struct timespec __user * ) fifth ) ;
2005-04-17 02:20:36 +04:00
case SEMGET :
2007-10-12 02:46:15 +04:00
return sys_semget ( first , second , third ) ;
2005-04-17 02:20:36 +04:00
case SEMCTL : {
union semun fourth ;
if ( ! ptr )
return - EINVAL ;
2006-02-21 10:05:11 +03:00
if ( get_user ( fourth . __pad , ( void __user * __user * ) ptr ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2007-10-12 02:46:15 +04:00
return sys_semctl ( first , second , third , fourth ) ;
2005-04-17 02:20:36 +04:00
}
case MSGSND :
2007-10-12 02:46:15 +04:00
return sys_msgsnd ( first , ( struct msgbuf __user * ) ptr ,
second , third ) ;
2005-04-17 02:20:36 +04:00
case MSGRCV :
switch ( version ) {
case 0 : {
struct ipc_kludge tmp ;
if ( ! ptr )
return - EINVAL ;
if ( copy_from_user ( & tmp ,
2006-02-08 17:39:49 +03:00
( struct ipc_kludge __user * ) ptr ,
2007-10-12 02:46:15 +04:00
sizeof ( tmp ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2007-10-12 02:46:15 +04:00
return sys_msgrcv ( first , tmp . msgp , second ,
tmp . msgtyp , third ) ;
2005-04-17 02:20:36 +04:00
}
default :
2007-10-12 02:46:15 +04:00
return sys_msgrcv ( first ,
( struct msgbuf __user * ) ptr ,
second , fifth , third ) ;
2005-04-17 02:20:36 +04:00
}
case MSGGET :
2007-10-12 02:46:15 +04:00
return sys_msgget ( ( key_t ) first , second ) ;
2005-04-17 02:20:36 +04:00
case MSGCTL :
2007-10-12 02:46:15 +04:00
return sys_msgctl ( first , second ,
( struct msqid_ds __user * ) ptr ) ;
2005-04-17 02:20:36 +04:00
case SHMAT :
switch ( version ) {
default : {
2006-06-28 14:24:12 +04:00
unsigned long raddr ;
2007-10-12 02:46:15 +04:00
ret = do_shmat ( first , ( char __user * ) ptr , second ,
& raddr ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
return ret ;
2007-10-12 02:46:15 +04:00
return put_user ( raddr , ( unsigned long __user * ) third ) ;
2005-04-17 02:20:36 +04:00
}
case 1 : /* iBCS2 emulator entry point */
if ( ! segment_eq ( get_fs ( ) , get_ds ( ) ) )
return - EINVAL ;
2007-10-12 02:46:15 +04:00
return do_shmat ( first , ( char __user * ) ptr , second ,
( unsigned long * ) third ) ;
2005-04-17 02:20:36 +04:00
}
case SHMDT :
2007-10-12 02:46:15 +04:00
return sys_shmdt ( ( char __user * ) ptr ) ;
2005-04-17 02:20:36 +04:00
case SHMGET :
2007-10-12 02:46:15 +04:00
return sys_shmget ( first , second , third ) ;
2005-04-17 02:20:36 +04:00
case SHMCTL :
2007-10-12 02:46:15 +04:00
return sys_shmctl ( first , second ,
( struct shmid_ds __user * ) ptr ) ;
2005-04-17 02:20:36 +04:00
default :
return - ENOSYS ;
}
}
/*
* No implemented yet . . .
*/
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE3 ( cachectl , char * , addr , int , nbytes , int , op )
2005-04-17 02:20:36 +04:00
{
return - ENOSYS ;
}
/*
* If we ever come here the user sp is bad . Zap the process right away .
* Due to the bad stack signaling wouldn ' t work .
*/
asmlinkage void bad_stack ( void )
{
do_exit ( SIGSEGV ) ;
}
2006-10-02 13:18:34 +04:00
/*
* Do a system call from kernel instead of calling sys_execve so we
* end up with proper pt_regs .
*/
int kernel_execve ( const char * filename , char * const argv [ ] , char * const envp [ ] )
{
register unsigned long __a0 asm ( " $4 " ) = ( unsigned long ) filename ;
register unsigned long __a1 asm ( " $5 " ) = ( unsigned long ) argv ;
register unsigned long __a2 asm ( " $6 " ) = ( unsigned long ) envp ;
register unsigned long __a3 asm ( " $7 " ) ;
unsigned long __v0 ;
__asm__ volatile ( " \n "
" .set noreorder \n "
" li $2, %5 # __NR_execve \n "
" syscall \n "
" move %0, $2 \n "
" .set reorder \n "
: " =&r " ( __v0 ) , " =r " ( __a3 )
: " r " ( __a0 ) , " r " ( __a1 ) , " r " ( __a2 ) , " i " ( __NR_execve )
: " $2 " , " $8 " , " $9 " , " $10 " , " $11 " , " $12 " , " $13 " , " $14 " , " $15 " , " $24 " ,
" memory " ) ;
if ( __a3 = = 0 )
return __v0 ;
return - __v0 ;
}