[PATCH] Fix sysenter disabling in vm86 mode

From: Brian Gerst
Date: Mon Oct 27 2003 - 12:42:54 EST


The current code disables sysenter when first entering vm86 mode, but does not disable it again when coming back to a vm86 task after a task switch.

--
Brian Gerst
diff -urN linux-2.6.0-test8-bk/arch/i386/kernel/cpu/common.c linux/arch/i386/kernel/cpu/common.c
--- linux-2.6.0-test8-bk/arch/i386/kernel/cpu/common.c 2003-09-28 10:20:31.000000000 -0400
+++ linux/arch/i386/kernel/cpu/common.c 2003-10-22 10:10:39.932356584 -0400
@@ -510,7 +510,7 @@
BUG();
enter_lazy_tlb(&init_mm, current);

- load_esp0(t, thread->esp0);
+ load_esp0(t, thread);
set_tss_desc(cpu,t);
cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff;
load_TR_desc();
diff -urN linux-2.6.0-test8-bk/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- linux-2.6.0-test8-bk/arch/i386/kernel/process.c 2003-10-08 16:11:44.000000000 -0400
+++ linux/arch/i386/kernel/process.c 2003-10-22 10:10:39.933356432 -0400
@@ -507,7 +507,7 @@
/*
* Reload esp0, LDT and the page table pointer:
*/
- load_esp0(tss, next->esp0);
+ load_esp0(tss, next);

/*
* Load the per-thread Thread-Local Storage descriptor.
diff -urN linux-2.6.0-test8-bk/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- linux-2.6.0-test8-bk/arch/i386/kernel/vm86.c 2003-07-27 12:57:40.000000000 -0400
+++ linux/arch/i386/kernel/vm86.c 2003-10-22 10:10:39.934356280 -0400
@@ -117,7 +117,8 @@

tss = init_tss + get_cpu();
current->thread.esp0 = current->thread.saved_esp0;
- load_esp0(tss, current->thread.esp0);
+ current->thread.sysenter_cs = __KERNEL_CS;
+ load_esp0(tss, &current->thread);
current->thread.saved_esp0 = 0;
put_cpu();

@@ -294,8 +295,10 @@
asm volatile("movl %%gs,%0":"=m" (tsk->thread.saved_gs));

tss = init_tss + get_cpu();
- tss->esp0 = tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0;
- disable_sysenter(tss);
+ tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0;
+ if (cpu_has_sep)
+ tsk->thread.sysenter_cs = 0;
+ load_esp0(tss, &tsk->thread);
put_cpu();

tsk->thread.screen_bitmap = info->screen_bitmap;
diff -urN linux-2.6.0-test8-bk/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- linux-2.6.0-test8-bk/include/asm-i386/processor.h 2003-10-18 09:29:32.000000000 -0400
+++ linux/include/asm-i386/processor.h 2003-10-22 10:10:50.824700696 -0400
@@ -407,6 +407,7 @@
/* cached TLS descriptors. */
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
unsigned long esp0;
+ unsigned long sysenter_cs;
unsigned long eip;
unsigned long esp;
unsigned long fs;
@@ -428,6 +429,7 @@

#define INIT_THREAD { \
.vm86_info = NULL, \
+ .sysenter_cs = __KERNEL_CS, \
.io_bitmap_ptr = NULL, \
}

@@ -447,21 +449,13 @@
.io_bitmap = { [ 0 ... IO_BITMAP_LONGS] = ~0 }, \
}

-static inline void load_esp0(struct tss_struct *tss, unsigned long esp0)
+static inline void load_esp0(struct tss_struct *tss, struct thread_struct *thread)
{
- tss->esp0 = esp0;
+ tss->esp0 = thread->esp0;
/* This can only happen when SEP is enabled, no need to test "SEP"arately */
- if ((unlikely(tss->ss1 != __KERNEL_CS))) {
- tss->ss1 = __KERNEL_CS;
- wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
- }
-}
-
-static inline void disable_sysenter(struct tss_struct *tss)
-{
- if (cpu_has_sep) {
- tss->ss1 = 0;
- wrmsr(MSR_IA32_SYSENTER_CS, 0, 0);
+ if (unlikely(tss->ss1 != thread->sysenter_cs)) {
+ tss->ss1 = thread->sysenter_cs;
+ wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0);
}
}