2005-04-17 02:20:36 +04:00
/*
* Conversion between 32 - bit and 64 - bit native system calls .
*
* Copyright ( C ) 2000 Silicon Graphics , Inc .
* Written by Ulf Carlsson ( ulfc @ engr . sgi . com )
* sys32_execve from ia64 / ia32 code , Feb 2000 , Kanoj Sarcar ( kanoj @ sgi . com )
*/
# include <linux/compiler.h>
# include <linux/mm.h>
# include <linux/errno.h>
# include <linux/file.h>
# include <linux/smp_lock.h>
# include <linux/highuid.h>
# include <linux/resource.h>
# include <linux/highmem.h>
# include <linux/time.h>
# include <linux/times.h>
# include <linux/poll.h>
# include <linux/slab.h>
# include <linux/skbuff.h>
# include <linux/filter.h>
# include <linux/shm.h>
# include <linux/sem.h>
# include <linux/msg.h>
# include <linux/icmpv6.h>
# include <linux/syscalls.h>
# include <linux/sysctl.h>
# include <linux/utime.h>
# include <linux/utsname.h>
# include <linux/personality.h>
# include <linux/dnotify.h>
# include <linux/module.h>
# include <linux/binfmts.h>
# include <linux/security.h>
# include <linux/compat.h>
# include <linux/vfs.h>
2007-10-17 10:29:24 +04:00
# include <linux/ipc.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <net/scm.h>
2007-02-13 03:05:11 +03:00
# include <asm/compat-signal.h>
2005-04-17 02:20:36 +04:00
# include <asm/sim.h>
# include <asm/uaccess.h>
# include <asm/mmu_context.h>
# include <asm/mman.h>
/* Use this to get at 32-bit user passed pointers. */
/* A() macro should be used for places where you e.g.
have some internal variable u32 and just want to get
rid of a compiler warning . AA ( ) has to be used in
places where you want to convert a function argument
to 32 bit pointer or when you e . g . access pt_regs
structure and want to consider 32 bit registers only .
*/
# define A(__x) ((unsigned long)(__x))
# define AA(__x) ((unsigned long)((int)__x))
# ifdef __MIPSEB__
2007-10-12 02:46:15 +04:00
# define merge_64(r1, r2) ((((r1) & 0xffffffffUL) << 32) + ((r2) & 0xffffffffUL))
2005-04-17 02:20:36 +04:00
# endif
# ifdef __MIPSEL__
2007-10-12 02:46:15 +04:00
# define merge_64(r1, r2) ((((r2) & 0xffffffffUL) << 32) + ((r1) & 0xffffffffUL))
2005-04-17 02:20:36 +04:00
# endif
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( 32 _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
{
struct file * file = NULL ;
unsigned long error ;
error = - EINVAL ;
2006-02-25 08:20:29 +03:00
if ( pgoff & ( ~ PAGE_MASK > > 12 ) )
goto out ;
pgoff > > = PAGE_SHIFT - 12 ;
2005-04-17 02:20:36 +04:00
if ( ! ( flags & MAP_ANONYMOUS ) ) {
error = - EBADF ;
file = fget ( fd ) ;
if ( ! file )
goto out ;
}
flags & = ~ ( MAP_EXECUTABLE | MAP_DENYWRITE ) ;
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 ;
}
/*
* sys_execve ( ) executes a new program .
*/
asmlinkage int sys32_execve ( nabi_no_regargs struct pt_regs regs )
{
int error ;
char * filename ;
filename = getname ( compat_ptr ( regs . regs [ 4 ] ) ) ;
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
error = compat_do_execve ( filename , compat_ptr ( regs . regs [ 5 ] ) ,
compat_ptr ( regs . regs [ 6 ] ) , & regs ) ;
putname ( filename ) ;
out :
return error ;
}
# define RLIM_INFINITY32 0x7fffffff
# define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x)
struct rlimit32 {
int rlim_cur ;
int rlim_max ;
} ;
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE4 ( 32 _truncate64 , const char __user * , path ,
unsigned long , __dummy , unsigned long , a2 , unsigned long , a3 )
2005-04-17 02:20:36 +04:00
{
2008-01-29 13:15:02 +03:00
return sys_truncate ( path , merge_64 ( a2 , a3 ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE4 ( 32 _ftruncate64 , unsigned long , fd , unsigned long , __dummy ,
unsigned long , a2 , unsigned long , a3 )
2005-04-17 02:20:36 +04:00
{
2008-01-29 13:15:02 +03:00
return sys_ftruncate ( fd , merge_64 ( a2 , a3 ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-28 03:36:09 +03:00
SYSCALL_DEFINE5 ( 32 _llseek , unsigned int , fd , unsigned int , offset_high ,
unsigned int , offset_low , loff_t __user * , result ,
unsigned int , origin )
2005-04-17 02:20:36 +04:00
{
return sys_llseek ( fd , offset_high , offset_low , result , origin ) ;
}
/* From the Single Unix Spec: pread & pwrite act like lseek to pos + op +
lseek back to original location . They fail just like lseek does on
non - seekable files . */
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( 32 _pread , unsigned long , fd , char __user * , buf , size_t , count ,
unsigned long , unused , unsigned long , a4 , unsigned long , a5 )
2005-04-17 02:20:36 +04:00
{
2006-04-26 10:28:09 +04:00
return sys_pread64 ( fd , buf , count , merge_64 ( a4 , a5 ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( 32 _pwrite , unsigned int , fd , const char __user * , buf ,
size_t , count , u32 , unused , u64 , a4 , u64 , a5 )
2005-04-17 02:20:36 +04:00
{
2006-04-26 10:28:09 +04:00
return sys_pwrite64 ( fd , buf , count , merge_64 ( a4 , a5 ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE2 ( 32 _sched_rr_get_interval , compat_pid_t , pid ,
struct compat_timespec __user * , interval )
2005-04-17 02:20:36 +04:00
{
struct timespec t ;
int ret ;
2007-10-12 02:46:15 +04:00
mm_segment_t old_fs = get_fs ( ) ;
2005-04-17 02:20:36 +04:00
2007-10-12 02:46:15 +04:00
set_fs ( KERNEL_DS ) ;
2006-02-21 10:05:11 +03:00
ret = sys_sched_rr_get_interval ( pid , ( struct timespec __user * ) & t ) ;
2007-10-12 02:46:15 +04:00
set_fs ( old_fs ) ;
2005-04-17 02:20:36 +04:00
if ( put_user ( t . tv_sec , & interval - > tv_sec ) | |
2007-10-12 02:46:15 +04:00
__put_user ( t . tv_nsec , & interval - > tv_nsec ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return ret ;
}
2007-03-10 21:22:25 +03:00
# ifdef CONFIG_SYSVIPC
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( 32 _ipc , u32 , call , long , first , long , second , long , third ,
unsigned long , ptr , unsigned long , fifth )
2005-04-17 02:20:36 +04:00
{
int version , err ;
version = call > > 16 ; /* hack for backward compatibility */
call & = 0xffff ;
switch ( call ) {
case SEMOP :
/* struct sembuf is the same on 32 and 64bit :)) */
2006-11-07 12:02:44 +03:00
err = sys_semtimedop ( first , compat_ptr ( ptr ) , second , NULL ) ;
2005-04-17 02:20:36 +04:00
break ;
case SEMTIMEDOP :
2006-11-07 12:02:44 +03:00
err = compat_sys_semtimedop ( first , compat_ptr ( ptr ) , second ,
compat_ptr ( fifth ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case SEMGET :
2006-11-07 12:02:44 +03:00
err = sys_semget ( first , second , third ) ;
2005-04-17 02:20:36 +04:00
break ;
case SEMCTL :
2006-11-07 12:02:44 +03:00
err = compat_sys_semctl ( first , second , third , compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case MSGSND :
2006-11-07 12:02:44 +03:00
err = compat_sys_msgsnd ( first , second , third , compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case MSGRCV :
2006-11-07 12:02:44 +03:00
err = compat_sys_msgrcv ( first , second , fifth , third ,
version , compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case MSGGET :
2006-11-07 12:02:44 +03:00
err = sys_msgget ( ( key_t ) first , second ) ;
2005-04-17 02:20:36 +04:00
break ;
case MSGCTL :
2006-11-07 12:02:44 +03:00
err = compat_sys_msgctl ( first , second , compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case SHMAT :
2006-11-07 12:02:44 +03:00
err = compat_sys_shmat ( first , second , third , version ,
compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case SHMDT :
2006-11-07 12:02:44 +03:00
err = sys_shmdt ( compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case SHMGET :
2006-11-07 12:02:44 +03:00
err = sys_shmget ( first , ( unsigned ) second , third ) ;
2005-04-17 02:20:36 +04:00
break ;
case SHMCTL :
2006-11-07 12:02:44 +03:00
err = compat_sys_shmctl ( first , second , compat_ptr ( ptr ) ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
err = - EINVAL ;
break ;
}
return err ;
}
2007-03-10 21:22:25 +03:00
# else
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE6 ( 32 _ipc , u32 , call , int , first , int , second , int , third ,
2009-03-09 04:45:12 +03:00
u32 , ptr , u32 , fifth )
2007-03-10 21:22:25 +03:00
{
return - ENOSYS ;
}
# endif /* CONFIG_SYSVIPC */
2006-11-07 12:02:44 +03:00
# ifdef CONFIG_MIPS32_N32
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE4 ( n32_semctl , int , semid , int , semnum , int , cmd , u32 , arg )
2005-04-17 02:20:36 +04:00
{
2006-11-07 12:02:44 +03:00
/* compat_sys_semctl expects a pointer to union semun */
u32 __user * uptr = compat_alloc_user_space ( sizeof ( u32 ) ) ;
2007-01-10 12:53:33 +03:00
if ( put_user ( arg , uptr ) )
2006-11-07 12:02:44 +03:00
return - EFAULT ;
return compat_sys_semctl ( semid , semnum , cmd , uptr ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-10 12:53:33 +03:00
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE4 ( n32_msgsnd , int , msqid , u32 , msgp , unsigned int , msgsz ,
int , msgflg )
2007-01-10 12:53:33 +03:00
{
return compat_sys_msgsnd ( msqid , msgsz , msgflg , compat_ptr ( msgp ) ) ;
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE5 ( n32_msgrcv , int , msqid , u32 , msgp , size_t , msgsz ,
int , msgtyp , int , msgflg )
2007-01-10 12:53:33 +03:00
{
return compat_sys_msgrcv ( msqid , msgsz , msgtyp , msgflg , IPC_64 ,
compat_ptr ( msgp ) ) ;
}
2006-11-07 12:02:44 +03:00
# endif
2005-04-17 02:20:36 +04:00
struct sysctl_args32
{
compat_caddr_t name ;
int nlen ;
compat_caddr_t oldval ;
compat_caddr_t oldlenp ;
compat_caddr_t newval ;
compat_size_t newlen ;
unsigned int __unused [ 4 ] ;
} ;
2006-09-27 12:51:04 +04:00
# ifdef CONFIG_SYSCTL_SYSCALL
2005-04-17 02:20:36 +04:00
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE1 ( 32 _sysctl , struct sysctl_args32 __user * , args )
2005-04-17 02:20:36 +04:00
{
struct sysctl_args32 tmp ;
int error ;
2006-02-21 10:05:11 +03:00
size_t oldlen ;
size_t __user * oldlenp = NULL ;
unsigned long addr = ( ( ( unsigned long ) & args - > __unused [ 0 ] ) + 7 ) & ~ 7 ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & tmp , args , sizeof ( tmp ) ) )
return - EFAULT ;
if ( tmp . oldval & & tmp . oldlenp ) {
/* Duh, this is ugly and might not work if sysctl_args
is in read - only memory , but do_sysctl does indirectly
a lot of uaccess in both directions and we ' d have to
basically copy the whole sysctl . c here , and
glibc ' s __sysctl uses rw memory for the structure
anyway . */
2006-02-21 10:05:11 +03:00
if ( get_user ( oldlen , ( u32 __user * ) A ( tmp . oldlenp ) ) | |
put_user ( oldlen , ( size_t __user * ) addr ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-02-21 10:05:11 +03:00
oldlenp = ( size_t __user * ) addr ;
2005-04-17 02:20:36 +04:00
}
lock_kernel ( ) ;
2006-02-21 10:05:11 +03:00
error = do_sysctl ( ( int __user * ) A ( tmp . name ) , tmp . nlen , ( void __user * ) A ( tmp . oldval ) ,
oldlenp , ( void __user * ) A ( tmp . newval ) , tmp . newlen ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
if ( oldlenp ) {
if ( ! error ) {
2006-02-21 10:05:11 +03:00
if ( get_user ( oldlen , ( size_t __user * ) addr ) | |
put_user ( oldlen , ( u32 __user * ) A ( tmp . oldlenp ) ) )
2005-04-17 02:20:36 +04:00
error = - EFAULT ;
}
copy_to_user ( args - > __unused , tmp . __unused , sizeof ( tmp . __unused ) ) ;
}
return error ;
}
2009-02-08 19:00:26 +03:00
# else
SYSCALL_DEFINE1 ( 32 _sysctl , struct sysctl_args32 __user * , args )
{
return - ENOSYS ;
}
2006-09-27 12:51:04 +04:00
# endif /* CONFIG_SYSCTL_SYSCALL */
2005-04-17 02:20:36 +04:00
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE1 ( 32 _newuname , struct new_utsname __user * , name )
2005-04-17 02:20:36 +04:00
{
int ret = 0 ;
down_read ( & uts_sem ) ;
2006-10-02 13:18:11 +04:00
if ( copy_to_user ( name , utsname ( ) , sizeof * name ) )
2005-04-17 02:20:36 +04:00
ret = - EFAULT ;
up_read ( & uts_sem ) ;
if ( current - > personality = = PER_LINUX32 & & ! ret )
if ( copy_to_user ( name - > machine , " mips \0 \0 \0 " , 8 ) )
ret = - EFAULT ;
return ret ;
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE1 ( 32 _personality , unsigned long , personality )
2005-04-17 02:20:36 +04:00
{
int ret ;
2006-08-13 03:53:29 +04:00
personality & = 0xffffffff ;
if ( personality ( current - > personality ) = = PER_LINUX32 & &
personality = = PER_LINUX )
2005-04-17 02:20:36 +04:00
personality = PER_LINUX32 ;
ret = sys_personality ( personality ) ;
if ( ret = = PER_LINUX32 )
ret = PER_LINUX ;
return ret ;
}
2009-02-08 19:00:26 +03:00
SYSCALL_DEFINE4 ( 32 _sendfile , long , out_fd , long , in_fd ,
compat_off_t __user * , offset , s32 , count )
2005-04-17 02:20:36 +04:00
{
mm_segment_t old_fs = get_fs ( ) ;
int ret ;
off_t of ;
2005-09-04 02:56:17 +04:00
2005-04-17 02:20:36 +04:00
if ( offset & & get_user ( of , offset ) )
return - EFAULT ;
2005-09-04 02:56:17 +04:00
2005-04-17 02:20:36 +04:00
set_fs ( KERNEL_DS ) ;
2006-02-21 10:05:11 +03:00
ret = sys_sendfile ( out_fd , in_fd , offset ? ( off_t __user * ) & of : NULL , count ) ;
2005-04-17 02:20:36 +04:00
set_fs ( old_fs ) ;
2005-09-04 02:56:17 +04:00
2005-04-17 02:20:36 +04:00
if ( offset & & put_user ( of , offset ) )
return - EFAULT ;
2005-09-04 02:56:17 +04:00
2005-04-17 02:20:36 +04:00
return ret ;
}
asmlinkage ssize_t sys32_readahead ( int fd , u32 pad0 , u64 a2 , u64 a3 ,
size_t count )
{
return sys_readahead ( fd , merge_64 ( a2 , a3 ) , count ) ;
}
2006-04-01 10:49:21 +04:00
asmlinkage long sys32_sync_file_range ( int fd , int __pad ,
unsigned long a2 , unsigned long a3 ,
unsigned long a4 , unsigned long a5 ,
int flags )
{
return sys_sync_file_range ( fd ,
merge_64 ( a2 , a3 ) , merge_64 ( a4 , a5 ) ,
flags ) ;
}
2007-05-17 19:46:13 +04:00
asmlinkage long sys32_fadvise64_64 ( int fd , int __pad ,
unsigned long a2 , unsigned long a3 ,
unsigned long a4 , unsigned long a5 ,
int flags )
{
return sys_fadvise64_64 ( fd ,
merge_64 ( a2 , a3 ) , merge_64 ( a4 , a5 ) ,
flags ) ;
}
2007-07-26 06:38:24 +04:00
asmlinkage long sys32_fallocate ( int fd , int mode , unsigned offset_a2 ,
unsigned offset_a3 , unsigned len_a4 , unsigned len_a5 )
{
return sys_fallocate ( fd , mode , merge_64 ( offset_a2 , offset_a3 ) ,
merge_64 ( len_a4 , len_a5 ) ) ;
}
2005-04-13 21:43:59 +04:00
save_static_function ( sys32_clone ) ;
2007-07-22 12:01:39 +04:00
static int noinline __used
2005-04-13 21:43:59 +04:00
_sys32_clone ( nabi_no_regargs struct pt_regs regs )
{
unsigned long clone_flags ;
unsigned long newsp ;
int __user * parent_tidptr , * child_tidptr ;
clone_flags = regs . regs [ 4 ] ;
newsp = regs . regs [ 5 ] ;
if ( ! newsp )
newsp = regs . regs [ 29 ] ;
2006-02-21 10:05:11 +03:00
parent_tidptr = ( int __user * ) regs . regs [ 6 ] ;
2005-04-13 21:43:59 +04:00
/* Use __dummy4 instead of getting it off the stack, so that
syscall ( ) works . */
child_tidptr = ( int __user * ) __dummy4 ;
return do_fork ( clone_flags , newsp , & regs , 0 ,
parent_tidptr , child_tidptr ) ;
}