Skip to content

Commit da34517

Browse files
christofferdall-armMarc Zyngier
authored andcommitted
KVM: arm/arm64: Allow user injection of external data aborts
In some scenarios, such as buggy guest or incorrect configuration of the VMM and firmware description data, userspace will detect a memory access to a portion of the IPA, which is not mapped to any MMIO region. For this purpose, the appropriate action is to inject an external abort to the guest. The kernel already has functionality to inject an external abort, but we need to wire up a signal from user space that lets user space tell the kernel to do this. It turns out, we already have the set event functionality which we can perfectly reuse for this. Signed-off-by: Christoffer Dall <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent c726200 commit da34517

File tree

8 files changed

+49
-5
lines changed

8 files changed

+49
-5
lines changed

Documentation/virt/kvm/api.txt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,12 +1002,18 @@ Specifying exception.has_esr on a system that does not support it will return
10021002
-EINVAL. Setting anything other than the lower 24bits of exception.serror_esr
10031003
will return -EINVAL.
10041004

1005+
It is not possible to read back a pending external abort (injected via
1006+
KVM_SET_VCPU_EVENTS or otherwise) because such an exception is always delivered
1007+
directly to the virtual CPU).
1008+
1009+
10051010
struct kvm_vcpu_events {
10061011
struct {
10071012
__u8 serror_pending;
10081013
__u8 serror_has_esr;
1014+
__u8 ext_dabt_pending;
10091015
/* Align it to 8 bytes */
1010-
__u8 pad[6];
1016+
__u8 pad[5];
10111017
__u64 serror_esr;
10121018
} exception;
10131019
__u32 reserved[12];
@@ -1051,9 +1057,23 @@ contain a valid state and shall be written into the VCPU.
10511057

10521058
ARM/ARM64:
10531059

1060+
User space may need to inject several types of events to the guest.
1061+
10541062
Set the pending SError exception state for this VCPU. It is not possible to
10551063
'cancel' an Serror that has been made pending.
10561064

1065+
If the guest performed an access to I/O memory which could not be handled by
1066+
userspace, for example because of missing instruction syndrome decode
1067+
information or because there is no device mapped at the accessed IPA, then
1068+
userspace can ask the kernel to inject an external abort using the address
1069+
from the exiting fault on the VCPU. It is a programming error to set
1070+
ext_dabt_pending after an exit which was not either KVM_EXIT_MMIO or
1071+
KVM_EXIT_ARM_NISV. This feature is only available if the system supports
1072+
KVM_CAP_ARM_INJECT_EXT_DABT. This is a helper which provides commonality in
1073+
how userspace reports accesses for the above cases to guests, across different
1074+
userspace implementations. Nevertheless, userspace can still emulate all Arm
1075+
exceptions by manipulating individual registers using the KVM_SET_ONE_REG API.
1076+
10571077
See KVM_GET_VCPU_EVENTS for the data structure.
10581078

10591079

arch/arm/include/uapi/asm/kvm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ struct kvm_vcpu_events {
131131
struct {
132132
__u8 serror_pending;
133133
__u8 serror_has_esr;
134+
__u8 ext_dabt_pending;
134135
/* Align it to 8 bytes */
135-
__u8 pad[6];
136+
__u8 pad[5];
136137
__u64 serror_esr;
137138
} exception;
138139
__u32 reserved[12];

arch/arm/kvm/guest.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@ int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
255255
{
256256
events->exception.serror_pending = !!(*vcpu_hcr(vcpu) & HCR_VA);
257257

258+
/*
259+
* We never return a pending ext_dabt here because we deliver it to
260+
* the virtual CPU directly when setting the event and it's no longer
261+
* 'pending' at this point.
262+
*/
263+
258264
return 0;
259265
}
260266

@@ -263,12 +269,16 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
263269
{
264270
bool serror_pending = events->exception.serror_pending;
265271
bool has_esr = events->exception.serror_has_esr;
272+
bool ext_dabt_pending = events->exception.ext_dabt_pending;
266273

267274
if (serror_pending && has_esr)
268275
return -EINVAL;
269276
else if (serror_pending)
270277
kvm_inject_vabt(vcpu);
271278

279+
if (ext_dabt_pending)
280+
kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
281+
272282
return 0;
273283
}
274284

arch/arm64/include/uapi/asm/kvm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,9 @@ struct kvm_vcpu_events {
164164
struct {
165165
__u8 serror_pending;
166166
__u8 serror_has_esr;
167+
__u8 ext_dabt_pending;
167168
/* Align it to 8 bytes */
168-
__u8 pad[6];
169+
__u8 pad[5];
169170
__u64 serror_esr;
170171
} exception;
171172
__u32 reserved[12];

arch/arm64/kvm/guest.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,12 @@ int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
712712
if (events->exception.serror_pending && events->exception.serror_has_esr)
713713
events->exception.serror_esr = vcpu_get_vsesr(vcpu);
714714

715+
/*
716+
* We never return a pending ext_dabt here because we deliver it to
717+
* the virtual CPU directly when setting the event and it's no longer
718+
* 'pending' at this point.
719+
*/
720+
715721
return 0;
716722
}
717723

@@ -720,6 +726,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
720726
{
721727
bool serror_pending = events->exception.serror_pending;
722728
bool has_esr = events->exception.serror_has_esr;
729+
bool ext_dabt_pending = events->exception.ext_dabt_pending;
723730

724731
if (serror_pending && has_esr) {
725732
if (!cpus_have_const_cap(ARM64_HAS_RAS_EXTN))
@@ -733,6 +740,9 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
733740
kvm_inject_vabt(vcpu);
734741
}
735742

743+
if (ext_dabt_pending)
744+
kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
745+
736746
return 0;
737747
}
738748

arch/arm64/kvm/inject_fault.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu)
109109

110110
/**
111111
* kvm_inject_dabt - inject a data abort into the guest
112-
* @vcpu: The VCPU to receive the undefined exception
112+
* @vcpu: The VCPU to receive the data abort
113113
* @addr: The address to report in the DFAR
114114
*
115115
* It is assumed that this code is called from the VCPU thread and that the
@@ -125,7 +125,7 @@ void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr)
125125

126126
/**
127127
* kvm_inject_pabt - inject a prefetch abort into the guest
128-
* @vcpu: The VCPU to receive the undefined exception
128+
* @vcpu: The VCPU to receive the prefetch abort
129129
* @addr: The address to report in the DFAR
130130
*
131131
* It is assumed that this code is called from the VCPU thread and that the

include/uapi/linux/kvm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,7 @@ struct kvm_ppc_resize_hpt {
10071007
#define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
10081008
#define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
10091009
#define KVM_CAP_ARM_NISV_TO_USER 176
1010+
#define KVM_CAP_ARM_INJECT_EXT_DABT 177
10101011

10111012
#ifdef KVM_CAP_IRQ_ROUTING
10121013

virt/kvm/arm/arm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
218218
case KVM_CAP_VCPU_EVENTS:
219219
case KVM_CAP_ARM_IRQ_LINE_LAYOUT_2:
220220
case KVM_CAP_ARM_NISV_TO_USER:
221+
case KVM_CAP_ARM_INJECT_EXT_DABT:
221222
r = 1;
222223
break;
223224
case KVM_CAP_ARM_SET_DEVICE_ADDR:

0 commit comments

Comments
 (0)