mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-05-30 01:27:16 +00:00
target-i386: defer VMEXIT to do_interrupt
Paths through the softmmu code during code generation now need to be audited to check for double locking of tb_lock. In particular, VMEXIT can take tb_lock through cpu_vmexit -> cpu_x86_update_cr4 -> tlb_flush. To avoid this, split VMEXIT delivery in two parts, similar to what is done with exceptions. cpu_vmexit only records the VMEXIT exit code and information, and cc->do_interrupt can then deliver it when it is safe to take the lock. Backports commit 10cde894b63146139f981857e4eedf756fa53dcb from qemu
This commit is contained in:
parent
ad548f8110
commit
bc7a9ccfbd
|
@ -695,6 +695,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
|
||||||
|
|
||||||
#define EXCP_SYSCALL 0x100 /* only happens in user only emulation
|
#define EXCP_SYSCALL 0x100 /* only happens in user only emulation
|
||||||
for syscall instruction */
|
for syscall instruction */
|
||||||
|
#define EXCP_VMEXIT 0x100
|
||||||
|
|
||||||
/* i386-specific interrupt pending bits. */
|
/* i386-specific interrupt pending bits. */
|
||||||
#define CPU_INTERRUPT_POLL CPU_INTERRUPT_TGT_EXT_1
|
#define CPU_INTERRUPT_POLL CPU_INTERRUPT_TGT_EXT_1
|
||||||
|
@ -1605,6 +1606,7 @@ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type,
|
||||||
uint64_t param, uintptr_t retaddr);
|
uint64_t param, uintptr_t retaddr);
|
||||||
void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1,
|
void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1,
|
||||||
uintptr_t retaddr);
|
uintptr_t retaddr);
|
||||||
|
void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1);
|
||||||
|
|
||||||
/* seg_helper.c */
|
/* seg_helper.c */
|
||||||
void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw);
|
void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw);
|
||||||
|
|
|
@ -1298,15 +1298,17 @@ void x86_cpu_do_interrupt(CPUState *cs)
|
||||||
/* successfully delivered */
|
/* successfully delivered */
|
||||||
env->old_exception = -1;
|
env->old_exception = -1;
|
||||||
#else
|
#else
|
||||||
/* simulate a real cpu exception. On i386, it can
|
if (cs->exception_index >= EXCP_VMEXIT) {
|
||||||
trigger new exceptions, but we do not handle
|
assert(env->old_exception == -1);
|
||||||
double or triple faults yet. */
|
do_vmexit(env, cs->exception_index - EXCP_VMEXIT, env->error_code);
|
||||||
do_interrupt_all(cpu, cs->exception_index,
|
} else {
|
||||||
env->exception_is_int,
|
do_interrupt_all(cpu, cs->exception_index,
|
||||||
env->error_code,
|
env->exception_is_int,
|
||||||
env->exception_next_eip, 0);
|
env->error_code,
|
||||||
/* successfully delivered */
|
env->exception_next_eip, 0);
|
||||||
env->old_exception = -1;
|
/* successfully delivered */
|
||||||
|
env->old_exception = -1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -568,12 +568,10 @@ void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: currently only 32 bits of exit_code are used */
|
|
||||||
void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1,
|
void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1,
|
||||||
uintptr_t retaddr)
|
uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(x86_env_get_cpu(env));
|
CPUState *cs = CPU(x86_env_get_cpu(env));
|
||||||
uint32_t int_ctl;
|
|
||||||
|
|
||||||
if (retaddr) {
|
if (retaddr) {
|
||||||
cpu_restore_state(cs, retaddr);
|
cpu_restore_state(cs, retaddr);
|
||||||
|
@ -585,6 +583,18 @@ void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1,
|
||||||
x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
|
x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
|
||||||
control.exit_info_2)),
|
control.exit_info_2)),
|
||||||
env->eip);
|
env->eip);
|
||||||
|
cs->exception_index = EXCP_VMEXIT + exit_code;
|
||||||
|
env->error_code = exit_info_1;
|
||||||
|
|
||||||
|
/* remove any pending exception */
|
||||||
|
env->old_exception = -1;
|
||||||
|
cpu_loop_exit(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
|
||||||
|
{
|
||||||
|
CPUState *cs = CPU(x86_env_get_cpu(env));
|
||||||
|
uint32_t int_ctl;
|
||||||
|
|
||||||
if (env->hflags & HF_INHIBIT_IRQ_MASK) {
|
if (env->hflags & HF_INHIBIT_IRQ_MASK) {
|
||||||
x86_stl_phys(cs,
|
x86_stl_phys(cs,
|
||||||
|
@ -747,13 +757,6 @@ void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1,
|
||||||
/* If the host's rIP reloaded by #VMEXIT is outside the limit of the
|
/* If the host's rIP reloaded by #VMEXIT is outside the limit of the
|
||||||
host's code segment or non-canonical (in the case of long mode), a
|
host's code segment or non-canonical (in the case of long mode), a
|
||||||
#GP fault is delivered inside the host. */
|
#GP fault is delivered inside the host. */
|
||||||
|
|
||||||
/* remove any pending exception */
|
|
||||||
cs->exception_index = -1;
|
|
||||||
env->error_code = 0;
|
|
||||||
env->old_exception = -1;
|
|
||||||
|
|
||||||
cpu_loop_exit(cs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue