Skip to content

Commit c726200

Browse files
christofferdall-armMarc Zyngier
authored andcommitted
KVM: arm/arm64: Allow reporting non-ISV data aborts to userspace
For a long time, if a guest accessed memory outside of a memslot using any of the load/store instructions in the architecture which doesn't supply decoding information in the ESR_EL2 (the ISV bit is not set), the kernel would print the following message and terminate the VM as a result of returning -ENOSYS to userspace: load/store instruction decoding not implemented The reason behind this message is that KVM assumes that all accesses outside a memslot is an MMIO access which should be handled by userspace, and we originally expected to eventually implement some sort of decoding of load/store instructions where the ISV bit was not set. However, it turns out that many of the instructions which don't provide decoding information on abort are not safe to use for MMIO accesses, and the remaining few that would potentially make sense to use on MMIO accesses, such as those with register writeback, are not used in practice. It also turns out that fetching an instruction from guest memory can be a pretty horrible affair, involving stopping all CPUs on SMP systems, handling multiple corner cases of address translation in software, and more. It doesn't appear likely that we'll ever implement this in the kernel. What is much more common is that a user has misconfigured his/her guest and is actually not accessing an MMIO region, but just hitting some random hole in the IPA space. In this scenario, the error message above is almost misleading and has led to a great deal of confusion over the years. It is, nevertheless, ABI to userspace, and we therefore need to introduce a new capability that userspace explicitly enables to change behavior. This patch introduces KVM_CAP_ARM_NISV_TO_USER (NISV meaning Non-ISV) which does exactly that, and introduces a new exit reason to report the event to userspace. User space can then emulate an exception to the guest, restart the guest, suspend the guest, or take any other appropriate action as per the policy of the running system. Reported-by: Heinrich Schuchardt <[email protected]> Signed-off-by: Christoffer Dall <[email protected]> Reviewed-by: Alexander Graf <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent 4f5cafb commit c726200

File tree

9 files changed

+96
-1
lines changed

9 files changed

+96
-1
lines changed

Documentation/virt/kvm/api.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4468,6 +4468,39 @@ Hyper-V SynIC state change. Notification is used to remap SynIC
44684468
event/message pages and to enable/disable SynIC messages/events processing
44694469
in userspace.
44704470

4471+
/* KVM_EXIT_ARM_NISV */
4472+
struct {
4473+
__u64 esr_iss;
4474+
__u64 fault_ipa;
4475+
} arm_nisv;
4476+
4477+
Used on arm and arm64 systems. If a guest accesses memory not in a memslot,
4478+
KVM will typically return to userspace and ask it to do MMIO emulation on its
4479+
behalf. However, for certain classes of instructions, no instruction decode
4480+
(direction, length of memory access) is provided, and fetching and decoding
4481+
the instruction from the VM is overly complicated to live in the kernel.
4482+
4483+
Historically, when this situation occurred, KVM would print a warning and kill
4484+
the VM. KVM assumed that if the guest accessed non-memslot memory, it was
4485+
trying to do I/O, which just couldn't be emulated, and the warning message was
4486+
phrased accordingly. However, what happened more often was that a guest bug
4487+
caused access outside the guest memory areas which should lead to a more
4488+
meaningful warning message and an external abort in the guest, if the access
4489+
did not fall within an I/O window.
4490+
4491+
Userspace implementations can query for KVM_CAP_ARM_NISV_TO_USER, and enable
4492+
this capability at VM creation. Once this is done, these types of errors will
4493+
instead return to userspace with KVM_EXIT_ARM_NISV, with the valid bits from
4494+
the HSR (arm) and ESR_EL2 (arm64) in the esr_iss field, and the faulting IPA
4495+
in the fault_ipa field. Userspace can either fix up the access if it's
4496+
actually an I/O access by decoding the instruction from guest memory (if it's
4497+
very brave) and continue executing the guest, or it can decide to suspend,
4498+
dump, or restart the guest.
4499+
4500+
Note that KVM does not skip the faulting instruction as it does for
4501+
KVM_EXIT_MMIO, but userspace has to emulate any change to the processing state
4502+
if it decides to decode and emulate the instruction.
4503+
44714504
/* Fix the size of the union. */
44724505
char padding[256];
44734506
};

arch/arm/include/asm/kvm_arm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
#define HSR_ISV (_AC(1, UL) << HSR_ISV_SHIFT)
163163
#define HSR_SRT_SHIFT (16)
164164
#define HSR_SRT_MASK (0xf << HSR_SRT_SHIFT)
165+
#define HSR_CM (1 << 8)
165166
#define HSR_FSC (0x3f)
166167
#define HSR_FSC_TYPE (0x3c)
167168
#define HSR_SSE (1 << 21)

arch/arm/include/asm/kvm_emulate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ static inline bool kvm_vcpu_dabt_isvalid(struct kvm_vcpu *vcpu)
167167
return kvm_vcpu_get_hsr(vcpu) & HSR_ISV;
168168
}
169169

