lkml.org 
[lkml]   [2021]   [Jul]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 2/3] KVM: x86: On emulation failure, convey the exit reason, etc. to userspace
    Date
    Should instruction emulation fail, include the VM exit reason, etc. in
    the emulation_failure data passed to userspace, in order that the VMM
    can report it as a debugging aid when describing the failure.

    Suggested-by: Joao Martins <joao.m.martins@oracle.com>
    Signed-off-by: David Edmondson <david.edmondson@oracle.com>
    ---
    arch/x86/include/asm/kvm_host.h | 5 ++++
    arch/x86/kvm/vmx/vmx.c | 5 +---
    arch/x86/kvm/x86.c | 53 ++++++++++++++++++++++++++-------
    include/uapi/linux/kvm.h | 7 +++++
    4 files changed, 56 insertions(+), 14 deletions(-)

    diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
    index dfb902930cdc..17da43c1aa67 100644
    --- a/arch/x86/include/asm/kvm_host.h
    +++ b/arch/x86/include/asm/kvm_host.h
    @@ -1630,6 +1630,11 @@ extern u64 kvm_mce_cap_supported;
    int kvm_emulate_instruction(struct kvm_vcpu *vcpu, int emulation_type);
    int kvm_emulate_instruction_from_buffer(struct kvm_vcpu *vcpu,
    void *insn, int insn_len);
    +void kvm_prepare_emulation_failure_exit(struct kvm_vcpu *vcpu,
    + bool instruction_bytes,
    + void *data, unsigned int ndata);
    +void kvm_prepare_emulation_failure_exit_with_reason(struct kvm_vcpu *vcpu,
    + bool instruction_bytes);

    void kvm_enable_efer_bits(u64);
    bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer);
    diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
    index fefdecb0ff3d..a8d303c7c099 100644
    --- a/arch/x86/kvm/vmx/vmx.c
    +++ b/arch/x86/kvm/vmx/vmx.c
    @@ -5367,10 +5367,7 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)

    if (vmx->emulation_required && !vmx->rmode.vm86_active &&
    vcpu->arch.exception.pending) {
    - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
    - vcpu->run->internal.suberror =
    - KVM_INTERNAL_ERROR_EMULATION;
    - vcpu->run->internal.ndata = 0;
    + kvm_prepare_emulation_failure_exit_with_reason(vcpu, false);
    return 0;
    }

    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index a4fd10604f72..a97bacd8922f 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -7456,7 +7456,9 @@ void kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip)
    }
    EXPORT_SYMBOL_GPL(kvm_inject_realmode_interrupt);

    -static void prepare_emulation_failure_exit(struct kvm_vcpu *vcpu)
    +void kvm_prepare_emulation_failure_exit(struct kvm_vcpu *vcpu,
    + bool instruction_bytes,
    + void *data, unsigned int ndata)
    {
    struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt;
    u32 insn_size = ctxt->fetch.end - ctxt->fetch.data;
    @@ -7464,10 +7466,10 @@ static void prepare_emulation_failure_exit(struct kvm_vcpu *vcpu)

    run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
    run->emulation_failure.suberror = KVM_INTERNAL_ERROR_EMULATION;
    - run->emulation_failure.ndata = 0;
    + run->emulation_failure.ndata = 1; /* Always include the flags. */
    run->emulation_failure.flags = 0;

    - if (insn_size) {
    + if (instruction_bytes && insn_size) {
    run->emulation_failure.ndata = 3;
    run->emulation_failure.flags |=
    KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES;
    @@ -7477,7 +7479,42 @@ static void prepare_emulation_failure_exit(struct kvm_vcpu *vcpu)
    memcpy(run->emulation_failure.insn_bytes,
    ctxt->fetch.data, insn_size);
    }
    +
    + ndata = min((size_t)ndata, sizeof(run->internal.data) -
    + run->emulation_failure.ndata * sizeof(u64));
    + if (ndata) {
    + unsigned int offset =
    + offsetof(struct kvm_run, emulation_failure.flags) +
    + run->emulation_failure.ndata * sizeof(u64);
    +
    + memcpy((void *)run + offset, data, ndata);
    + run->emulation_failure.ndata += ndata / sizeof(u64);
    + }
    +}
    +EXPORT_SYMBOL_GPL(kvm_prepare_emulation_failure_exit);
    +
    +void kvm_prepare_emulation_failure_exit_with_reason(struct kvm_vcpu *vcpu,
    + bool instruction_bytes)
    +{
    + struct {
    + __u64 exit_reason;
    + __u64 exit_info1;
    + __u64 exit_info2;
    + __u32 intr_info;
    + __u32 error_code;
    + } exit_reason;
    +
    + static_call(kvm_x86_get_exit_info)(vcpu,
    + &exit_reason.exit_reason,
    + &exit_reason.exit_info1,
    + &exit_reason.exit_info2,
    + &exit_reason.intr_info,
    + &exit_reason.error_code);
    +
    + kvm_prepare_emulation_failure_exit(vcpu, instruction_bytes,
    + &exit_reason, sizeof(exit_reason));
    }
    +EXPORT_SYMBOL_GPL(kvm_prepare_emulation_failure_exit_with_reason);

    static int handle_emulation_failure(struct kvm_vcpu *vcpu, int emulation_type)
    {
    @@ -7493,16 +7530,14 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu, int emulation_type)

    if (kvm->arch.exit_on_emulation_error ||
    (emulation_type & EMULTYPE_SKIP)) {
    - prepare_emulation_failure_exit(vcpu);
    + kvm_prepare_emulation_failure_exit_with_reason(vcpu, true);
    return 0;
    }

    kvm_queue_exception(vcpu, UD_VECTOR);

    if (!is_guest_mode(vcpu) && static_call(kvm_x86_get_cpl)(vcpu) == 0) {
    - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
    - vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
    - vcpu->run->internal.ndata = 0;
    + kvm_prepare_emulation_failure_exit_with_reason(vcpu, false);
    return 0;
    }

    @@ -12095,9 +12130,7 @@ int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r,
    * doesn't seem to be a real use-case behind such requests, just return
    * KVM_EXIT_INTERNAL_ERROR for now.
    */
    - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
    - vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
    - vcpu->run->internal.ndata = 0;
    + kvm_prepare_emulation_failure_exit_with_reason(vcpu, false);

    return 0;
    }
    diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
    index d9e4aabcb31a..f1ef4117b824 100644
    --- a/include/uapi/linux/kvm.h
    +++ b/include/uapi/linux/kvm.h
    @@ -397,6 +397,12 @@ struct kvm_run {
    * "ndata" is correct, that new fields are enumerated in "flags",
    * and that each flag enumerates fields that are 64-bit aligned
    * and sized (so that ndata+internal.data[] is valid/accurate).
    + *
    + * Space beyond the defined fields may be used to
    + * store arbitrary debug information relating to the
    + * emulation failure. It is accounted for in "ndata"
    + * but otherwise unspecified and is not represented in
    + * "flags".
    */
    struct {
    __u32 suberror;
    @@ -404,6 +410,7 @@ struct kvm_run {
    __u64 flags;
    __u8 insn_size;
    __u8 insn_bytes[15];
    + /* Arbitrary debug data may follow. */
    } emulation_failure;
    /* KVM_EXIT_OSI */
    struct {
    --
    2.30.2
    \
     
     \ /
      Last update: 2021-07-29 15:47    [W:7.957 / U:0.688 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site