2005-04-17 02:20:36 +04:00
/*
* This contains the io - permission bitmap code - written by obz , with changes
2008-01-30 15:33:10 +03:00
* by Linus . 32 / 64 bits code unification by Miguel Botón .
2005-04-17 02:20:36 +04:00
*/
# include <linux/sched.h>
# include <linux/kernel.h>
2006-01-11 23:17:48 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/ioport.h>
# include <linux/smp.h>
# include <linux/stddef.h>
# include <linux/slab.h>
# include <linux/thread_info.h>
2007-05-02 21:27:10 +04:00
# include <linux/syscalls.h>
2011-02-16 17:48:35 +03:00
# include <linux/bitmap.h>
2008-07-21 20:04:13 +04:00
# include <asm/syscalls.h>
2005-04-17 02:20:36 +04:00
/*
* this changes the io permissions bitmap in the current task .
*/
asmlinkage long sys_ioperm ( unsigned long from , unsigned long num , int turn_on )
{
2009-01-04 13:59:32 +03:00
struct thread_struct * t = & current - > thread ;
struct tss_struct * tss ;
2008-01-30 15:33:10 +03:00
unsigned int i , max_long , bytes , bytes_updated ;
2005-04-17 02:20:36 +04:00
if ( ( from + num < = from ) | | ( from + num > IO_BITMAP_BITS ) )
return - EINVAL ;
if ( turn_on & & ! capable ( CAP_SYS_RAWIO ) )
return - EPERM ;
/*
* If it ' s the first ioperm ( ) call in this thread ' s lifetime , set the
* IO bitmap up . ioperm ( ) is much less timing critical than clone ( ) ,
* this is why we delay this operation until now :
*/
if ( ! t - > io_bitmap_ptr ) {
2008-01-30 15:30:24 +03:00
unsigned long * bitmap = kmalloc ( IO_BITMAP_BYTES , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! bitmap )
return - ENOMEM ;
memset ( bitmap , 0xff , IO_BITMAP_BYTES ) ;
t - > io_bitmap_ptr = bitmap ;
2006-07-10 05:12:39 +04:00
set_thread_flag ( TIF_IO_BITMAP ) ;
2005-04-17 02:20:36 +04:00
}
/*
* do it in the per - thread copy and in the TSS . . .
*
* Disable preemption via get_cpu ( ) - we must not switch away
* because the - > io_bitmap_max value must match the bitmap
* contents :
*/
tss = & per_cpu ( init_tss , get_cpu ( ) ) ;
2011-02-16 17:48:35 +03:00
if ( turn_on )
bitmap_clear ( t - > io_bitmap_ptr , from , num ) ;
else
bitmap_set ( t - > io_bitmap_ptr , from , num ) ;
2005-04-17 02:20:36 +04:00
/*
* Search for a ( possibly new ) maximum . This is simple and stupid ,
* to keep it obviously correct :
*/
max_long = 0 ;
for ( i = 0 ; i < IO_BITMAP_LONGS ; i + + )
if ( t - > io_bitmap_ptr [ i ] ! = ~ 0UL )
max_long = i ;
2008-01-30 15:33:10 +03:00
bytes = ( max_long + 1 ) * sizeof ( unsigned long ) ;
bytes_updated = max ( bytes , t - > io_bitmap_max ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:33:10 +03:00
t - > io_bitmap_max = bytes ;
/* Update the TSS: */
memcpy ( tss - > io_bitmap , t - > io_bitmap_ptr , bytes_updated ) ;
2005-04-17 02:20:36 +04:00
put_cpu ( ) ;
return 0 ;
}
/*
* sys_iopl has to be used when you want to access the IO ports
* beyond the 0x3ff range : to get the full 65536 ports bitmapped
* you ' d need 8 kB of bitmaps / process , which is a bit excessive .
*
2008-01-30 15:30:56 +03:00
* Here we just change the flags value on the stack : we allow
2005-04-17 02:20:36 +04:00
* only the super - user to do it . This depends on the stack - layout
* on system - call entry - see also fork ( ) and the signal handling
* code .
*/
2012-11-20 07:00:52 +04:00
SYSCALL_DEFINE1 ( iopl , unsigned int , level )
2005-04-17 02:20:36 +04:00
{
2012-11-20 07:00:52 +04:00
struct pt_regs * regs = current_pt_regs ( ) ;
2008-01-30 15:30:56 +03:00
unsigned int old = ( regs - > flags > > 12 ) & 3 ;
2009-12-10 03:01:52 +03:00
struct thread_struct * t = & current - > thread ;
2005-04-17 02:20:36 +04:00
if ( level > 3 )
return - EINVAL ;
/* Trying to gain more privileges? */
if ( level > old ) {
if ( ! capable ( CAP_SYS_RAWIO ) )
return - EPERM ;
}
2008-01-30 15:33:10 +03:00
regs - > flags = ( regs - > flags & ~ X86_EFLAGS_IOPL ) | ( level < < 12 ) ;
2008-01-30 15:33:10 +03:00
t - > iopl = level < < 12 ;
set_iopl_mask ( t - > iopl ) ;
2009-12-10 03:01:52 +03:00
return 0 ;
}