From: Zachary Amsden The pushf/popf in switch_to are ONLY used to switch IOPL. Making this explicit in C code is more clear. This pushf/popf pair was added as a bugfix for leaking IOPL to unprivileged processes when using sysenter/sysexit based system calls (sysexit does not restore flags). When requesting an IOPL change in sys_iopl(), it is just as easy to change the current flags and the flags in the stack image (in case an IRET is required), but there is no reason to force an IRET if we came in from the SYSENTER path. This change is the minimal solution for supporting a paravirtualized Linux kernel that allows user processes to run with I/O privilege. Other solutions require radical rewrites of part of the low level fault / system call handling code, or do not fully support sysenter based system calls. Unfortunately, this added one field to the thread_struct. But as a bonus, on P4, the fastest time measured for switch_to() went from 312 to 260 cycles, a win of about 17% in the fast case through this performance critical path. Signed-off-by: Zachary Amsden Signed-off-by: Andrew Morton --- arch/i386/kernel/ioport.c | 7 ++++--- arch/i386/kernel/process.c | 6 ++++++ include/asm-i386/processor.h | 16 ++++++++++++++++ include/asm-i386/system.h | 4 +--- 4 files changed, 27 insertions(+), 6 deletions(-) diff -puN arch/i386/kernel/ioport.c~x86-make-iopl-explicit arch/i386/kernel/ioport.c --- devel/arch/i386/kernel/ioport.c~x86-make-iopl-explicit 2005-08-03 23:51:49.000000000 -0700 +++ devel-akpm/arch/i386/kernel/ioport.c 2005-08-03 23:51:49.000000000 -0700 @@ -132,6 +132,7 @@ asmlinkage long sys_iopl(unsigned long u volatile struct pt_regs * regs = (struct pt_regs *) &unused; unsigned int level = regs->ebx; unsigned int old = (regs->eflags >> 12) & 3; + struct thread_struct *t = ¤t->thread; if (level > 3) return -EINVAL; @@ -140,8 +141,8 @@ asmlinkage long sys_iopl(unsigned long u if (!capable(CAP_SYS_RAWIO)) return -EPERM; } - regs->eflags = (regs->eflags &~ 0x3000UL) | (level << 12); - /* Make sure we return the long way (not sysenter) */ - set_thread_flag(TIF_IRET); + t->iopl = level << 12; + regs->eflags = (regs->eflags & ~X86_EFLAGS_IOPL) | t->iopl; + set_iopl_mask(t->iopl); return 0; } diff -puN arch/i386/kernel/process.c~x86-make-iopl-explicit arch/i386/kernel/process.c --- devel/arch/i386/kernel/process.c~x86-make-iopl-explicit 2005-08-03 23:51:49.000000000 -0700 +++ devel-akpm/arch/i386/kernel/process.c 2005-08-04 00:17:29.000000000 -0700 @@ -712,6 +712,12 @@ struct task_struct fastcall * __switch_t loadsegment(gs, next->gs); /* + * Restore IOPL if needed. + */ + if (unlikely(prev->iopl != next->iopl)) + set_iopl_mask(next->iopl); + + /* * Now maybe reload the debug registers */ if (unlikely(next->debugreg[7])) { diff -puN include/asm-i386/processor.h~x86-make-iopl-explicit include/asm-i386/processor.h --- devel/include/asm-i386/processor.h~x86-make-iopl-explicit 2005-08-03 23:51:49.000000000 -0700 +++ devel-akpm/include/asm-i386/processor.h 2005-08-04 00:20:02.000000000 -0700 @@ -455,6 +455,7 @@ struct thread_struct { unsigned int saved_fs, saved_gs; /* IO permissions */ unsigned long *io_bitmap_ptr; + unsigned long iopl; /* max allowed port in the bitmap, in bytes: */ unsigned long io_bitmap_max; }; @@ -511,6 +512,21 @@ static inline void load_esp0(struct tss_ : /* no output */ \ :"r" (value)) +/* + * Set IOPL bits in EFLAGS from given mask + */ +static inline void set_iopl_mask(unsigned mask) +{ + unsigned int reg; + __asm__ __volatile__ ("pushfl;" + "popl %0;" + "andl %1, %0;" + "orl %2, %0;" + "pushl %0;" + "popfl" + : "=&r" (reg) + : "i" (~X86_EFLAGS_IOPL), "r" (mask)); +} /* Forward declaration, a strange C thing */ struct task_struct; diff -puN include/asm-i386/system.h~x86-make-iopl-explicit include/asm-i386/system.h --- devel/include/asm-i386/system.h~x86-make-iopl-explicit 2005-08-03 23:51:49.000000000 -0700 +++ devel-akpm/include/asm-i386/system.h 2005-08-04 00:17:29.000000000 -0700 @@ -14,8 +14,7 @@ extern struct task_struct * FASTCALL(__s #define switch_to(prev,next,last) do { \ unsigned long esi,edi; \ - asm volatile("pushfl\n\t" \ - "pushl %%ebp\n\t" \ + asm volatile("pushl %%ebp\n\t" \ "movl %%esp,%0\n\t" /* save ESP */ \ "movl %5,%%esp\n\t" /* restore ESP */ \ "movl $1f,%1\n\t" /* save EIP */ \ @@ -23,7 +22,6 @@ extern struct task_struct * FASTCALL(__s "jmp __switch_to\n" \ "1:\t" \ "popl %%ebp\n\t" \ - "popfl" \ :"=m" (prev->thread.esp),"=m" (prev->thread.eip), \ "=a" (last),"=S" (esi),"=D" (edi) \ :"m" (next->thread.esp),"m" (next->thread.eip), \ _