1 | /* |
2 | * x86 SVM helpers |
3 | * |
4 | * Copyright (c) 2003 Fabrice Bellard |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "qemu/osdep.h" |
21 | #include "cpu.h" |
22 | #include "exec/helper-proto.h" |
23 | #include "exec/exec-all.h" |
24 | #include "exec/cpu_ldst.h" |
25 | |
26 | /* Secure Virtual Machine helpers */ |
27 | |
28 | #if defined(CONFIG_USER_ONLY) |
29 | |
30 | void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) |
31 | { |
32 | } |
33 | |
34 | void helper_vmmcall(CPUX86State *env) |
35 | { |
36 | } |
37 | |
38 | void helper_vmload(CPUX86State *env, int aflag) |
39 | { |
40 | } |
41 | |
42 | void helper_vmsave(CPUX86State *env, int aflag) |
43 | { |
44 | } |
45 | |
46 | void helper_stgi(CPUX86State *env) |
47 | { |
48 | } |
49 | |
50 | void helper_clgi(CPUX86State *env) |
51 | { |
52 | } |
53 | |
54 | void helper_skinit(CPUX86State *env) |
55 | { |
56 | } |
57 | |
58 | void helper_invlpga(CPUX86State *env, int aflag) |
59 | { |
60 | } |
61 | |
62 | void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, |
63 | uintptr_t retaddr) |
64 | { |
65 | assert(0); |
66 | } |
67 | |
68 | void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, |
69 | uint64_t param) |
70 | { |
71 | } |
72 | |
73 | void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, |
74 | uint64_t param, uintptr_t retaddr) |
75 | { |
76 | } |
77 | |
78 | void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, |
79 | uint32_t next_eip_addend) |
80 | { |
81 | } |
82 | #else |
83 | |
84 | static inline void svm_save_seg(CPUX86State *env, hwaddr addr, |
85 | const SegmentCache *sc) |
86 | { |
87 | CPUState *cs = env_cpu(env); |
88 | |
89 | x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector), |
90 | sc->selector); |
91 | x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base), |
92 | sc->base); |
93 | x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit), |
94 | sc->limit); |
95 | x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib), |
96 | ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); |
97 | } |
98 | |
99 | static inline void svm_load_seg(CPUX86State *env, hwaddr addr, |
100 | SegmentCache *sc) |
101 | { |
102 | CPUState *cs = env_cpu(env); |
103 | unsigned int flags; |
104 | |
105 | sc->selector = x86_lduw_phys(cs, |
106 | addr + offsetof(struct vmcb_seg, selector)); |
107 | sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base)); |
108 | sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit)); |
109 | flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib)); |
110 | sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); |
111 | } |
112 | |
113 | static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr, |
114 | int seg_reg) |
115 | { |
116 | SegmentCache sc1, *sc = &sc1; |
117 | |
118 | svm_load_seg(env, addr, sc); |
119 | cpu_x86_load_seg_cache(env, seg_reg, sc->selector, |
120 | sc->base, sc->limit, sc->flags); |
121 | } |
122 | |
123 | void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) |
124 | { |
125 | CPUState *cs = env_cpu(env); |
126 | target_ulong addr; |
127 | uint64_t nested_ctl; |
128 | uint32_t event_inj; |
129 | uint32_t int_ctl; |
130 | |
131 | cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); |
132 | |
133 | if (aflag == 2) { |
134 | addr = env->regs[R_EAX]; |
135 | } else { |
136 | addr = (uint32_t)env->regs[R_EAX]; |
137 | } |
138 | |
139 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n" , addr); |
140 | |
141 | env->vm_vmcb = addr; |
142 | |
143 | /* save the current CPU state in the hsave page */ |
144 | x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), |
145 | env->gdt.base); |
146 | x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), |
147 | env->gdt.limit); |
148 | |
149 | x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base), |
150 | env->idt.base); |
151 | x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), |
152 | env->idt.limit); |
153 | |
154 | x86_stq_phys(cs, |
155 | env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]); |
156 | x86_stq_phys(cs, |
157 | env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]); |
158 | x86_stq_phys(cs, |
159 | env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]); |
160 | x86_stq_phys(cs, |
161 | env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]); |
162 | x86_stq_phys(cs, |
163 | env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]); |
164 | x86_stq_phys(cs, |
165 | env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]); |
166 | |
167 | x86_stq_phys(cs, |
168 | env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer); |
169 | x86_stq_phys(cs, |
170 | env->vm_hsave + offsetof(struct vmcb, save.rflags), |
171 | cpu_compute_eflags(env)); |
172 | |
173 | svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.es), |
174 | &env->segs[R_ES]); |
175 | svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.cs), |
176 | &env->segs[R_CS]); |
177 | svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ss), |
178 | &env->segs[R_SS]); |
179 | svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds), |
180 | &env->segs[R_DS]); |
181 | |
182 | x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip), |
183 | env->eip + next_eip_addend); |
184 | x86_stq_phys(cs, |
185 | env->vm_hsave + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); |
186 | x86_stq_phys(cs, |
187 | env->vm_hsave + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); |
188 | |
189 | /* load the interception bitmaps so we do not need to access the |
190 | vmcb in svm mode */ |
191 | env->intercept = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
192 | control.intercept)); |
193 | env->intercept_cr_read = x86_lduw_phys(cs, env->vm_vmcb + |
194 | offsetof(struct vmcb, |
195 | control.intercept_cr_read)); |
196 | env->intercept_cr_write = x86_lduw_phys(cs, env->vm_vmcb + |
197 | offsetof(struct vmcb, |
198 | control.intercept_cr_write)); |
199 | env->intercept_dr_read = x86_lduw_phys(cs, env->vm_vmcb + |
200 | offsetof(struct vmcb, |
201 | control.intercept_dr_read)); |
202 | env->intercept_dr_write = x86_lduw_phys(cs, env->vm_vmcb + |
203 | offsetof(struct vmcb, |
204 | control.intercept_dr_write)); |
205 | env->intercept_exceptions = x86_ldl_phys(cs, env->vm_vmcb + |
206 | offsetof(struct vmcb, |
207 | control.intercept_exceptions |
208 | )); |
209 | |
210 | nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
211 | control.nested_ctl)); |
212 | if (nested_ctl & SVM_NPT_ENABLED) { |
213 | env->nested_cr3 = x86_ldq_phys(cs, |
214 | env->vm_vmcb + offsetof(struct vmcb, |
215 | control.nested_cr3)); |
216 | env->hflags2 |= HF2_NPT_MASK; |
217 | |
218 | env->nested_pg_mode = 0; |
219 | if (env->cr[4] & CR4_PAE_MASK) { |
220 | env->nested_pg_mode |= SVM_NPT_PAE; |
221 | } |
222 | if (env->hflags & HF_LMA_MASK) { |
223 | env->nested_pg_mode |= SVM_NPT_LMA; |
224 | } |
225 | if (env->efer & MSR_EFER_NXE) { |
226 | env->nested_pg_mode |= SVM_NPT_NXE; |
227 | } |
228 | } |
229 | |
230 | /* enable intercepts */ |
231 | env->hflags |= HF_GUEST_MASK; |
232 | |
233 | env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb + |
234 | offsetof(struct vmcb, control.tsc_offset)); |
235 | |
236 | env->gdt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
237 | save.gdtr.base)); |
238 | env->gdt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
239 | save.gdtr.limit)); |
240 | |
241 | env->idt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
242 | save.idtr.base)); |
243 | env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
244 | save.idtr.limit)); |
245 | |
246 | /* clear exit_info_2 so we behave like the real hardware */ |
247 | x86_stq_phys(cs, |
248 | env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0); |
249 | |
250 | cpu_x86_update_cr0(env, x86_ldq_phys(cs, |
251 | env->vm_vmcb + offsetof(struct vmcb, |
252 | save.cr0))); |
253 | cpu_x86_update_cr4(env, x86_ldq_phys(cs, |
254 | env->vm_vmcb + offsetof(struct vmcb, |
255 | save.cr4))); |
256 | cpu_x86_update_cr3(env, x86_ldq_phys(cs, |
257 | env->vm_vmcb + offsetof(struct vmcb, |
258 | save.cr3))); |
259 | env->cr[2] = x86_ldq_phys(cs, |
260 | env->vm_vmcb + offsetof(struct vmcb, save.cr2)); |
261 | int_ctl = x86_ldl_phys(cs, |
262 | env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); |
263 | env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); |
264 | if (int_ctl & V_INTR_MASKING_MASK) { |
265 | env->v_tpr = int_ctl & V_TPR_MASK; |
266 | env->hflags2 |= HF2_VINTR_MASK; |
267 | if (env->eflags & IF_MASK) { |
268 | env->hflags2 |= HF2_HIF_MASK; |
269 | } |
270 | } |
271 | |
272 | cpu_load_efer(env, |
273 | x86_ldq_phys(cs, |
274 | env->vm_vmcb + offsetof(struct vmcb, save.efer))); |
275 | env->eflags = 0; |
276 | cpu_load_eflags(env, x86_ldq_phys(cs, |
277 | env->vm_vmcb + offsetof(struct vmcb, |
278 | save.rflags)), |
279 | ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); |
280 | |
281 | svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es), |
282 | R_ES); |
283 | svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), |
284 | R_CS); |
285 | svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), |
286 | R_SS); |
287 | svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), |
288 | R_DS); |
289 | |
290 | env->eip = x86_ldq_phys(cs, |
291 | env->vm_vmcb + offsetof(struct vmcb, save.rip)); |
292 | |
293 | env->regs[R_ESP] = x86_ldq_phys(cs, |
294 | env->vm_vmcb + offsetof(struct vmcb, save.rsp)); |
295 | env->regs[R_EAX] = x86_ldq_phys(cs, |
296 | env->vm_vmcb + offsetof(struct vmcb, save.rax)); |
297 | env->dr[7] = x86_ldq_phys(cs, |
298 | env->vm_vmcb + offsetof(struct vmcb, save.dr7)); |
299 | env->dr[6] = x86_ldq_phys(cs, |
300 | env->vm_vmcb + offsetof(struct vmcb, save.dr6)); |
301 | |
302 | /* FIXME: guest state consistency checks */ |
303 | |
304 | switch (x86_ldub_phys(cs, |
305 | env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) { |
306 | case TLB_CONTROL_DO_NOTHING: |
307 | break; |
308 | case TLB_CONTROL_FLUSH_ALL_ASID: |
309 | /* FIXME: this is not 100% correct but should work for now */ |
310 | tlb_flush(cs); |
311 | break; |
312 | } |
313 | |
314 | env->hflags2 |= HF2_GIF_MASK; |
315 | |
316 | if (int_ctl & V_IRQ_MASK) { |
317 | CPUState *cs = env_cpu(env); |
318 | |
319 | cs->interrupt_request |= CPU_INTERRUPT_VIRQ; |
320 | } |
321 | |
322 | /* maybe we need to inject an event */ |
323 | event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
324 | control.event_inj)); |
325 | if (event_inj & SVM_EVTINJ_VALID) { |
326 | uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK; |
327 | uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR; |
328 | uint32_t event_inj_err = x86_ldl_phys(cs, env->vm_vmcb + |
329 | offsetof(struct vmcb, |
330 | control.event_inj_err)); |
331 | |
332 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): " , valid_err); |
333 | /* FIXME: need to implement valid_err */ |
334 | switch (event_inj & SVM_EVTINJ_TYPE_MASK) { |
335 | case SVM_EVTINJ_TYPE_INTR: |
336 | cs->exception_index = vector; |
337 | env->error_code = event_inj_err; |
338 | env->exception_is_int = 0; |
339 | env->exception_next_eip = -1; |
340 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR" ); |
341 | /* XXX: is it always correct? */ |
342 | do_interrupt_x86_hardirq(env, vector, 1); |
343 | break; |
344 | case SVM_EVTINJ_TYPE_NMI: |
345 | cs->exception_index = EXCP02_NMI; |
346 | env->error_code = event_inj_err; |
347 | env->exception_is_int = 0; |
348 | env->exception_next_eip = env->eip; |
349 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI" ); |
350 | cpu_loop_exit(cs); |
351 | break; |
352 | case SVM_EVTINJ_TYPE_EXEPT: |
353 | cs->exception_index = vector; |
354 | env->error_code = event_inj_err; |
355 | env->exception_is_int = 0; |
356 | env->exception_next_eip = -1; |
357 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT" ); |
358 | cpu_loop_exit(cs); |
359 | break; |
360 | case SVM_EVTINJ_TYPE_SOFT: |
361 | cs->exception_index = vector; |
362 | env->error_code = event_inj_err; |
363 | env->exception_is_int = 1; |
364 | env->exception_next_eip = env->eip; |
365 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT" ); |
366 | cpu_loop_exit(cs); |
367 | break; |
368 | } |
369 | qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n" , cs->exception_index, |
370 | env->error_code); |
371 | } |
372 | } |
373 | |
374 | void helper_vmmcall(CPUX86State *env) |
375 | { |
376 | cpu_svm_check_intercept_param(env, SVM_EXIT_VMMCALL, 0, GETPC()); |
377 | raise_exception(env, EXCP06_ILLOP); |
378 | } |
379 | |
380 | void helper_vmload(CPUX86State *env, int aflag) |
381 | { |
382 | CPUState *cs = env_cpu(env); |
383 | target_ulong addr; |
384 | |
385 | cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); |
386 | |
387 | if (aflag == 2) { |
388 | addr = env->regs[R_EAX]; |
389 | } else { |
390 | addr = (uint32_t)env->regs[R_EAX]; |
391 | } |
392 | |
393 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx |
394 | "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n" , |
395 | addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, |
396 | save.fs.base)), |
397 | env->segs[R_FS].base); |
398 | |
399 | svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.fs), R_FS); |
400 | svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.gs), R_GS); |
401 | svm_load_seg(env, addr + offsetof(struct vmcb, save.tr), &env->tr); |
402 | svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); |
403 | |
404 | #ifdef TARGET_X86_64 |
405 | env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb, |
406 | save.kernel_gs_base)); |
407 | env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar)); |
408 | env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar)); |
409 | env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask)); |
410 | #endif |
411 | env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star)); |
412 | env->sysenter_cs = x86_ldq_phys(cs, |
413 | addr + offsetof(struct vmcb, save.sysenter_cs)); |
414 | env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb, |
415 | save.sysenter_esp)); |
416 | env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb, |
417 | save.sysenter_eip)); |
418 | } |
419 | |
420 | void helper_vmsave(CPUX86State *env, int aflag) |
421 | { |
422 | CPUState *cs = env_cpu(env); |
423 | target_ulong addr; |
424 | |
425 | cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); |
426 | |
427 | if (aflag == 2) { |
428 | addr = env->regs[R_EAX]; |
429 | } else { |
430 | addr = (uint32_t)env->regs[R_EAX]; |
431 | } |
432 | |
433 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx |
434 | "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n" , |
435 | addr, x86_ldq_phys(cs, |
436 | addr + offsetof(struct vmcb, save.fs.base)), |
437 | env->segs[R_FS].base); |
438 | |
439 | svm_save_seg(env, addr + offsetof(struct vmcb, save.fs), |
440 | &env->segs[R_FS]); |
441 | svm_save_seg(env, addr + offsetof(struct vmcb, save.gs), |
442 | &env->segs[R_GS]); |
443 | svm_save_seg(env, addr + offsetof(struct vmcb, save.tr), |
444 | &env->tr); |
445 | svm_save_seg(env, addr + offsetof(struct vmcb, save.ldtr), |
446 | &env->ldt); |
447 | |
448 | #ifdef TARGET_X86_64 |
449 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base), |
450 | env->kernelgsbase); |
451 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar); |
452 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar); |
453 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask); |
454 | #endif |
455 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star); |
456 | x86_stq_phys(cs, |
457 | addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs); |
458 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp), |
459 | env->sysenter_esp); |
460 | x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip), |
461 | env->sysenter_eip); |
462 | } |
463 | |
464 | void helper_stgi(CPUX86State *env) |
465 | { |
466 | cpu_svm_check_intercept_param(env, SVM_EXIT_STGI, 0, GETPC()); |
467 | env->hflags2 |= HF2_GIF_MASK; |
468 | } |
469 | |
470 | void helper_clgi(CPUX86State *env) |
471 | { |
472 | cpu_svm_check_intercept_param(env, SVM_EXIT_CLGI, 0, GETPC()); |
473 | env->hflags2 &= ~HF2_GIF_MASK; |
474 | } |
475 | |
476 | void helper_skinit(CPUX86State *env) |
477 | { |
478 | cpu_svm_check_intercept_param(env, SVM_EXIT_SKINIT, 0, GETPC()); |
479 | /* XXX: not implemented */ |
480 | raise_exception(env, EXCP06_ILLOP); |
481 | } |
482 | |
483 | void helper_invlpga(CPUX86State *env, int aflag) |
484 | { |
485 | X86CPU *cpu = env_archcpu(env); |
486 | target_ulong addr; |
487 | |
488 | cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPGA, 0, GETPC()); |
489 | |
490 | if (aflag == 2) { |
491 | addr = env->regs[R_EAX]; |
492 | } else { |
493 | addr = (uint32_t)env->regs[R_EAX]; |
494 | } |
495 | |
496 | /* XXX: could use the ASID to see if it is needed to do the |
497 | flush */ |
498 | tlb_flush_page(CPU(cpu), addr); |
499 | } |
500 | |
501 | void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, |
502 | uint64_t param, uintptr_t retaddr) |
503 | { |
504 | CPUState *cs = env_cpu(env); |
505 | |
506 | if (likely(!(env->hflags & HF_GUEST_MASK))) { |
507 | return; |
508 | } |
509 | switch (type) { |
510 | case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8: |
511 | if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) { |
512 | cpu_vmexit(env, type, param, retaddr); |
513 | } |
514 | break; |
515 | case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8: |
516 | if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) { |
517 | cpu_vmexit(env, type, param, retaddr); |
518 | } |
519 | break; |
520 | case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7: |
521 | if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) { |
522 | cpu_vmexit(env, type, param, retaddr); |
523 | } |
524 | break; |
525 | case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7: |
526 | if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) { |
527 | cpu_vmexit(env, type, param, retaddr); |
528 | } |
529 | break; |
530 | case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31: |
531 | if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) { |
532 | cpu_vmexit(env, type, param, retaddr); |
533 | } |
534 | break; |
535 | case SVM_EXIT_MSR: |
536 | if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) { |
537 | /* FIXME: this should be read in at vmrun (faster this way?) */ |
538 | uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + |
539 | offsetof(struct vmcb, |
540 | control.msrpm_base_pa)); |
541 | uint32_t t0, t1; |
542 | |
543 | switch ((uint32_t)env->regs[R_ECX]) { |
544 | case 0 ... 0x1fff: |
545 | t0 = (env->regs[R_ECX] * 2) % 8; |
546 | t1 = (env->regs[R_ECX] * 2) / 8; |
547 | break; |
548 | case 0xc0000000 ... 0xc0001fff: |
549 | t0 = (8192 + env->regs[R_ECX] - 0xc0000000) * 2; |
550 | t1 = (t0 / 8); |
551 | t0 %= 8; |
552 | break; |
553 | case 0xc0010000 ... 0xc0011fff: |
554 | t0 = (16384 + env->regs[R_ECX] - 0xc0010000) * 2; |
555 | t1 = (t0 / 8); |
556 | t0 %= 8; |
557 | break; |
558 | default: |
559 | cpu_vmexit(env, type, param, retaddr); |
560 | t0 = 0; |
561 | t1 = 0; |
562 | break; |
563 | } |
564 | if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { |
565 | cpu_vmexit(env, type, param, retaddr); |
566 | } |
567 | } |
568 | break; |
569 | default: |
570 | if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) { |
571 | cpu_vmexit(env, type, param, retaddr); |
572 | } |
573 | break; |
574 | } |
575 | } |
576 | |
577 | void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, |
578 | uint64_t param) |
579 | { |
580 | cpu_svm_check_intercept_param(env, type, param, GETPC()); |
581 | } |
582 | |
583 | void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, |
584 | uint32_t next_eip_addend) |
585 | { |
586 | CPUState *cs = env_cpu(env); |
587 | |
588 | if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) { |
589 | /* FIXME: this should be read in at vmrun (faster this way?) */ |
590 | uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + |
591 | offsetof(struct vmcb, control.iopm_base_pa)); |
592 | uint16_t mask = (1 << ((param >> 4) & 7)) - 1; |
593 | |
594 | if (x86_lduw_phys(cs, addr + port / 8) & (mask << (port & 7))) { |
595 | /* next env->eip */ |
596 | x86_stq_phys(cs, |
597 | env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), |
598 | env->eip + next_eip_addend); |
599 | cpu_vmexit(env, SVM_EXIT_IOIO, param | (port << 16), GETPC()); |
600 | } |
601 | } |
602 | } |
603 | |
604 | void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1, |
605 | uintptr_t retaddr) |
606 | { |
607 | CPUState *cs = env_cpu(env); |
608 | |
609 | cpu_restore_state(cs, retaddr, true); |
610 | |
611 | qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" |
612 | PRIx64 ", " TARGET_FMT_lx ")!\n" , |
613 | exit_code, exit_info_1, |
614 | x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
615 | control.exit_info_2)), |
616 | env->eip); |
617 | |
618 | cs->exception_index = EXCP_VMEXIT + exit_code; |
619 | env->error_code = exit_info_1; |
620 | |
621 | /* remove any pending exception */ |
622 | env->old_exception = -1; |
623 | cpu_loop_exit(cs); |
624 | } |
625 | |
626 | void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) |
627 | { |
628 | CPUState *cs = env_cpu(env); |
629 | uint32_t int_ctl; |
630 | |
631 | if (env->hflags & HF_INHIBIT_IRQ_MASK) { |
632 | x86_stl_phys(cs, |
633 | env->vm_vmcb + offsetof(struct vmcb, control.int_state), |
634 | SVM_INTERRUPT_SHADOW_MASK); |
635 | env->hflags &= ~HF_INHIBIT_IRQ_MASK; |
636 | } else { |
637 | x86_stl_phys(cs, |
638 | env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); |
639 | } |
640 | env->hflags2 &= ~HF2_NPT_MASK; |
641 | |
642 | /* Save the VM state in the vmcb */ |
643 | svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), |
644 | &env->segs[R_ES]); |
645 | svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), |
646 | &env->segs[R_CS]); |
647 | svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), |
648 | &env->segs[R_SS]); |
649 | svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), |
650 | &env->segs[R_DS]); |
651 | |
652 | x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), |
653 | env->gdt.base); |
654 | x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), |
655 | env->gdt.limit); |
656 | |
657 | x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), |
658 | env->idt.base); |
659 | x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), |
660 | env->idt.limit); |
661 | |
662 | x86_stq_phys(cs, |
663 | env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer); |
664 | x86_stq_phys(cs, |
665 | env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]); |
666 | x86_stq_phys(cs, |
667 | env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]); |
668 | x86_stq_phys(cs, |
669 | env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]); |
670 | x86_stq_phys(cs, |
671 | env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]); |
672 | |
673 | int_ctl = x86_ldl_phys(cs, |
674 | env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); |
675 | int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK); |
676 | int_ctl |= env->v_tpr & V_TPR_MASK; |
677 | if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { |
678 | int_ctl |= V_IRQ_MASK; |
679 | } |
680 | x86_stl_phys(cs, |
681 | env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl); |
682 | |
683 | x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags), |
684 | cpu_compute_eflags(env)); |
685 | x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip), |
686 | env->eip); |
687 | x86_stq_phys(cs, |
688 | env->vm_vmcb + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); |
689 | x86_stq_phys(cs, |
690 | env->vm_vmcb + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); |
691 | x86_stq_phys(cs, |
692 | env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]); |
693 | x86_stq_phys(cs, |
694 | env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]); |
695 | x86_stb_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cpl), |
696 | env->hflags & HF_CPL_MASK); |
697 | |
698 | /* Reload the host state from vm_hsave */ |
699 | env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); |
700 | env->hflags &= ~HF_GUEST_MASK; |
701 | env->intercept = 0; |
702 | env->intercept_exceptions = 0; |
703 | cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; |
704 | env->tsc_offset = 0; |
705 | |
706 | env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, |
707 | save.gdtr.base)); |
708 | env->gdt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, |
709 | save.gdtr.limit)); |
710 | |
711 | env->idt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, |
712 | save.idtr.base)); |
713 | env->idt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, |
714 | save.idtr.limit)); |
715 | |
716 | cpu_x86_update_cr0(env, x86_ldq_phys(cs, |
717 | env->vm_hsave + offsetof(struct vmcb, |
718 | save.cr0)) | |
719 | CR0_PE_MASK); |
720 | cpu_x86_update_cr4(env, x86_ldq_phys(cs, |
721 | env->vm_hsave + offsetof(struct vmcb, |
722 | save.cr4))); |
723 | cpu_x86_update_cr3(env, x86_ldq_phys(cs, |
724 | env->vm_hsave + offsetof(struct vmcb, |
725 | save.cr3))); |
726 | /* we need to set the efer after the crs so the hidden flags get |
727 | set properly */ |
728 | cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, |
729 | save.efer))); |
730 | env->eflags = 0; |
731 | cpu_load_eflags(env, x86_ldq_phys(cs, |
732 | env->vm_hsave + offsetof(struct vmcb, |
733 | save.rflags)), |
734 | ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | |
735 | VM_MASK)); |
736 | |
737 | svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es), |
738 | R_ES); |
739 | svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.cs), |
740 | R_CS); |
741 | svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ss), |
742 | R_SS); |
743 | svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds), |
744 | R_DS); |
745 | |
746 | env->eip = x86_ldq_phys(cs, |
747 | env->vm_hsave + offsetof(struct vmcb, save.rip)); |
748 | env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_hsave + |
749 | offsetof(struct vmcb, save.rsp)); |
750 | env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_hsave + |
751 | offsetof(struct vmcb, save.rax)); |
752 | |
753 | env->dr[6] = x86_ldq_phys(cs, |
754 | env->vm_hsave + offsetof(struct vmcb, save.dr6)); |
755 | env->dr[7] = x86_ldq_phys(cs, |
756 | env->vm_hsave + offsetof(struct vmcb, save.dr7)); |
757 | |
758 | /* other setups */ |
759 | x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_code), |
760 | exit_code); |
761 | x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), |
762 | exit_info_1); |
763 | |
764 | x86_stl_phys(cs, |
765 | env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info), |
766 | x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
767 | control.event_inj))); |
768 | x86_stl_phys(cs, |
769 | env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err), |
770 | x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, |
771 | control.event_inj_err))); |
772 | x86_stl_phys(cs, |
773 | env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0); |
774 | |
775 | env->hflags2 &= ~HF2_GIF_MASK; |
776 | /* FIXME: Resets the current ASID register to zero (host ASID). */ |
777 | |
778 | /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ |
779 | |
780 | /* Clears the TSC_OFFSET inside the processor. */ |
781 | |
782 | /* If the host is in PAE mode, the processor reloads the host's PDPEs |
783 | from the page table indicated the host's CR3. If the PDPEs contain |
784 | illegal state, the processor causes a shutdown. */ |
785 | |
786 | /* Disables all breakpoints in the host DR7 register. */ |
787 | |
788 | /* Checks the reloaded host state for consistency. */ |
789 | |
790 | /* If the host's rIP reloaded by #VMEXIT is outside the limit of the |
791 | host's code segment or non-canonical (in the case of long mode), a |
792 | #GP fault is delivered inside the host. */ |
793 | } |
794 | |
795 | #endif |
796 | |