Commit f8ea7c60 authored by Vitaly Kuznetsov's avatar Vitaly Kuznetsov Committed by Paolo Bonzini

x86: kvm: svm: propagate errors from skip_emulated_instruction()

On AMD, kvm_x86_ops->skip_emulated_instruction(vcpu) can, in theory,
fail: in !nrips case we call kvm_emulate_instruction(EMULTYPE_SKIP).
Currently, we only do printk(KERN_DEBUG) when this happens and this
is not ideal. Propagate the error up the stack.

On VMX, skip_emulated_instruction() doesn't fail, we have two call
sites calling it explicitly: handle_exception_nmi() and
handle_task_switch(), we can just ignore the result.

On SVM, we also have two explicit call sites:
svm_queue_exception() and it seems we don't need to do anything there as
we check if RIP was advanced or not. In task_switch_interception(),
however, we are better off not proceeding to kvm_task_switch() in case
skip_emulated_instruction() failed.
Suggested-by: default avatarSean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: default avatarVitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 05402f64
...@@ -1070,7 +1070,7 @@ struct kvm_x86_ops { ...@@ -1070,7 +1070,7 @@ struct kvm_x86_ops {
void (*run)(struct kvm_vcpu *vcpu); void (*run)(struct kvm_vcpu *vcpu);
int (*handle_exit)(struct kvm_vcpu *vcpu); int (*handle_exit)(struct kvm_vcpu *vcpu);
void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); int (*skip_emulated_instruction)(struct kvm_vcpu *vcpu);
void (*set_interrupt_shadow)(struct kvm_vcpu *vcpu, int mask); void (*set_interrupt_shadow)(struct kvm_vcpu *vcpu, int mask);
u32 (*get_interrupt_shadow)(struct kvm_vcpu *vcpu); u32 (*get_interrupt_shadow)(struct kvm_vcpu *vcpu);
void (*patch_hypercall)(struct kvm_vcpu *vcpu, void (*patch_hypercall)(struct kvm_vcpu *vcpu,
......
...@@ -768,7 +768,7 @@ static void svm_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) ...@@ -768,7 +768,7 @@ static void svm_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask)
} }
static void skip_emulated_instruction(struct kvm_vcpu *vcpu) static int skip_emulated_instruction(struct kvm_vcpu *vcpu)
{ {
struct vcpu_svm *svm = to_svm(vcpu); struct vcpu_svm *svm = to_svm(vcpu);
...@@ -777,18 +777,17 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu) ...@@ -777,18 +777,17 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
svm->next_rip = svm->vmcb->control.next_rip; svm->next_rip = svm->vmcb->control.next_rip;
} }
if (!svm->next_rip) { if (!svm->next_rip)
if (kvm_emulate_instruction(vcpu, EMULTYPE_SKIP) != return kvm_emulate_instruction(vcpu, EMULTYPE_SKIP);
EMULATE_DONE)
printk(KERN_DEBUG "%s: NOP\n", __func__);
return;
}
if (svm->next_rip - kvm_rip_read(vcpu) > MAX_INST_SIZE) if (svm->next_rip - kvm_rip_read(vcpu) > MAX_INST_SIZE)
printk(KERN_ERR "%s: ip 0x%lx next 0x%llx\n", printk(KERN_ERR "%s: ip 0x%lx next 0x%llx\n",
__func__, kvm_rip_read(vcpu), svm->next_rip); __func__, kvm_rip_read(vcpu), svm->next_rip);
kvm_rip_write(vcpu, svm->next_rip); kvm_rip_write(vcpu, svm->next_rip);
svm_set_interrupt_shadow(vcpu, 0); svm_set_interrupt_shadow(vcpu, 0);
return EMULATE_DONE;
} }
static void svm_queue_exception(struct kvm_vcpu *vcpu) static void svm_queue_exception(struct kvm_vcpu *vcpu)
...@@ -819,7 +818,7 @@ static void svm_queue_exception(struct kvm_vcpu *vcpu) ...@@ -819,7 +818,7 @@ static void svm_queue_exception(struct kvm_vcpu *vcpu)
* raises a fault that is not intercepted. Still better than * raises a fault that is not intercepted. Still better than
* failing in all cases. * failing in all cases.
*/ */
skip_emulated_instruction(&svm->vcpu); (void)skip_emulated_instruction(&svm->vcpu);
rip = kvm_rip_read(&svm->vcpu); rip = kvm_rip_read(&svm->vcpu);
svm->int3_rip = rip + svm->vmcb->save.cs.base; svm->int3_rip = rip + svm->vmcb->save.cs.base;
svm->int3_injected = rip - old_rip; svm->int3_injected = rip - old_rip;
...@@ -3896,20 +3895,25 @@ static int task_switch_interception(struct vcpu_svm *svm) ...@@ -3896,20 +3895,25 @@ static int task_switch_interception(struct vcpu_svm *svm)
if (reason != TASK_SWITCH_GATE || if (reason != TASK_SWITCH_GATE ||
int_type == SVM_EXITINTINFO_TYPE_SOFT || int_type == SVM_EXITINTINFO_TYPE_SOFT ||
(int_type == SVM_EXITINTINFO_TYPE_EXEPT && (int_type == SVM_EXITINTINFO_TYPE_EXEPT &&
(int_vec == OF_VECTOR || int_vec == BP_VECTOR))) (int_vec == OF_VECTOR || int_vec == BP_VECTOR))) {
skip_emulated_instruction(&svm->vcpu); if (skip_emulated_instruction(&svm->vcpu) != EMULATE_DONE)
goto fail;
}
if (int_type != SVM_EXITINTINFO_TYPE_SOFT) if (int_type != SVM_EXITINTINFO_TYPE_SOFT)
int_vec = -1; int_vec = -1;
if (kvm_task_switch(&svm->vcpu, tss_selector, int_vec, reason, if (kvm_task_switch(&svm->vcpu, tss_selector, int_vec, reason,
has_error_code, error_code) == EMULATE_FAIL) { has_error_code, error_code) == EMULATE_FAIL)
goto fail;
return 1;
fail:
svm->vcpu.run->exit_reason = KVM_EXIT_INTERNAL_ERROR; svm->vcpu.run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
svm->vcpu.run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; svm->vcpu.run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
svm->vcpu.run->internal.ndata = 0; svm->vcpu.run->internal.ndata = 0;
return 0; return 0;
}
return 1;
} }
static int cpuid_interception(struct vcpu_svm *svm) static int cpuid_interception(struct vcpu_svm *svm)
......
...@@ -1472,8 +1472,11 @@ static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) ...@@ -1472,8 +1472,11 @@ static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data)
return 0; return 0;
} }
/*
static void skip_emulated_instruction(struct kvm_vcpu *vcpu) * Returns an int to be compatible with SVM implementation (which can fail).
* Do not use directly, use skip_emulated_instruction() instead.
*/
static int __skip_emulated_instruction(struct kvm_vcpu *vcpu)
{ {
unsigned long rip; unsigned long rip;
...@@ -1483,6 +1486,13 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu) ...@@ -1483,6 +1486,13 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
/* skipping an emulated instruction also counts */ /* skipping an emulated instruction also counts */
vmx_set_interrupt_shadow(vcpu, 0); vmx_set_interrupt_shadow(vcpu, 0);
return EMULATE_DONE;
}
static inline void skip_emulated_instruction(struct kvm_vcpu *vcpu)
{
(void)__skip_emulated_instruction(vcpu);
} }
static void vmx_clear_hlt(struct kvm_vcpu *vcpu) static void vmx_clear_hlt(struct kvm_vcpu *vcpu)
...@@ -7705,7 +7715,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = { ...@@ -7705,7 +7715,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
.run = vmx_vcpu_run, .run = vmx_vcpu_run,
.handle_exit = vmx_handle_exit, .handle_exit = vmx_handle_exit,
.skip_emulated_instruction = skip_emulated_instruction, .skip_emulated_instruction = __skip_emulated_instruction,
.set_interrupt_shadow = vmx_set_interrupt_shadow, .set_interrupt_shadow = vmx_set_interrupt_shadow,
.get_interrupt_shadow = vmx_get_interrupt_shadow, .get_interrupt_shadow = vmx_get_interrupt_shadow,
.patch_hypercall = vmx_patch_hypercall, .patch_hypercall = vmx_patch_hypercall,
......
...@@ -6390,9 +6390,11 @@ static void kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu, int *r) ...@@ -6390,9 +6390,11 @@ static void kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu, int *r)
int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu) int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu)
{ {
unsigned long rflags = kvm_x86_ops->get_rflags(vcpu); unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
int r = EMULATE_DONE; int r;
kvm_x86_ops->skip_emulated_instruction(vcpu); r = kvm_x86_ops->skip_emulated_instruction(vcpu);
if (unlikely(r != EMULATE_DONE))
return 0;
/* /*
* rflags is the old, "raw" value of the flags. The new value has * rflags is the old, "raw" value of the flags. The new value has
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment