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 .
*/
# include <linux/a.out.h>
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>
# include <linux/smp.h>
# include <linux/smp_lock.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>
2005-04-17 02:20:36 +04:00
# include <asm/branch.h>
# include <asm/cachectl.h>
# include <asm/cacheflush.h>
# include <asm/ipc.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>
asmlinkage int sys_pipe ( nabi_no_regargs volatile struct pt_regs regs )
{
int fd [ 2 ] ;
int error , res ;
error = do_pipe ( fd ) ;
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 ;
if ( flags & MAP_FIXED ) {
/*
* We do not accept a shared mapping if it would violate
* cache aliasing constraints .
*/
if ( ( flags & MAP_SHARED ) & & ( addr & shm_align_mask ) )
return - EINVAL ;
return addr ;
}
if ( len > task_size )
return - ENOMEM ;
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 ) ;
}
}
/* common code for old and new mmaps */
static inline unsigned long
do_mmap2 ( unsigned long addr , unsigned long len , unsigned long prot ,
unsigned long flags , unsigned long fd , unsigned long pgoff )
{
unsigned long error = - EBADF ;
struct file * file = NULL ;
flags & = ~ ( MAP_EXECUTABLE | MAP_DENYWRITE ) ;
if ( ! ( flags & MAP_ANONYMOUS ) ) {
file = fget ( fd ) ;
if ( ! file )
goto out ;
}
down_write ( & current - > mm - > mmap_sem ) ;
error = do_mmap_pgoff ( file , addr , len , prot , flags , pgoff ) ;
up_write ( & current - > mm - > mmap_sem ) ;
if ( file )
fput ( file ) ;
out :
return error ;
}
asmlinkage unsigned long
old_mmap ( unsigned long addr , unsigned long len , int prot ,
int flags , int fd , off_t offset )
{
unsigned long result ;
result = - EINVAL ;
if ( offset & ~ PAGE_MASK )
goto out ;
result = do_mmap2 ( addr , len , prot , flags , fd , offset > > PAGE_SHIFT ) ;
out :
return result ;
}
asmlinkage unsigned long
sys_mmap2 ( unsigned long addr , unsigned long len , unsigned long prot ,
unsigned long flags , unsigned long fd , unsigned long pgoff )
{
2006-02-25 08:20:29 +03:00
if ( pgoff & ( ~ PAGE_MASK > > 12 ) )
return - EINVAL ;
return do_mmap2 ( addr , len , prot , flags , fd , pgoff > > ( PAGE_SHIFT - 12 ) ) ;
2005-04-17 02:20:36 +04:00
}
save_static_function ( sys_fork ) ;
__attribute_used__ noinline static int
_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 ) ;
__attribute_used__ noinline static int
_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 . . .
*/
2006-02-08 17:39:49 +03:00
asmlinkage int sys_uname ( struct old_utsname __user * name )
2005-04-17 02:20:36 +04:00
{
if ( name & & ! copy_to_user ( name , & system_utsname , sizeof ( * name ) ) )
return 0 ;
return - EFAULT ;
}
/*
* Compacrapability . . .
*/
2006-02-08 17:39:49 +03:00
asmlinkage int sys_olduname ( struct oldold_utsname __user * name )
2005-04-17 02:20:36 +04:00
{
int error ;
if ( ! name )
return - EFAULT ;
if ( ! access_ok ( VERIFY_WRITE , name , sizeof ( struct oldold_utsname ) ) )
return - EFAULT ;
error = __copy_to_user ( & name - > sysname , & system_utsname . sysname , __OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > sysname + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > nodename , & system_utsname . nodename , __OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > nodename + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > release , & system_utsname . release , __OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > release + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > version , & system_utsname . version , __OLD_UTS_LEN ) ;
error - = __put_user ( 0 , name - > version + __OLD_UTS_LEN ) ;
error - = __copy_to_user ( & name - > machine , & system_utsname . machine , __OLD_UTS_LEN ) ;
error = __put_user ( 0 , name - > machine + __OLD_UTS_LEN ) ;
error = error ? - EFAULT : 0 ;
return error ;
}
2005-04-13 21:43:59 +04:00
void sys_set_thread_area ( unsigned long addr )
{
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 ;
/* If some future MIPS implementation has this register in hardware,
* we will need to update it here ( and in context switches ) . */
}
2005-04-17 02:20:36 +04:00
asmlinkage int _sys_sysmips ( int cmd , long arg1 , int arg2 , int arg3 )
{
2006-05-31 20:00:03 +04:00
int tmp ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case MIPS_ATOMIC_SET :
printk ( KERN_CRIT " How did I get here? \n " ) ;
return - EINVAL ;
case MIPS_FIXADE :
tmp = current - > thread . mflags & ~ 3 ;
current - > thread . mflags = tmp | ( arg1 & 3 ) ;
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 .
*/
2006-06-28 14:24:12 +04:00
asmlinkage int sys_ipc ( unsigned int call , int first , int second ,
2006-02-08 17:39:49 +03:00
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 :
2006-02-08 17:39:49 +03:00
return sys_semtimedop ( first , ( struct sembuf __user * ) ptr ,
second , NULL ) ;
2005-04-17 02:20:36 +04:00
case SEMTIMEDOP :
2006-02-08 17:39:49 +03: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 :
return sys_semget ( first , second , third ) ;
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 ;
return sys_semctl ( first , second , third , fourth ) ;
}
case MSGSND :
2006-02-08 17:39:49 +03:00
return sys_msgsnd ( first , ( struct msgbuf __user * ) ptr ,
2005-04-17 02:20:36 +04:00
second , third ) ;
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 ,
2005-04-17 02:20:36 +04:00
sizeof ( tmp ) ) )
return - EFAULT ;
return sys_msgrcv ( first , tmp . msgp , second ,
tmp . msgtyp , third ) ;
}
default :
return sys_msgrcv ( first ,
2006-02-08 17:39:49 +03:00
( struct msgbuf __user * ) ptr ,
2005-04-17 02:20:36 +04:00
second , fifth , third ) ;
}
case MSGGET :
return sys_msgget ( ( key_t ) first , second ) ;
case MSGCTL :
2006-02-08 17:39:49 +03: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 ;
2006-02-08 17:39:49 +03:00
ret = do_shmat ( first , ( char __user * ) ptr , second ,
& raddr ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
return ret ;
2006-06-28 14:24:12 +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 ;
2006-02-08 17:39:49 +03:00
return do_shmat ( first , ( char __user * ) ptr , second ,
2006-06-28 14:24:12 +04:00
( unsigned long * ) third ) ;
2005-04-17 02:20:36 +04:00
}
case SHMDT :
2006-02-08 17:39:49 +03:00
return sys_shmdt ( ( char __user * ) ptr ) ;
2005-04-17 02:20:36 +04:00
case SHMGET :
return sys_shmget ( first , second , third ) ;
case SHMCTL :
return sys_shmctl ( first , second ,
2006-02-08 17:39:49 +03:00
( struct shmid_ds __user * ) ptr ) ;
2005-04-17 02:20:36 +04:00
default :
return - ENOSYS ;
}
}
/*
* No implemented yet . . .
*/
asmlinkage int sys_cachectl ( char * addr , int nbytes , int op )
{
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 ) ;
}