diff options
author | Jeremy Fitzhardinge <jeremy@goop.org> | 2008-07-08 18:07:14 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-16 05:05:52 -0400 |
commit | 6fcac6d305e8238939e169f4c52e8ec8a552a31f (patch) | |
tree | b973a215938121d42bc586925d9712f205badea0 | |
parent | d6182fbf04164016cb6540db02eef3d6bdc967c3 (diff) |
xen64: set up syscall and sysenter entrypoints for 64-bit
We set up entrypoints for syscall and sysenter. sysenter is only used
for 32-bit compat processes, whereas syscall can be used in by both 32
and 64-bit processes.
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Cc: Stephen Tweedie <sct@redhat.com>
Cc: Eduardo Habkost <ehabkost@redhat.com>
Cc: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/xen/enlighten.c | 4 | ||||
-rw-r--r-- | arch/x86/xen/setup.c | 42 | ||||
-rw-r--r-- | arch/x86/xen/smp.c | 1 | ||||
-rw-r--r-- | arch/x86/xen/xen-asm_64.S | 129 | ||||
-rw-r--r-- | arch/x86/xen/xen-ops.h | 3 |
5 files changed, 174 insertions, 5 deletions
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 48f1a7eca8b9..87d36044054d 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c | |||
@@ -1139,6 +1139,10 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { | |||
1139 | 1139 | ||
1140 | .iret = xen_iret, | 1140 | .iret = xen_iret, |
1141 | .irq_enable_sysexit = xen_sysexit, | 1141 | .irq_enable_sysexit = xen_sysexit, |
1142 | #ifdef CONFIG_X86_64 | ||
1143 | .usergs_sysret32 = xen_sysret32, | ||
1144 | .usergs_sysret64 = xen_sysret64, | ||
1145 | #endif | ||
1142 | 1146 | ||
1143 | .load_tr_desc = paravirt_nop, | 1147 | .load_tr_desc = paravirt_nop, |
1144 | .set_ldt = xen_set_ldt, | 1148 | .set_ldt = xen_set_ldt, |
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index bea3d4f779db..9d7a14402895 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c | |||
@@ -86,9 +86,11 @@ static void xen_idle(void) | |||
86 | */ | 86 | */ |
87 | static void __init fiddle_vdso(void) | 87 | static void __init fiddle_vdso(void) |
88 | { | 88 | { |
89 | #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) | ||
89 | extern const char vdso32_default_start; | 90 | extern const char vdso32_default_start; |
90 | u32 *mask = VDSO32_SYMBOL(&vdso32_default_start, NOTE_MASK); | 91 | u32 *mask = VDSO32_SYMBOL(&vdso32_default_start, NOTE_MASK); |
91 | *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT; | 92 | *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT; |
93 | #endif | ||
92 | } | 94 | } |
93 | 95 | ||
94 | static __cpuinit int register_callback(unsigned type, const void *func) | 96 | static __cpuinit int register_callback(unsigned type, const void *func) |
@@ -106,15 +108,48 @@ void __cpuinit xen_enable_sysenter(void) | |||
106 | { | 108 | { |
107 | int cpu = smp_processor_id(); | 109 | int cpu = smp_processor_id(); |
108 | extern void xen_sysenter_target(void); | 110 | extern void xen_sysenter_target(void); |
111 | int ret; | ||
112 | |||
113 | #ifdef CONFIG_X86_32 | ||
114 | if (!boot_cpu_has(X86_FEATURE_SEP)) { | ||
115 | return; | ||
116 | } | ||
117 | #else | ||
118 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL && | ||
119 | boot_cpu_data.x86_vendor != X86_VENDOR_CENTAUR) { | ||
120 | return; | ||
121 | } | ||
122 | #endif | ||
109 | 123 | ||
110 | if (!boot_cpu_has(X86_FEATURE_SEP) || | 124 | ret = register_callback(CALLBACKTYPE_sysenter, xen_sysenter_target); |
111 | register_callback(CALLBACKTYPE_sysenter, | 125 | if(ret != 0) { |
112 | xen_sysenter_target) != 0) { | ||
113 | clear_cpu_cap(&cpu_data(cpu), X86_FEATURE_SEP); | 126 | clear_cpu_cap(&cpu_data(cpu), X86_FEATURE_SEP); |
114 | clear_cpu_cap(&boot_cpu_data, X86_FEATURE_SEP); | 127 | clear_cpu_cap(&boot_cpu_data, X86_FEATURE_SEP); |
115 | } | 128 | } |
116 | } | 129 | } |
117 | 130 | ||
131 | void __cpuinit xen_enable_syscall(void) | ||
132 | { | ||
133 | #ifdef CONFIG_X86_64 | ||
134 | int cpu = smp_processor_id(); | ||
135 | int ret; | ||
136 | extern void xen_syscall_target(void); | ||
137 | extern void xen_syscall32_target(void); | ||
138 | |||
139 | ret = register_callback(CALLBACKTYPE_syscall, xen_syscall_target); | ||
140 | if (ret != 0) { | ||
141 | printk("failed to set syscall: %d\n", ret); | ||
142 | clear_cpu_cap(&cpu_data(cpu), X86_FEATURE_SYSCALL); | ||
143 | clear_cpu_cap(&boot_cpu_data, X86_FEATURE_SYSCALL); | ||
144 | } else { | ||
145 | ret = register_callback(CALLBACKTYPE_syscall32, | ||
146 | xen_syscall32_target); | ||
147 | if (ret != 0) | ||
148 | printk("failed to set 32-bit syscall: %d\n", ret); | ||
149 | } | ||
150 | #endif /* CONFIG_X86_64 */ | ||
151 | } | ||
152 | |||
118 | void __init xen_arch_setup(void) | 153 | void __init xen_arch_setup(void) |
119 | { | 154 | { |
120 | struct physdev_set_iopl set_iopl; | 155 | struct physdev_set_iopl set_iopl; |
@@ -131,6 +166,7 @@ void __init xen_arch_setup(void) | |||
131 | BUG(); | 166 | BUG(); |
132 | 167 | ||
133 | xen_enable_sysenter(); | 168 | xen_enable_sysenter(); |
169 | xen_enable_syscall(); | ||
134 | 170 | ||
135 | set_iopl.iopl = 1; | 171 | set_iopl.iopl = 1; |
136 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); | 172 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); |
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 8310ca0ea375..f702199312a5 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c | |||
@@ -69,6 +69,7 @@ static __cpuinit void cpu_bringup_and_idle(void) | |||
69 | preempt_disable(); | 69 | preempt_disable(); |
70 | 70 | ||
71 | xen_enable_sysenter(); | 71 | xen_enable_sysenter(); |
72 | xen_enable_syscall(); | ||
72 | 73 | ||
73 | cpu = smp_processor_id(); | 74 | cpu = smp_processor_id(); |
74 | smp_store_cpu_info(cpu); | 75 | smp_store_cpu_info(cpu); |
diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S index b147b495daef..4038cbfe3331 100644 --- a/arch/x86/xen/xen-asm_64.S +++ b/arch/x86/xen/xen-asm_64.S | |||
@@ -15,6 +15,8 @@ | |||
15 | 15 | ||
16 | #include <asm/asm-offsets.h> | 16 | #include <asm/asm-offsets.h> |
17 | #include <asm/processor-flags.h> | 17 | #include <asm/processor-flags.h> |
18 | #include <asm/errno.h> | ||
19 | #include <asm/segment.h> | ||
18 | 20 | ||
19 | #include <xen/interface/xen.h> | 21 | #include <xen/interface/xen.h> |
20 | 22 | ||
@@ -138,9 +140,132 @@ ENTRY(xen_adjust_exception_frame) | |||
138 | mov 8+8(%rsp),%r11 | 140 | mov 8+8(%rsp),%r11 |
139 | ret $16 | 141 | ret $16 |
140 | 142 | ||
143 | hypercall_iret = hypercall_page + __HYPERVISOR_iret * 32 | ||
144 | /* | ||
145 | Xen64 iret frame: | ||
146 | |||
147 | ss | ||
148 | rsp | ||
149 | rflags | ||
150 | cs | ||
151 | rip <-- standard iret frame | ||
152 | |||
153 | flags | ||
154 | |||
155 | rcx } | ||
156 | r11 }<-- pushed by hypercall page | ||
157 | rsp -> rax } | ||
158 | */ | ||
141 | ENTRY(xen_iret) | 159 | ENTRY(xen_iret) |
142 | pushq $0 | 160 | pushq $0 |
143 | jmp hypercall_page + __HYPERVISOR_iret * 32 | 161 | 1: jmp hypercall_iret |
162 | ENDPATCH(xen_iret) | ||
163 | RELOC(xen_iret, 1b+1) | ||
144 | 164 | ||
165 | /* | ||
166 | sysexit is not used for 64-bit processes, so it's | ||
167 | only ever used to return to 32-bit compat userspace. | ||
168 | */ | ||
145 | ENTRY(xen_sysexit) | 169 | ENTRY(xen_sysexit) |
146 | ud2a | 170 | pushq $__USER32_DS |
171 | pushq %rcx | ||
172 | pushq $X86_EFLAGS_IF | ||
173 | pushq $__USER32_CS | ||
174 | pushq %rdx | ||
175 | |||
176 | pushq $VGCF_in_syscall | ||
177 | 1: jmp hypercall_iret | ||
178 | ENDPATCH(xen_sysexit) | ||
179 | RELOC(xen_sysexit, 1b+1) | ||
180 | |||
181 | ENTRY(xen_sysret64) | ||
182 | /* We're already on the usermode stack at this point, but still | ||
183 | with the kernel gs, so we can easily switch back */ | ||
184 | movq %rsp, %gs:pda_oldrsp | ||
185 | movq %gs:pda_kernelstack,%rsp | ||
186 | |||
187 | pushq $__USER_DS | ||
188 | pushq %gs:pda_oldrsp | ||
189 | pushq %r11 | ||
190 | pushq $__USER_CS | ||
191 | pushq %rcx | ||
192 | |||
193 | pushq $VGCF_in_syscall | ||
194 | 1: jmp hypercall_iret | ||
195 | ENDPATCH(xen_sysret64) | ||
196 | RELOC(xen_sysret64, 1b+1) | ||
197 | |||
198 | ENTRY(xen_sysret32) | ||
199 | /* We're already on the usermode stack at this point, but still | ||
200 | with the kernel gs, so we can easily switch back */ | ||
201 | movq %rsp, %gs:pda_oldrsp | ||
202 | movq %gs:pda_kernelstack, %rsp | ||
203 | |||
204 | pushq $__USER32_DS | ||
205 | pushq %gs:pda_oldrsp | ||
206 | pushq %r11 | ||
207 | pushq $__USER32_CS | ||
208 | pushq %rcx | ||
209 | |||
210 | pushq $VGCF_in_syscall | ||
211 | 1: jmp hypercall_iret | ||
212 | ENDPATCH(xen_sysret32) | ||
213 | RELOC(xen_sysret32, 1b+1) | ||
214 | |||
215 | /* | ||
216 | Xen handles syscall callbacks much like ordinary exceptions, | ||
217 | which means we have: | ||
218 | - kernel gs | ||
219 | - kernel rsp | ||
220 | - an iret-like stack frame on the stack (including rcx and r11): | ||
221 | ss | ||
222 | rsp | ||
223 | rflags | ||
224 | cs | ||
225 | rip | ||
226 | r11 | ||
227 | rsp-> rcx | ||
228 | |||
229 | In all the entrypoints, we undo all that to make it look | ||
230 | like a CPU-generated syscall/sysenter and jump to the normal | ||
231 | entrypoint. | ||
232 | */ | ||
233 | |||
234 | .macro undo_xen_syscall | ||
235 | mov 0*8(%rsp),%rcx | ||
236 | mov 1*8(%rsp),%r11 | ||
237 | mov 5*8(%rsp),%rsp | ||
238 | .endm | ||
239 | |||
240 | /* Normal 64-bit system call target */ | ||
241 | ENTRY(xen_syscall_target) | ||
242 | undo_xen_syscall | ||
243 | jmp system_call_after_swapgs | ||
244 | ENDPROC(xen_syscall_target) | ||
245 | |||
246 | #ifdef CONFIG_IA32_EMULATION | ||
247 | |||
248 | /* 32-bit compat syscall target */ | ||
249 | ENTRY(xen_syscall32_target) | ||
250 | undo_xen_syscall | ||
251 | jmp ia32_cstar_target | ||
252 | ENDPROC(xen_syscall32_target) | ||
253 | |||
254 | /* 32-bit compat sysenter target */ | ||
255 | ENTRY(xen_sysenter_target) | ||
256 | undo_xen_syscall | ||
257 | jmp ia32_sysenter_target | ||
258 | ENDPROC(xen_sysenter_target) | ||
259 | |||
260 | #else /* !CONFIG_IA32_EMULATION */ | ||
261 | |||
262 | ENTRY(xen_syscall32_target) | ||
263 | ENTRY(xen_sysenter_target) | ||
264 | lea 16(%rsp), %rsp /* strip %rcx,%r11 */ | ||
265 | mov $-ENOSYS, %rax | ||
266 | pushq $VGCF_in_syscall | ||
267 | jmp hypercall_iret | ||
268 | ENDPROC(xen_syscall32_target) | ||
269 | ENDPROC(xen_sysenter_target) | ||
270 | |||
271 | #endif /* CONFIG_IA32_EMULATION */ | ||
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index c4800a2c5a41..dd3c23152a2e 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h | |||
@@ -26,6 +26,7 @@ char * __init xen_memory_setup(void); | |||
26 | void __init xen_arch_setup(void); | 26 | void __init xen_arch_setup(void); |
27 | void __init xen_init_IRQ(void); | 27 | void __init xen_init_IRQ(void); |
28 | void xen_enable_sysenter(void); | 28 | void xen_enable_sysenter(void); |
29 | void xen_enable_syscall(void); | ||
29 | void xen_vcpu_restore(void); | 30 | void xen_vcpu_restore(void); |
30 | 31 | ||
31 | void __init xen_build_dynamic_phys_to_machine(void); | 32 | void __init xen_build_dynamic_phys_to_machine(void); |
@@ -70,6 +71,8 @@ DECL_ASM(void, xen_restore_fl_direct, unsigned long); | |||
70 | /* These are not functions, and cannot be called normally */ | 71 | /* These are not functions, and cannot be called normally */ |
71 | void xen_iret(void); | 72 | void xen_iret(void); |
72 | void xen_sysexit(void); | 73 | void xen_sysexit(void); |
74 | void xen_sysret32(void); | ||
75 | void xen_sysret64(void); | ||
73 | void xen_adjust_exception_frame(void); | 76 | void xen_adjust_exception_frame(void); |
74 | 77 | ||
75 | #endif /* XEN_OPS_H */ | 78 | #endif /* XEN_OPS_H */ |