Skip to content

Commit 19238e7

Browse files
suomilewisbonzini
authored andcommitted
kvm: x86: Allow userspace to handle emulation errors
Add a fallback mechanism to the in-kernel instruction emulator that allows userspace the opportunity to process an instruction the emulator was unable to. When the in-kernel instruction emulator fails to process an instruction it will either inject a #UD into the guest or exit to userspace with exit reason KVM_INTERNAL_ERROR. This is because it does not know how to proceed in an appropriate manner. This feature lets userspace get involved to see if it can figure out a better path forward. Signed-off-by: Aaron Lewis <aaronlewis@google.com> Reviewed-by: David Edmondson <david.edmondson@oracle.com> Message-Id: <20210510144834.658457-2-aaronlewis@google.com> Reviewed-by: Jim Mattson <jmattson@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 27de925 commit 19238e7

4 files changed

Lines changed: 85 additions & 4 deletions

File tree

Documentation/virt/kvm/api.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6546,6 +6546,7 @@ KVM_RUN_BUS_LOCK flag is used to distinguish between them.
65466546
This capability can be used to check / enable 2nd DAWR feature provided
65476547
by POWER10 processor.
65486548

6549+
65496550
7.24 KVM_CAP_VM_COPY_ENC_CONTEXT_FROM
65506551
-------------------------------------
65516552

@@ -6603,6 +6604,25 @@ present in the "ibm,hypertas-functions" device-tree property.
66036604
This capability is enabled for hypervisors on platforms like POWER9
66046605
that support radix MMU.
66056606

6607+
7.27 KVM_CAP_EXIT_ON_EMULATION_FAILURE
6608+
--------------------------------------
6609+
6610+
:Architectures: x86
6611+
:Parameters: args[0] whether the feature should be enabled or not
6612+
6613+
When this capability is enabled, an emulation failure will result in an exit
6614+
to userspace with KVM_INTERNAL_ERROR (except when the emulator was invoked
6615+
to handle a VMware backdoor instruction). Furthermore, KVM will now provide up
6616+
to 15 instruction bytes for any exit to userspace resulting from an emulation
6617+
failure. When these exits to userspace occur use the emulation_failure struct
6618+
instead of the internal struct. They both have the same layout, but the
6619+
emulation_failure struct matches the content better. It also explicitly
6620+
defines the 'flags' field which is used to describe the fields in the struct
6621+
that are valid (ie: if KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES is
6622+
set in the 'flags' field then both 'insn_size' and 'insn_bytes' have valid data
6623+
in them.)
6624+
6625+
66066626
8. Other capabilities.
66076627
======================
66086628

arch/x86/include/asm/kvm_host.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,12 @@ struct kvm_arch {
11141114
bool exception_payload_enabled;
11151115

11161116
bool bus_lock_detection_enabled;
1117+
/*
1118+
* If exit_on_emulation_error is set, and the in-kernel instruction
1119+
* emulator fails to emulate an instruction, allow userspace
1120+
* the opportunity to look at it.
1121+
*/
1122+
bool exit_on_emulation_error;
11171123

11181124
/* Deflect RDMSR and WRMSR to user space when they trigger a #GP */
11191125
u32 user_space_msr_mask;

arch/x86/kvm/x86.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4010,6 +4010,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
40104010
#endif
40114011
case KVM_CAP_VM_COPY_ENC_CONTEXT_FROM:
40124012
case KVM_CAP_SREGS2:
4013+
case KVM_CAP_EXIT_ON_EMULATION_FAILURE:
40134014
r = 1;
40144015
break;
40154016
case KVM_CAP_EXIT_HYPERCALL:
@@ -5649,6 +5650,13 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
56495650
kvm->arch.hypercall_exit_enabled = cap->args[0];
56505651
r = 0;
56515652
break;
5653+
case KVM_CAP_EXIT_ON_EMULATION_FAILURE:
5654+
r = -EINVAL;
5655+
if (cap->args[0] & ~1)
5656+
break;
5657+
kvm->arch.exit_on_emulation_error = cap->args[0];
5658+
r = 0;
5659+
break;
56525660
default:
56535661
r = -EINVAL;
56545662
break;
@@ -7444,8 +7452,33 @@ void kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip)
74447452
}
74457453
EXPORT_SYMBOL_GPL(kvm_inject_realmode_interrupt);
74467454

