2005-04-17 02:20:36 +04:00
/*
* This contains the io - permission bitmap code - written by obz , with changes
* by Linus .
*/
# 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>
2005-04-17 02:20:36 +04:00
/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
2008-01-30 15:30:23 +03:00
static void set_bitmap ( unsigned long * bitmap , unsigned int base ,
unsigned int extent , int new_value )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:30:23 +03:00
unsigned int i ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:23 +03:00
for ( i = base ; i < base + extent ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( new_value )
2008-01-30 15:30:23 +03:00
__set_bit ( i , bitmap ) ;
2005-04-17 02:20:36 +04:00
else
2008-01-30 15:30:23 +03:00
__clear_bit ( i , bitmap ) ;
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 )
{
struct thread_struct * t = & current - > thread ;
struct tss_struct * tss ;
2008-01-30 15:30:24 +03:00
unsigned long i , max_long ;
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 ( ) ) ;
set_bitmap ( t - > io_bitmap_ptr , from , num , ! turn_on ) ;
/*
* 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:30:24 +03:00
t - > io_bitmap_max = ( max_long + 1 ) * sizeof ( unsigned long ) ;
2005-04-17 02:20:36 +04:00
/*
* Sets the lazy trigger so that the next I / O operation will
* reload the correct bitmap .
2005-11-06 02:54:07 +03:00
* Reset the owner so that a process switch will not set
* tss - > io_bitmap_base to IO_BITMAP_OFFSET .
2005-04-17 02:20:36 +04:00
*/
2007-05-02 21:27:13 +04:00
tss - > x86_tss . io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY ;
2005-11-06 02:54:07 +03:00
tss - > io_bitmap_owner = NULL ;
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 .
*
* Here we just change the eflags value on the stack : we allow
* 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 .
*/
2008-01-30 15:30:24 +03:00
asmlinkage long sys_iopl ( unsigned long regsp )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:30:24 +03:00
volatile struct pt_regs * regs = ( struct pt_regs * ) & regsp ;
2005-04-17 02:20:36 +04:00
unsigned int level = regs - > ebx ;
unsigned int old = ( regs - > eflags > > 12 ) & 3 ;
2005-09-04 02:56:44 +04: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:30:24 +03:00
2005-09-04 02:56:44 +04:00
t - > iopl = level < < 12 ;
regs - > eflags = ( regs - > eflags & ~ X86_EFLAGS_IOPL ) | t - > iopl ;
set_iopl_mask ( t - > iopl ) ;
2008-01-30 15:30:24 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}