diff options
author | Jeremy Fitzhardinge <jeremy@goop.org> | 2008-03-17 19:37:17 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-04-24 17:57:31 -0400 |
commit | e2a81baf6604a2e08e10c7405b0349106f77c8af (patch) | |
tree | 3eaf386316be1f499d92fae213493ec3d6b5b576 /arch/x86 | |
parent | aa380c82b83252754a8c11bfc92359bd87cbf710 (diff) |
xen: support sysenter/sysexit if hypervisor does
64-bit Xen supports sysenter for 32-bit guests, so support its
use. (sysenter is faster than int $0x80 in 32-on-64.)
sysexit is still not supported, so we fake it up using iret.
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/kernel/entry_32.S | 18 | ||||
-rw-r--r-- | arch/x86/xen/enlighten.c | 3 | ||||
-rw-r--r-- | arch/x86/xen/setup.c | 21 | ||||
-rw-r--r-- | arch/x86/xen/smp.c | 1 | ||||
-rw-r--r-- | arch/x86/xen/xen-asm.S | 56 | ||||
-rw-r--r-- | arch/x86/xen/xen-ops.h | 3 |
6 files changed, 99 insertions, 3 deletions
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 568c6ccd7ae..5d80d53eaff 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
@@ -1017,6 +1017,13 @@ ENTRY(kernel_thread_helper) | |||
1017 | ENDPROC(kernel_thread_helper) | 1017 | ENDPROC(kernel_thread_helper) |
1018 | 1018 | ||
1019 | #ifdef CONFIG_XEN | 1019 | #ifdef CONFIG_XEN |
1020 | /* Xen doesn't set %esp to be precisely what the normal sysenter | ||
1021 | entrypoint expects, so fix it up before using the normal path. */ | ||
1022 | ENTRY(xen_sysenter_target) | ||
1023 | RING0_INT_FRAME | ||
1024 | addl $5*4, %esp /* remove xen-provided frame */ | ||
1025 | jmp sysenter_past_esp | ||
1026 | |||
1020 | ENTRY(xen_hypervisor_callback) | 1027 | ENTRY(xen_hypervisor_callback) |
1021 | CFI_STARTPROC | 1028 | CFI_STARTPROC |
1022 | pushl $0 | 1029 | pushl $0 |
@@ -1036,8 +1043,17 @@ ENTRY(xen_hypervisor_callback) | |||
1036 | jae 1f | 1043 | jae 1f |
1037 | 1044 | ||
1038 | call xen_iret_crit_fixup | 1045 | call xen_iret_crit_fixup |
1046 | jmp 2f | ||
1047 | |||
1048 | 1: cmpl $xen_sysexit_start_crit,%eax | ||
1049 | jb 2f | ||
1050 | cmpl $xen_sysexit_end_crit,%eax | ||
1051 | jae 2f | ||
1052 | |||
1053 | jmp xen_sysexit_crit_fixup | ||
1039 | 1054 | ||
1040 | 1: mov %esp, %eax | 1055 | ENTRY(xen_do_upcall) |
1056 | 2: mov %esp, %eax | ||
1041 | call xen_evtchn_do_upcall | 1057 | call xen_evtchn_do_upcall |
1042 | jmp ret_from_intr | 1058 | jmp ret_from_intr |
1043 | CFI_ENDPROC | 1059 | CFI_ENDPROC |
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 36f36e6b087..943684566eb 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c | |||
@@ -155,7 +155,6 @@ static void xen_cpuid(unsigned int *ax, unsigned int *bx, | |||
155 | if (*ax == 1) | 155 | if (*ax == 1) |
156 | maskedx = ~((1 << X86_FEATURE_APIC) | /* disable APIC */ | 156 | maskedx = ~((1 << X86_FEATURE_APIC) | /* disable APIC */ |
157 | (1 << X86_FEATURE_ACPI) | /* disable ACPI */ | 157 | (1 << X86_FEATURE_ACPI) | /* disable ACPI */ |
158 | (1 << X86_FEATURE_SEP) | /* disable SEP */ | ||
159 | (1 << X86_FEATURE_ACC)); /* thermal monitoring */ | 158 | (1 << X86_FEATURE_ACC)); /* thermal monitoring */ |
160 | 159 | ||
161 | asm(XEN_EMULATE_PREFIX "cpuid" | 160 | asm(XEN_EMULATE_PREFIX "cpuid" |
@@ -994,7 +993,7 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { | |||
994 | .read_pmc = native_read_pmc, | 993 | .read_pmc = native_read_pmc, |
995 | 994 | ||
996 | .iret = xen_iret, | 995 | .iret = xen_iret, |
997 | .irq_enable_syscall_ret = NULL, /* never called */ | 996 | .irq_enable_syscall_ret = xen_sysexit, |
998 | 997 | ||
999 | .load_tr_desc = paravirt_nop, | 998 | .load_tr_desc = paravirt_nop, |
1000 | .set_ldt = xen_set_ldt, | 999 | .set_ldt = xen_set_ldt, |
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 2341492bf7a..82517e4a752 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <asm/xen/hypervisor.h> | 16 | #include <asm/xen/hypervisor.h> |
17 | #include <asm/xen/hypercall.h> | 17 | #include <asm/xen/hypercall.h> |
18 | 18 | ||
19 | #include <xen/interface/callback.h> | ||
19 | #include <xen/interface/physdev.h> | 20 | #include <xen/interface/physdev.h> |
20 | #include <xen/features.h> | 21 | #include <xen/features.h> |
21 | 22 | ||
@@ -68,6 +69,24 @@ static void __init fiddle_vdso(void) | |||
68 | *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT; | 69 | *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT; |
69 | } | 70 | } |
70 | 71 | ||
72 | void xen_enable_sysenter(void) | ||
73 | { | ||
74 | int cpu = smp_processor_id(); | ||
75 | extern void xen_sysenter_target(void); | ||
76 | /* Mask events on entry, even though they get enabled immediately */ | ||
77 | static struct callback_register sysenter = { | ||
78 | .type = CALLBACKTYPE_sysenter, | ||
79 | .address = { __KERNEL_CS, (unsigned long)xen_sysenter_target }, | ||
80 | .flags = CALLBACKF_mask_events, | ||
81 | }; | ||
82 | |||
83 | if (!boot_cpu_has(X86_FEATURE_SEP) || | ||
84 | HYPERVISOR_callback_op(CALLBACKOP_register, &sysenter) != 0) { | ||
85 | clear_cpu_cap(&cpu_data(cpu), X86_FEATURE_SEP); | ||
86 | clear_cpu_cap(&boot_cpu_data, X86_FEATURE_SEP); | ||
87 | } | ||
88 | } | ||
89 | |||
71 | void __init xen_arch_setup(void) | 90 | void __init xen_arch_setup(void) |
72 | { | 91 | { |
73 | struct physdev_set_iopl set_iopl; | 92 | struct physdev_set_iopl set_iopl; |
@@ -82,6 +101,8 @@ void __init xen_arch_setup(void) | |||
82 | HYPERVISOR_set_callbacks(__KERNEL_CS, (unsigned long)xen_hypervisor_callback, | 101 | HYPERVISOR_set_callbacks(__KERNEL_CS, (unsigned long)xen_hypervisor_callback, |
83 | __KERNEL_CS, (unsigned long)xen_failsafe_callback); | 102 | __KERNEL_CS, (unsigned long)xen_failsafe_callback); |
84 | 103 | ||
104 | xen_enable_sysenter(); | ||
105 | |||
85 | set_iopl.iopl = 1; | 106 | set_iopl.iopl = 1; |
86 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); | 107 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); |
87 | if (rc != 0) | 108 | if (rc != 0) |
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index e340ff92f6b..d61e4f8b07c 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c | |||
@@ -72,6 +72,7 @@ static __cpuinit void cpu_bringup_and_idle(void) | |||
72 | int cpu = smp_processor_id(); | 72 | int cpu = smp_processor_id(); |
73 | 73 | ||
74 | cpu_init(); | 74 | cpu_init(); |
75 | xen_enable_sysenter(); | ||
75 | 76 | ||
76 | preempt_disable(); | 77 | preempt_disable(); |
77 | per_cpu(cpu_state, cpu) = CPU_ONLINE; | 78 | per_cpu(cpu_state, cpu) = CPU_ONLINE; |
diff --git a/arch/x86/xen/xen-asm.S b/arch/x86/xen/xen-asm.S index 99223cc323b..1ac08082a4b 100644 --- a/arch/x86/xen/xen-asm.S +++ b/arch/x86/xen/xen-asm.S | |||
@@ -280,6 +280,62 @@ ENTRY(xen_iret_crit_fixup) | |||
280 | 2: ret | 280 | 2: ret |
281 | 281 | ||
282 | 282 | ||
283 | ENTRY(xen_sysexit) | ||
284 | /* Store vcpu_info pointer for easy access. Do it this | ||
285 | way to avoid having to reload %fs */ | ||
286 | #ifdef CONFIG_SMP | ||
287 | GET_THREAD_INFO(%eax) | ||
288 | movl TI_cpu(%eax),%eax | ||
289 | movl __per_cpu_offset(,%eax,4),%eax | ||
290 | mov per_cpu__xen_vcpu(%eax),%eax | ||
291 | #else | ||
292 | movl per_cpu__xen_vcpu, %eax | ||
293 | #endif | ||
294 | |||
295 | /* We can't actually use sysexit in a pv guest, | ||
296 | so fake it up with iret */ | ||
297 | pushl $__USER_DS /* user stack segment */ | ||
298 | pushl %ecx /* user esp */ | ||
299 | pushl PT_EFLAGS+2*4(%esp) /* user eflags */ | ||
300 | pushl $__USER_CS /* user code segment */ | ||
301 | pushl %edx /* user eip */ | ||
302 | |||
303 | xen_sysexit_start_crit: | ||
304 | /* Unmask events... */ | ||
305 | movb $0, XEN_vcpu_info_mask(%eax) | ||
306 | /* ...and test for pending. | ||
307 | There's a preempt window here, but it doesn't | ||
308 | matter because we're within the critical section. */ | ||
309 | testb $0xff, XEN_vcpu_info_pending(%eax) | ||
310 | |||
311 | /* If there's something pending, mask events again so we | ||
312 | can directly inject it back into the kernel. */ | ||
313 | jnz 1f | ||
314 | |||
315 | movl PT_EAX+5*4(%esp),%eax | ||
316 | 2: iret | ||
317 | 1: movb $1, XEN_vcpu_info_mask(%eax) | ||
318 | xen_sysexit_end_crit: | ||
319 | addl $5*4, %esp /* remove iret frame */ | ||
320 | /* no need to re-save regs, but need to restore kernel %fs */ | ||
321 | mov $__KERNEL_PERCPU, %eax | ||
322 | mov %eax, %fs | ||
323 | jmp xen_do_upcall | ||
324 | .section __ex_table,"a" | ||
325 | .align 4 | ||
326 | .long 2b,iret_exc | ||
327 | .previous | ||
328 | |||
329 | .globl xen_sysexit_start_crit, xen_sysexit_end_crit | ||
330 | /* | ||
331 | sysexit fixup is easy, since the old frame is still sitting there | ||
332 | on the stack. We just need to remove the new recursive | ||
333 | interrupt and return. | ||
334 | */ | ||
335 | ENTRY(xen_sysexit_crit_fixup) | ||
336 | addl $PT_OLDESP+5*4, %esp /* remove frame+iret */ | ||
337 | jmp xen_do_upcall | ||
338 | |||
283 | /* | 339 | /* |
284 | Force an event check by making a hypercall, | 340 | Force an event check by making a hypercall, |
285 | but preserve regs before making the call. | 341 | but preserve regs before making the call. |
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index 956a491ea99..01d4ff2ce40 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h | |||
@@ -19,6 +19,7 @@ extern struct shared_info *HYPERVISOR_shared_info; | |||
19 | char * __init xen_memory_setup(void); | 19 | char * __init xen_memory_setup(void); |
20 | void __init xen_arch_setup(void); | 20 | void __init xen_arch_setup(void); |
21 | void __init xen_init_IRQ(void); | 21 | void __init xen_init_IRQ(void); |
22 | void xen_enable_sysenter(void); | ||
22 | 23 | ||
23 | void xen_setup_timer(int cpu); | 24 | void xen_setup_timer(int cpu); |
24 | void xen_setup_cpu_clockevents(void); | 25 | void xen_setup_cpu_clockevents(void); |
@@ -64,4 +65,6 @@ DECL_ASM(unsigned long, xen_save_fl_direct, void); | |||
64 | DECL_ASM(void, xen_restore_fl_direct, unsigned long); | 65 | DECL_ASM(void, xen_restore_fl_direct, unsigned long); |
65 | 66 | ||
66 | void xen_iret(void); | 67 | void xen_iret(void); |
68 | void xen_sysexit(void); | ||
69 | |||
67 | #endif /* XEN_OPS_H */ | 70 | #endif /* XEN_OPS_H */ |