7455+
static void prepare_emulation_failure_exit(struct kvm_vcpu *vcpu)
7456+
{
7457+
struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt;
7458+
u32 insn_size = ctxt->fetch.end - ctxt->fetch.data;
7459+
struct kvm_run *run = vcpu->run;
7460+
7461+
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
7462+
run->emulation_failure.suberror = KVM_INTERNAL_ERROR_EMULATION;
7463+
run->emulation_failure.ndata = 0;
7464+
run->emulation_failure.flags = 0;
7465+
7466+
if (insn_size) {
7467+
run->emulation_failure.ndata = 3;
7468+
run->emulation_failure.flags |=
7469+
KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES;
7470+
run->emulation_failure.insn_size = insn_size;
7471+
memset(run->emulation_failure.insn_bytes, 0x90,
7472+
sizeof(run->emulation_failure.insn_bytes));
7473+
memcpy(run->emulation_failure.insn_bytes,
7474+
ctxt->fetch.data, insn_size);
7475+
}
7476+
}
7477+
74477478
static int handle_emulation_failure(struct kvm_vcpu *vcpu, int emulation_type)
74487479
{
7480+
struct kvm *kvm = vcpu->kvm;
7481+
74497482
++vcpu->stat.insn_emulation_fail;
74507483
trace_kvm_emulate_insn_failed(vcpu);
74517484

@@ -7454,10 +7487,9 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu, int emulation_type)
74547487
return 1;
74557488
}
74567489

7457-
if (emulation_type & EMULTYPE_SKIP) {
7458-
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
7459-
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
7460-
vcpu->run->internal.ndata = 0;
7490+
if (kvm->arch.exit_on_emulation_error ||
7491+
(emulation_type & EMULTYPE_SKIP)) {
7492+
prepare_emulation_failure_exit(vcpu);
74617493
return 0;
74627494
}
74637495

include/uapi/linux/kvm.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ struct kvm_xen_exit {
280280
/* Encounter unexpected vm-exit reason */
281281
#define KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON 4
282282

283+
/* Flags that describe what fields in emulation_failure hold valid data. */
284+
#define KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES (1ULL << 0)
285+
283286
/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */
284287
struct kvm_run {
285288
/* in */
@@ -383,6 +386,25 @@ struct kvm_run {
383386
__u32 ndata;
384387
__u64 data[16];
385388
} internal;
389+
/*
390+
* KVM_INTERNAL_ERROR_EMULATION
391+
*
392+
* "struct emulation_failure" is an overlay of "struct internal"
393+
* that is used for the KVM_INTERNAL_ERROR_EMULATION sub-type of
394+
* KVM_EXIT_INTERNAL_ERROR. Note, unlike other internal error
395+
* sub-types, this struct is ABI! It also needs to be backwards
396+
* compatible with "struct internal". Take special care that
397+
* "ndata" is correct, that new fields are enumerated in "flags",
398+
* and that each flag enumerates fields that are 64-bit aligned
399+
* and sized (so that ndata+internal.data[] is valid/accurate).
400+
*/
401+
struct {
402+
__u32 suberror;
403+
__u32 ndata;
404+
__u64 flags;
405+
__u8 insn_size;
406+
__u8 insn_bytes[15];
407+
} emulation_failure;
386408
/* KVM_EXIT_OSI */
387409
struct {
388410
__u64 gprs[32];
@@ -1088,6 +1110,7 @@ struct kvm_ppc_resize_hpt {
10881110
#define KVM_CAP_EXIT_HYPERCALL 201
10891111
#define KVM_CAP_PPC_RPT_INVALIDATE 202
10901112
#define KVM_CAP_BINARY_STATS_FD 203
1113+
#define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
10911114

10921115
#ifdef KVM_CAP_IRQ_ROUTING
10931116

0 commit comments

Comments
 (0)