2005-04-17 02:20:36 +04:00
/*
* This file contains various random system calls that
* have a non - standard calling sequence on the Linux / i386
* platform .
*/
# include <linux/errno.h>
# include <linux/sched.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/sem.h>
# include <linux/msg.h>
# include <linux/shm.h>
# include <linux/stat.h>
# include <linux/syscalls.h>
# include <linux/mman.h>
# include <linux/file.h>
# include <linux/utsname.h>
2007-10-17 10:29:24 +04:00
# include <linux/ipc.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
2006-10-02 13:18:34 +04:00
# include <asm/unistd.h>
2005-04-17 02:20:36 +04:00
/*
* sys_pipe ( ) is the normal C calling standard for creating
* a pipe . It ' s not the way Unix traditionally does this , though .
*/
asmlinkage int sys_pipe ( unsigned long __user * fildes )
{
int fd [ 2 ] ;
int error ;
error = do_pipe ( fd ) ;
if ( ! error ) {
if ( copy_to_user ( fildes , fd , 2 * sizeof ( int ) ) )
error = - EFAULT ;
}
return error ;
}
2006-03-22 11:08:44 +03:00
asmlinkage long sys_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
{
int error = - EBADF ;
2006-03-22 11:08:44 +03:00
struct file * file = NULL ;
struct mm_struct * mm = current - > mm ;
2005-04-17 02:20:36 +04:00
flags & = ~ ( MAP_EXECUTABLE | MAP_DENYWRITE ) ;
if ( ! ( flags & MAP_ANONYMOUS ) ) {
file = fget ( fd ) ;
if ( ! file )
goto out ;
}
2006-03-22 11:08:44 +03:00
down_write ( & mm - > mmap_sem ) ;
2005-04-17 02:20:36 +04:00
error = do_mmap_pgoff ( file , addr , len , prot , flags , pgoff ) ;
2006-03-22 11:08:44 +03:00
up_write ( & mm - > mmap_sem ) ;
2005-04-17 02:20:36 +04:00
if ( file )
fput ( file ) ;
out :
return error ;
}
/*
* Perform the select ( nd , in , out , ex , tv ) and mmap ( ) system
* calls . Linux / i386 didn ' t use to be able to handle more than
* 4 system call parameters , so these system calls used a memory
* block for parameter passing . .
*/
struct mmap_arg_struct {
unsigned long addr ;
unsigned long len ;
unsigned long prot ;
unsigned long flags ;
unsigned long fd ;
unsigned long offset ;
} ;
asmlinkage int old_mmap ( struct mmap_arg_struct __user * arg )
{
struct mmap_arg_struct a ;
int err = - EFAULT ;
if ( copy_from_user ( & a , arg , sizeof ( a ) ) )
goto out ;
err = - EINVAL ;
if ( a . offset & ~ PAGE_MASK )
goto out ;
2006-03-22 11:08:44 +03:00
err = sys_mmap2 ( a . addr , a . len , a . prot , a . flags ,
a . fd , a . offset > > PAGE_SHIFT ) ;
2005-04-17 02:20:36 +04:00
out :
return err ;
}
struct sel_arg_struct {
unsigned long n ;
fd_set __user * inp , * outp , * exp ;
struct timeval __user * tvp ;
} ;
asmlinkage int old_select ( struct sel_arg_struct __user * arg )
{
struct sel_arg_struct a ;
if ( copy_from_user ( & a , arg , sizeof ( a ) ) )
return - EFAULT ;
/* sys_select() does the appropriate kernel locking */
return sys_select ( a . n , a . inp , a . outp , a . exp , a . tvp ) ;
}
/*
* sys_ipc ( ) is the de - multiplexer for the SysV IPC calls . .
*
* This is really horribly ugly .
*/
asmlinkage int sys_ipc ( uint call , int first , int second ,
int third , void __user * ptr , long fifth )
{
int version , ret ;
version = call > > 16 ; /* hack for backward compatibility */
call & = 0xffff ;
switch ( call ) {
case SEMOP :
return sys_semtimedop ( first , ( struct sembuf __user * ) ptr , second , NULL ) ;
case SEMTIMEDOP :
return sys_semtimedop ( first , ( struct sembuf __user * ) ptr , second ,
( const struct timespec __user * ) fifth ) ;
case SEMGET :
return sys_semget ( first , second , third ) ;
case SEMCTL : {
union semun fourth ;
if ( ! ptr )
return - EINVAL ;
if ( get_user ( fourth . __pad , ( void __user * __user * ) ptr ) )
return - EFAULT ;
return sys_semctl ( first , second , third , fourth ) ;
}
case MSGSND :
return sys_msgsnd ( first , ( struct msgbuf __user * ) ptr ,
second , third ) ;
case MSGRCV :
switch ( version ) {
case 0 : {
struct ipc_kludge tmp ;
if ( ! ptr )
return - EINVAL ;
if ( copy_from_user ( & tmp ,
( struct ipc_kludge __user * ) ptr ,
sizeof ( tmp ) ) )
return - EFAULT ;
return sys_msgrcv ( first , tmp . msgp , second ,
tmp . msgtyp , third ) ;
}
default :
return sys_msgrcv ( first ,
( struct msgbuf __user * ) ptr ,
second , fifth , third ) ;
}
case MSGGET :
return sys_msgget ( ( key_t ) first , second ) ;
case MSGCTL :
return sys_msgctl ( first , second , ( struct msqid_ds __user * ) ptr ) ;
case SHMAT :
switch ( version ) {
default : {
ulong raddr ;
ret = do_shmat ( first , ( char __user * ) ptr , second , & raddr ) ;
if ( ret )
return ret ;
return put_user ( raddr , ( ulong __user * ) third ) ;
}
case 1 : /* iBCS2 emulator entry point */
if ( ! segment_eq ( get_fs ( ) , get_ds ( ) ) )
return - EINVAL ;
/* The "(ulong *) third" is valid _only_ because of the kernel segment thing */
return do_shmat ( first , ( char __user * ) ptr , second , ( ulong * ) third ) ;
}
case SHMDT :
return sys_shmdt ( ( char __user * ) ptr ) ;
case SHMGET :
return sys_shmget ( first , second , third ) ;
case SHMCTL :
return sys_shmctl ( first , second ,
( struct shmid_ds __user * ) ptr ) ;
default :
return - ENOSYS ;
}
}
/*
* Old cruft
*/
asmlinkage int sys_uname ( struct old_utsname __user * name )
{
int err ;
if ( ! name )
return - EFAULT ;
down_read ( & uts_sem ) ;
2006-10-02 13:18:11 +04:00
err = copy_to_user ( name , utsname ( ) , sizeof ( * name ) ) ;
2005-04-17 02:20:36 +04:00
up_read ( & uts_sem ) ;
return err ? - EFAULT : 0 ;
}
asmlinkage int sys_olduname ( struct oldold_utsname __user * name )
{
int error ;
if ( ! name )
return - EFAULT ;
if ( ! access_ok ( VERIFY_WRITE , name , sizeof ( struct oldold_utsname ) ) )
return - EFAULT ;
down_read ( & uts_sem ) ;
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
up_read ( & uts_sem ) ;
error = error ? - EFAULT : 0 ;
return error ;
}
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 [ ] )
{
long __res ;
asm volatile ( " push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx "
: " =a " ( __res )
: " 0 " ( __NR_execve ) , " ri " ( filename ) , " c " ( argv ) , " d " ( envp ) : " memory " ) ;
return __res ;
}