2005-04-16 15:20:36 -07:00
/*
2007-10-16 01:27:00 -07:00
* Copyright ( C ) 2003 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2005-04-16 15:20:36 -07:00
* Copyright 2003 PathScale , Inc .
*
* Licensed under the GPL
*/
# include "linux/linkage.h"
2005-05-05 16:15:15 -07:00
# include "linux/personality.h"
2007-10-16 01:27:00 -07:00
# include "linux/utsname.h"
2005-04-16 15:20:36 -07:00
# include "asm/prctl.h" /* XXX This should get the constants from libc */
2007-10-16 01:27:00 -07:00
# include "asm/uaccess.h"
2007-02-10 01:44:29 -08:00
# include "os.h"
2005-04-16 15:20:36 -07:00
2005-05-01 08:58:55 -07:00
asmlinkage long sys_uname64 ( struct new_utsname __user * name )
{
int err ;
2007-10-16 01:27:00 -07:00
2005-05-01 08:58:55 -07:00
down_read ( & uts_sem ) ;
2006-10-02 02:18:11 -07:00
err = copy_to_user ( name , utsname ( ) , sizeof ( * name ) ) ;
2005-05-01 08:58:55 -07:00
up_read ( & uts_sem ) ;
2007-10-16 01:27:00 -07:00
2005-05-01 08:58:55 -07:00
if ( personality ( current - > personality ) = = PER_LINUX32 )
err | = copy_to_user ( & name - > machine , " i686 " , 5 ) ;
2007-10-16 01:27:00 -07:00
2005-05-01 08:58:55 -07:00
return err ? - EFAULT : 0 ;
}
2007-10-16 01:26:58 -07:00
long arch_prctl ( struct task_struct * task , int code , unsigned long __user * addr )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:00 -07:00
unsigned long * ptr = addr , tmp ;
2007-02-10 01:44:29 -08:00
long ret ;
2007-10-16 01:27:06 -07:00
int pid = task - > mm - > context . id . u . pid ;
2005-04-16 15:20:36 -07:00
2007-02-10 01:44:29 -08:00
/*
* With ARCH_SET_FS ( and ARCH_SET_GS is treated similarly to
* be safe ) , we need to call arch_prctl on the host because
* setting % fs may result in something else happening ( like a
2007-02-10 01:44:30 -08:00
* GDT or thread . fs being set instead ) . So , we let the host
* fiddle the registers and thread struct and restore the
* registers afterwards .
2007-02-10 01:44:29 -08:00
*
* So , the saved registers are stored to the process ( this
* needed because a stub may have been the last thing to run ) ,
* arch_prctl is run on the host , then the registers are read
* back .
*/
2007-10-16 01:27:00 -07:00
switch ( code ) {
2005-04-16 15:20:36 -07:00
case ARCH_SET_FS :
2005-05-28 15:52:03 -07:00
case ARCH_SET_GS :
2007-10-16 01:27:00 -07:00
restore_registers ( pid , & current - > thread . regs . regs ) ;
break ;
case ARCH_GET_FS :
case ARCH_GET_GS :
/*
* With these two , we read to a local pointer and
* put_user it to the userspace pointer that we were
* given . If addr isn ' t valid ( because it hasn ' t been
* faulted in or is just bogus ) , we want put_user to
* fault it in ( or return - EFAULT ) instead of having
* the host return - EFAULT .
*/
ptr = & tmp ;
}
2007-02-10 01:44:29 -08:00
2007-10-16 01:27:00 -07:00
ret = os_arch_prctl ( pid , code , ptr ) ;
if ( ret )
return ret ;
2007-02-10 01:44:29 -08:00
2007-10-16 01:27:00 -07:00
switch ( code ) {
2007-02-10 01:44:29 -08:00
case ARCH_SET_FS :
2007-03-07 20:41:26 -08:00
current - > thread . arch . fs = ( unsigned long ) ptr ;
save_registers ( pid , & current - > thread . regs . regs ) ;
break ;
2007-02-10 01:44:29 -08:00
case ARCH_SET_GS :
2007-10-16 01:27:00 -07:00
save_registers ( pid , & current - > thread . regs . regs ) ;
2005-05-28 15:52:03 -07:00
break ;
2005-04-16 15:20:36 -07:00
case ARCH_GET_FS :
2007-02-10 01:44:29 -08:00
ret = put_user ( tmp , addr ) ;
2007-10-16 01:27:00 -07:00
break ;
2005-04-16 15:20:36 -07:00
case ARCH_GET_GS :
2007-02-10 01:44:29 -08:00
ret = put_user ( tmp , addr ) ;
2007-10-16 01:27:00 -07:00
break ;
2005-04-16 15:20:36 -07:00
}
2007-02-10 01:44:29 -08:00
return ret ;
2005-04-16 15:20:36 -07:00
}
long sys_arch_prctl ( int code , unsigned long addr )
{
2007-10-16 01:26:58 -07:00
return arch_prctl ( current , code , ( unsigned long __user * ) addr ) ;
2005-04-16 15:20:36 -07:00
}
long sys_clone ( unsigned long clone_flags , unsigned long newsp ,
void __user * parent_tid , void __user * child_tid )
{
long ret ;
2005-06-25 14:55:21 -07:00
if ( ! newsp )
newsp = UPT_SP ( & current - > thread . regs . regs ) ;
2005-04-16 15:20:36 -07:00
current - > thread . forking = 1 ;
2005-06-25 14:55:21 -07:00
ret = do_fork ( clone_flags , newsp , & current - > thread . regs , 0 , parent_tid ,
child_tid ) ;
2005-04-16 15:20:36 -07:00
current - > thread . forking = 0 ;
2007-02-10 01:44:29 -08:00
return ret ;
}
2007-10-16 01:26:58 -07:00
void arch_switch_to ( struct task_struct * from , struct task_struct * to )
2007-02-10 01:44:29 -08:00
{
2007-10-16 01:27:00 -07:00
if ( ( to - > thread . arch . fs = = 0 ) | | ( to - > mm = = NULL ) )
return ;
2007-02-10 01:44:29 -08:00
2007-10-16 01:27:00 -07:00
arch_prctl ( to , ARCH_SET_FS , ( void __user * ) to - > thread . arch . fs ) ;
2005-04-16 15:20:36 -07:00
}