2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / sys_arm . c
*
* Copyright ( C ) People who wrote linux / arch / i386 / kernel / sys_i386 . c
* Copyright ( C ) 1995 , 1996 Russell King .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This file contains various random system calls that
* have a non - standard calling sequence on the Linux / arm
* platform .
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/mm.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/fs.h>
# include <linux/file.h>
# include <linux/utsname.h>
# include <asm/uaccess.h>
# include <asm/ipc.h>
extern unsigned long do_mremap ( unsigned long addr , unsigned long old_len ,
unsigned long new_len , unsigned long flags ,
unsigned long new_addr ) ;
/*
* 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 ;
}
/* common code for old and new mmaps */
inline long do_mmap2 (
unsigned long addr , unsigned long len ,
unsigned long prot , unsigned long flags ,
unsigned long fd , unsigned long pgoff )
{
int error = - EINVAL ;
struct file * file = NULL ;
flags & = ~ ( MAP_EXECUTABLE | MAP_DENYWRITE ) ;
2005-04-20 00:29:21 +04:00
if ( flags & MAP_FIXED & & addr < FIRST_USER_ADDRESS )
2005-04-17 02:20:36 +04:00
goto out ;
error = - EBADF ;
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 ;
}
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 )
{
int error = - EFAULT ;
struct mmap_arg_struct a ;
if ( copy_from_user ( & a , arg , sizeof ( a ) ) )
goto out ;
error = - EINVAL ;
if ( a . offset & ~ PAGE_MASK )
goto out ;
error = do_mmap2 ( a . addr , a . len , a . prot , a . flags , a . fd , a . offset > > PAGE_SHIFT ) ;
out :
return error ;
}
asmlinkage unsigned long
sys_arm_mremap ( unsigned long addr , unsigned long old_len ,
unsigned long new_len , unsigned long flags ,
unsigned long new_addr )
{
unsigned long ret = - EINVAL ;
2005-04-20 00:29:21 +04:00
if ( flags & MREMAP_FIXED & & new_addr < FIRST_USER_ADDRESS )
2005-04-17 02:20:36 +04:00
goto out ;
down_write ( & current - > mm - > mmap_sem ) ;
ret = do_mremap ( addr , old_len , new_len , flags , new_addr ) ;
up_write ( & current - > mm - > mmap_sem ) ;
out :
return ret ;
}
/*
* Perform the select ( nd , in , out , ex , tv ) and mmap ( ) system
* calls .
*/
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 : /* Of course, we don't support iBCS2! */
return - EINVAL ;
}
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 ;
}
}
asmlinkage long sys_shmat ( int shmid , char __user * shmaddr , int shmflg ,
unsigned long __user * addr )
{
unsigned long ret ;
long err ;
err = do_shmat ( shmid , shmaddr , shmflg , & ret ) ;
if ( err = = 0 )
err = put_user ( ret , addr ) ;
return err ;
}
/* Fork a new task - this creates a new program thread.
* This is called indirectly via a small wrapper
*/
asmlinkage int sys_fork ( struct pt_regs * regs )
{
return do_fork ( SIGCHLD , regs - > ARM_sp , regs , 0 , NULL , NULL ) ;
}
/* Clone a task - this clones the calling program thread.
* This is called indirectly via a small wrapper
*/
asmlinkage int sys_clone ( unsigned long clone_flags , unsigned long newsp ,
int __user * parent_tidptr , int tls_val ,
int __user * child_tidptr , struct pt_regs * regs )
{
if ( ! newsp )
newsp = regs - > ARM_sp ;
return do_fork ( clone_flags , newsp , regs , 0 , parent_tidptr , child_tidptr ) ;
}
asmlinkage int sys_vfork ( struct pt_regs * regs )
{
return do_fork ( CLONE_VFORK | CLONE_VM | SIGCHLD , regs - > ARM_sp , regs , 0 , NULL , NULL ) ;
}
/* sys_execve() executes a new program.
* This is called indirectly via a small wrapper
*/
asmlinkage int sys_execve ( char __user * filenamei , char __user * __user * argv ,
char __user * __user * envp , struct pt_regs * regs )
{
int error ;
char * filename ;
filename = getname ( filenamei ) ;
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
error = do_execve ( filename , argv , envp , regs ) ;
putname ( filename ) ;
out :
return error ;
}
long execve ( const char * filename , char * * argv , char * * envp )
{
struct pt_regs regs ;
int ret ;
memset ( & regs , 0 , sizeof ( struct pt_regs ) ) ;
ret = do_execve ( ( char * ) filename , ( char __user * __user * ) argv ,
( char __user * __user * ) envp , & regs ) ;
if ( ret < 0 )
goto out ;
/*
* Save argc to the register structure for userspace .
*/
regs . ARM_r0 = ret ;
/*
* We were successful . We won ' t be returning to our caller , but
* instead to user space by manipulating the kernel stack .
*/
asm ( " add r0, %0, %1 \n \t "
" mov r1, %2 \n \t "
" mov r2, %3 \n \t "
" bl memmove \n \t " /* copy regs to top of stack */
" mov r8, #0 \n \t " /* not a syscall */
" mov r9, %0 \n \t " /* thread structure */
" mov sp, r0 \n \t " /* reposition stack pointer */
" b ret_to_user "
:
: " r " ( current_thread_info ( ) ) ,
" Ir " ( THREAD_SIZE - 8 - sizeof ( regs ) ) ,
" r " ( & regs ) ,
" Ir " ( sizeof ( regs ) )
: " r0 " , " r1 " , " r2 " , " r3 " , " ip " , " memory " ) ;
out :
return ret ;
}
EXPORT_SYMBOL ( execve ) ;