170+
static inline unsigned long kvm_vcpu_dabt_iss_nisv_sanitized(const struct kvm_vcpu *vcpu)
171+
{
172+
return kvm_vcpu_get_hsr(vcpu) & (HSR_CM | HSR_WNR | HSR_FSC);
173+
}
174+
170175
static inline bool kvm_vcpu_dabt_iswrite(struct kvm_vcpu *vcpu)
171176
{
172177
return kvm_vcpu_get_hsr(vcpu) & HSR_WNR;

arch/arm/include/asm/kvm_host.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ struct kvm_arch {
7676

7777
/* Mandated version of PSCI */
7878
u32 psci_version;
79+
80+
/*
81+
* If we encounter a data abort without valid instruction syndrome
82+
* information, report this to user space. User space can (and
83+
* should) opt in to this feature if KVM_CAP_ARM_NISV_TO_USER is
84+
* supported.
85+
*/
86+
bool return_nisv_io_abort_to_user;
7987
};
8088

8189
#define KVM_NR_MEM_OBJS 40

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ static inline bool kvm_vcpu_dabt_isvalid(const struct kvm_vcpu *vcpu)
258258
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_ISV);
259259
}
260260

261+
static inline unsigned long kvm_vcpu_dabt_iss_nisv_sanitized(const struct kvm_vcpu *vcpu)
262+
{
263+
return kvm_vcpu_get_hsr(vcpu) & (ESR_ELx_CM | ESR_ELx_WNR | ESR_ELx_FSC);
264+
}
265+
261266
static inline bool kvm_vcpu_dabt_issext(const struct kvm_vcpu *vcpu)
262267
{
263268
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SSE);

arch/arm64/include/asm/kvm_host.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ struct kvm_arch {
8383

8484
/* Mandated version of PSCI */
8585
u32 psci_version;
86+
87+
/*
88+
* If we encounter a data abort without valid instruction syndrome
89+
* information, report this to user space. User space can (and
90+
* should) opt in to this feature if KVM_CAP_ARM_NISV_TO_USER is
91+
* supported.
92+
*/
93+
bool return_nisv_io_abort_to_user;
8694
};
8795

8896
#define KVM_NR_MEM_OBJS 40

include/uapi/linux/kvm.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ struct kvm_hyperv_exit {
235235
#define KVM_EXIT_S390_STSI 25
236236
#define KVM_EXIT_IOAPIC_EOI 26
237237
#define KVM_EXIT_HYPERV 27
238+
#define KVM_EXIT_ARM_NISV 28
238239

239240
/* For KVM_EXIT_INTERNAL_ERROR */
240241
/* Emulate instruction failed. */
@@ -394,6 +395,11 @@ struct kvm_run {
394395
} eoi;
395396
/* KVM_EXIT_HYPERV */
396397
struct kvm_hyperv_exit hyperv;
398+
/* KVM_EXIT_ARM_NISV */
399+
struct {
400+
__u64 esr_iss;
401+
__u64 fault_ipa;
402+
} arm_nisv;
397403
/* Fix the size of the union. */
398404
char padding[256];
399405
};
@@ -1000,6 +1006,7 @@ struct kvm_ppc_resize_hpt {
10001006
#define KVM_CAP_PMU_EVENT_FILTER 173
10011007
#define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
10021008
#define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
1009+
#define KVM_CAP_ARM_NISV_TO_USER 176
10031010

10041011
#ifdef KVM_CAP_IRQ_ROUTING
10051012

virt/kvm/arm/arm.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,26 @@ int kvm_arch_check_processor_compat(void)
9898
return 0;
9999
}
100100

101+
int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
102+
struct kvm_enable_cap *cap)
103+
{
104+
int r;
105+
106+
if (cap->flags)
107+
return -EINVAL;
108+
109+
switch (cap->cap) {
110+
case KVM_CAP_ARM_NISV_TO_USER:
111+
r = 0;
112+
kvm->arch.return_nisv_io_abort_to_user = true;
113+
break;
114+
default:
115+
r = -EINVAL;
116+
break;
117+
}
118+
119+
return r;
120+
}
101121

102122
/**
103123
* kvm_arch_init_vm - initializes a VM data structure
@@ -197,6 +217,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
197217
case KVM_CAP_IMMEDIATE_EXIT:
198218
case KVM_CAP_VCPU_EVENTS:
199219
case KVM_CAP_ARM_IRQ_LINE_LAYOUT_2:
220+
case KVM_CAP_ARM_NISV_TO_USER:
200221
r = 1;
201222
break;
202223
case KVM_CAP_ARM_SET_DEVICE_ADDR:

virt/kvm/arm/mmio.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,14 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
167167
if (ret)
168168
return ret;
169169
} else {
170-
kvm_err("load/store instruction decoding not implemented\n");
170+
if (vcpu->kvm->arch.return_nisv_io_abort_to_user) {
171+
run->exit_reason = KVM_EXIT_ARM_NISV;
172+
run->arm_nisv.esr_iss = kvm_vcpu_dabt_iss_nisv_sanitized(vcpu);
173+
run->arm_nisv.fault_ipa = fault_ipa;
174+
return 0;
175+
}
176+
177+
kvm_pr_unimpl("Data abort outside memslots with no valid syndrome info\n");
171178
return -ENOSYS;
172179
}
173180

0 commit comments

Comments
 (0)