diff options
-rw-r--r-- | arch/x86/kvm/vmx/vmx.c | 70 |
1 files changed, 56 insertions, 14 deletions
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index bf5a700ef064..1e5f73a7f8f0 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -8008,20 +8008,13 @@ static __init void vmx_set_cpu_caps(void) } static bool vmx_is_io_intercepted(struct kvm_vcpu *vcpu, - struct x86_instruction_info *info) + struct x86_instruction_info *info, + unsigned long *exit_qualification) { struct vmcs12 *vmcs12 = get_vmcs12(vcpu); unsigned short port; int size; - - if (info->intercept == x86_intercept_in || - info->intercept == x86_intercept_ins) { - port = info->src_val; - size = info->dst_bytes; - } else { - port = info->dst_val; - size = info->src_bytes; - } + bool imm; /* * If the 'use IO bitmaps' VM-execution control is 0, IO instruction @@ -8033,6 +8026,30 @@ static bool vmx_is_io_intercepted(struct kvm_vcpu *vcpu, if (!nested_cpu_has(vmcs12, CPU_BASED_USE_IO_BITMAPS)) return nested_cpu_has(vmcs12, CPU_BASED_UNCOND_IO_EXITING); + if (info->intercept == x86_intercept_in || + info->intercept == x86_intercept_ins) { + port = info->src_val; + size = info->dst_bytes; + imm = info->src_type == OP_IMM; + } else { + port = info->dst_val; + size = info->src_bytes; + imm = info->dst_type == OP_IMM; + } + + + *exit_qualification = ((unsigned long)port << 16) | (size - 1); + + if (info->intercept == x86_intercept_ins || + info->intercept == x86_intercept_outs) + *exit_qualification |= BIT(4); + + if (info->rep_prefix) + *exit_qualification |= BIT(5); + + if (imm) + *exit_qualification |= BIT(6); + return nested_vmx_check_io_bitmaps(vcpu, port, size); } @@ -8042,6 +8059,9 @@ int vmx_check_intercept(struct kvm_vcpu *vcpu, struct x86_exception *exception) { struct vmcs12 *vmcs12 = get_vmcs12(vcpu); + unsigned long exit_qualification = 0; + u32 vm_exit_reason; + u64 exit_insn_len; switch (info->intercept) { case x86_intercept_rdpid: @@ -8062,8 +8082,10 @@ int vmx_check_intercept(struct kvm_vcpu *vcpu, case x86_intercept_ins: case x86_intercept_out: case x86_intercept_outs: - if (!vmx_is_io_intercepted(vcpu, info)) + if (!vmx_is_io_intercepted(vcpu, info, &exit_qualification)) return X86EMUL_CONTINUE; + + vm_exit_reason = EXIT_REASON_IO_INSTRUCTION; break; case x86_intercept_lgdt: @@ -8076,11 +8098,25 @@ int vmx_check_intercept(struct kvm_vcpu *vcpu, case x86_intercept_str: if (!nested_cpu_has2(vmcs12, SECONDARY_EXEC_DESC)) return X86EMUL_CONTINUE; + + if (info->intercept == x86_intercept_lldt || + info->intercept == x86_intercept_ltr || + info->intercept == x86_intercept_sldt || + info->intercept == x86_intercept_str) + vm_exit_reason = EXIT_REASON_LDTR_TR; + else + vm_exit_reason = EXIT_REASON_GDTR_IDTR; + /* + * FIXME: Decode the ModR/M to generate the correct exit + * qualification for memory operands. + */ break; case x86_intercept_hlt: if (!nested_cpu_has(vmcs12, CPU_BASED_HLT_EXITING)) return X86EMUL_CONTINUE; + + vm_exit_reason = EXIT_REASON_HLT; break; case x86_intercept_pause: @@ -8096,15 +8132,21 @@ int vmx_check_intercept(struct kvm_vcpu *vcpu, !nested_cpu_has(vmcs12, CPU_BASED_PAUSE_EXITING)) return X86EMUL_CONTINUE; + vm_exit_reason = EXIT_REASON_PAUSE_INSTRUCTION; break; /* TODO: check more intercepts... */ default: - break; + return X86EMUL_UNHANDLEABLE; } - /* FIXME: produce nested vmexit and return X86EMUL_INTERCEPTED. */ - return X86EMUL_UNHANDLEABLE; + exit_insn_len = abs_diff((s64)info->next_rip, (s64)info->rip); + if (!exit_insn_len || exit_insn_len > X86_MAX_INSTRUCTION_LENGTH) + return X86EMUL_UNHANDLEABLE; + + __nested_vmx_vmexit(vcpu, vm_exit_reason, 0, exit_qualification, + exit_insn_len); + return X86EMUL_INTERCEPTED; } #ifdef CONFIG_X86_64 |