2005-04-16 15:20:36 -07: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 12:17:48 -08:00
# include <linux/capability.h>
2005-04-16 15:20:36 -07: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-16 15:20:36 -07: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 14:39:39 +00:00
# include <linux/module.h>
2007-10-16 23:29:24 -07:00
# include <linux/ipc.h>
2009-09-17 02:25:05 +02:00
# include <linux/uaccess.h>
2005-04-16 15:20:36 -07:00
2009-09-17 02:25:05 +02:00
# include <asm/asm.h>
2005-04-16 15:20:36 -07:00
# include <asm/branch.h>
# include <asm/cachectl.h>
# include <asm/cacheflush.h>
2005-09-09 22:32:31 +02:00
# include <asm/asm-offsets.h>
2005-04-16 15:20:36 -07:00
# include <asm/signal.h>
# include <asm/sim.h>
# include <asm/shmparam.h>
# include <asm/sysmips.h>
# include <asm/uaccess.h>
2008-07-20 13:16:46 +01: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-16 15:20:36 -07:00
{
int fd [ 2 ] ;
int error , res ;
2008-07-23 21:29:30 -07:00
error = do_pipe_flags ( fd , 0 ) ;
2005-04-16 15:20:36 -07: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 14:39:39 +00:00
EXPORT_SYMBOL ( shm_align_mask ) ;
2005-04-16 15:20:36 -07: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-27 23:10:20 -07:00
if ( len > task_size )
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
if ( flags & MAP_FIXED ) {
2007-10-27 23:10:20 -07:00
/* Even MAP_FIXED mappings must reside within task_size. */
if ( task_size - len < addr )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
/*
* We do not accept a shared mapping if it would violate
* cache aliasing constraints .
*/
2009-12-05 15:10:44 -05:00
if ( ( flags & MAP_SHARED ) & &
( ( addr - ( pgoff < < PAGE_SHIFT ) ) & shm_align_mask ) )
2005-04-16 15:20:36 -07: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 16:00:26 +00: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-16 15:20:36 -07:00
{
unsigned long result ;
result = - EINVAL ;
if ( offset & ~ PAGE_MASK )
goto out ;
2009-11-30 17:37:04 -05:00
result = sys_mmap_pgoff ( addr , len , prot , flags , fd , offset > > PAGE_SHIFT ) ;
2005-04-16 15:20:36 -07:00
out :
return result ;
}
2009-02-08 16:00:26 +00: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-16 15:20:36 -07:00
{
2006-02-24 21:20:29 -08:00
if ( pgoff & ( ~ PAGE_MASK > > 12 ) )
return - EINVAL ;
2009-11-30 17:37:04 -05:00
return sys_mmap_pgoff ( addr , len , prot , flags , fd , pgoff > > ( PAGE_SHIFT - 12 ) ) ;
2005-04-16 15:20:36 -07:00
}
save_static_function ( sys_fork ) ;
2007-07-22 01:01:39 -07:00
static int __used noinline
2005-04-16 15:20:36 -07: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 01:01:39 -07:00
static int __used noinline
2005-04-16 15:20:36 -07:00
_sys_clone ( nabi_no_regargs struct pt_regs regs )
{
unsigned long clone_flags ;
unsigned long newsp ;
2005-04-13 17:43:59 +00:00
int __user * parent_tidptr , * child_tidptr ;
2005-04-16 15:20:36 -07:00
clone_flags = regs . regs [ 4 ] ;
newsp = regs . regs [ 5 ] ;
if ( ! newsp )
newsp = regs . regs [ 29 ] ;
2005-04-13 17:43:59 +00: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-16 15:20:36 -07: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 23:39:49 +09:00
filename = getname ( ( char __user * ) ( long ) regs . regs [ 4 ] ) ;
2005-04-16 15:20:36 -07:00
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
2006-02-08 23:39:49 +09:00
error = do_execve ( filename , ( char __user * __user * ) ( long ) regs . regs [ 5 ] ,
( char __user * __user * ) ( long ) regs . regs [ 6 ] , & regs ) ;
2005-04-16 15:20:36 -07:00
putname ( filename ) ;
out :
return error ;
}
2009-02-08 16:00:26 +00:00
SYSCALL_DEFINE1 ( set_thread_area , unsigned long , addr )
2005-04-13 17:43:59 +00:00
{
2006-01-12 01:06:07 -08:00
struct thread_info * ti = task_thread_info ( current ) ;
2005-04-13 17:43:59 +00:00
ti - > tp_value = addr ;
2007-07-10 17:33:02 +01:00
if ( cpu_has_userlocal )
write_c0_userlocal ( addr ) ;
2006-09-19 17:18:53 +01:00
return 0 ;
2005-04-13 17:43:59 +00:00
}
2009-09-17 02:25:05 +02:00
static inline int mips_atomic_set ( struct pt_regs * regs ,
unsigned long addr , unsigned long new )
2005-04-16 15:20:36 -07:00
{
2009-09-17 02:25:05 +02: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 11:33:03 +00:00
" .set mips3 \n "
2009-09-17 02:25:05 +02: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 11:33:03 +00:00
" .set mips0 \n "
2009-09-17 02:25:05 +02: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 11:33:03 +00:00
" .set mips3 \n "
2009-09-17 02:25:05 +02: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 11:33:03 +00:00
" .set mips0 \n "
2009-09-17 02:25:05 +02: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 16:19:33 +01:00
switch ( cmd ) {
2005-04-16 15:20:36 -07:00
case MIPS_ATOMIC_SET :
2009-09-17 02:25:05 +02:00
return mips_atomic_set ( & regs , arg1 , arg2 ) ;
2005-04-16 15:20:36 -07:00
case MIPS_FIXADE :
2007-07-25 16:19:33 +01: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-16 15:20:36 -07:00
return 0 ;
case FLUSH_CACHE :
__flush_cache_all ( ) ;
return 0 ;
}
return - EINVAL ;
}
/*
* No implemented yet . . .
*/
2009-02-08 16:00:26 +00:00
SYSCALL_DEFINE3 ( cachectl , char * , addr , int , nbytes , int , op )
2005-04-16 15:20:36 -07: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 02:18:34 -07: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 ;
}