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