Re: [PATCH v2 2/2] KVM: arm/arm64: Allow user injection of external data aborts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Oct 08, 2019 at 02:03:07PM +0200, Alexander Graf wrote:
> 
> 
> On 08.10.19 11:36, Christoffer Dall wrote:
> > 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 <christoffer.dall@xxxxxxx>
> > ---
> >   Documentation/virt/kvm/api.txt    | 18 +++++++++++++++++-
> >   arch/arm/include/uapi/asm/kvm.h   |  3 ++-
> >   arch/arm/kvm/guest.c              |  3 +++
> >   arch/arm64/include/uapi/asm/kvm.h |  3 ++-
> >   arch/arm64/kvm/guest.c            |  3 +++
> >   arch/arm64/kvm/inject_fault.c     |  4 ++--
> >   include/uapi/linux/kvm.h          |  1 +
> >   virt/kvm/arm/arm.c                |  1 +
> >   8 files changed, 31 insertions(+), 5 deletions(-)
> > 
> > diff --git a/Documentation/virt/kvm/api.txt b/Documentation/virt/kvm/api.txt
> > index 7403f15657c2..10ebe8cfda29 100644
> > --- a/Documentation/virt/kvm/api.txt
> > +++ b/Documentation/virt/kvm/api.txt
> > @@ -968,6 +968,8 @@ The following bits are defined in the flags field:
> >   ARM/ARM64:
> > +User space may need to inject several types of events to the guest.
> > +
> >   If the guest accesses a device that is being emulated by the host kernel in
> >   such a way that a real device would generate a physical SError, KVM may make
> >   a virtual SError pending for that VCPU. This system error interrupt remains
> > @@ -1002,12 +1004,26 @@ Specifying exception.has_esr on a system that does not support it will return
> >   -EINVAL. Setting anything other than the lower 24bits of exception.serror_esr
> >   will return -EINVAL.
> > +If the guest performed an access to I/O memory which could not be handled by
> > +userspace, for example because of missing instruction syndrome decode
> > +information or because there is no device mapped at the accessed IPA, then
> > +userspace can ask the kernel to inject an external abort using the address
> > +from the exiting fault on the VCPU. It is a programming error to set
> > +ext_dabt_pending at the same time as any of the serror fields, or to set
> > +ext_dabt_pending after an exit which was not either KVM_EXIT_MMIO or
> > +KVM_EXIT_ARM_NISV. This feature is only available if the system supports
> > +KVM_CAP_ARM_INJECT_EXT_DABT. This is a helper which provides commonality in
> > +how userspace reports accesses for the above cases to guests, across different
> > +userspace implementations. Nevertheless, userspace can still emulate all Arm
> > +exceptions by manipulating individual registers using the KVM_SET_ONE_REG API.
> > +
> >   struct kvm_vcpu_events {
> >   	struct {
> >   		__u8 serror_pending;
> >   		__u8 serror_has_esr;
> > +		__u8 ext_dabt_pending;
> >   		/* Align it to 8 bytes */
> > -		__u8 pad[6];
> > +		__u8 pad[5];
> >   		__u64 serror_esr;
> >   	} exception;
> >   	__u32 reserved[12];
> > diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h
> > index 2769360f195c..03cd7c19a683 100644
> > --- a/arch/arm/include/uapi/asm/kvm.h
> > +++ b/arch/arm/include/uapi/asm/kvm.h
> > @@ -131,8 +131,9 @@ struct kvm_vcpu_events {
> >   	struct {
> >   		__u8 serror_pending;
> >   		__u8 serror_has_esr;
> > +		__u8 ext_dabt_pending;
> >   		/* Align it to 8 bytes */
> > -		__u8 pad[6];
> > +		__u8 pad[5];
> >   		__u64 serror_esr;
> >   	} exception;
> >   	__u32 reserved[12];
> > diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c
> > index 684cf64b4033..4154c5589501 100644
> > --- a/arch/arm/kvm/guest.c
> > +++ b/arch/arm/kvm/guest.c
> > @@ -263,11 +263,14 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> >   {
> >   	bool serror_pending = events->exception.serror_pending;
> >   	bool has_esr = events->exception.serror_has_esr;
> > +	bool has_ext_dabt_pending = events->exception.ext_dabt_pending;
> >   	if (serror_pending && has_esr)
> >   		return -EINVAL;
> >   	else if (serror_pending)
> >   		kvm_inject_vabt(vcpu);
> > +	else if (has_ext_dabt_pending)
> > +		kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> >   	return 0;
> >   }
> > diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> > index 67c21f9bdbad..d49c17a80491 100644
> > --- a/arch/arm64/include/uapi/asm/kvm.h
> > +++ b/arch/arm64/include/uapi/asm/kvm.h
> > @@ -164,8 +164,9 @@ struct kvm_vcpu_events {
> >   	struct {
> >   		__u8 serror_pending;
> >   		__u8 serror_has_esr;
> > +		__u8 ext_dabt_pending;
> >   		/* Align it to 8 bytes */
> > -		__u8 pad[6];
> > +		__u8 pad[5];
> >   		__u64 serror_esr;
> >   	} exception;
> >   	__u32 reserved[12];
> > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> > index dfd626447482..10e6e2144dca 100644
> > --- a/arch/arm64/kvm/guest.c
> > +++ b/arch/arm64/kvm/guest.c
> > @@ -720,6 +720,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> >   {
> >   	bool serror_pending = events->exception.serror_pending;
> >   	bool has_esr = events->exception.serror_has_esr;
> > +	bool has_ext_dabt_pending = events->exception.ext_dabt_pending;
> 
> The has_ is inconsistent here (and below in the copies of this function).

True, my bad.

> 
> What I'm really curious on is why it's written the way it is though. Why not
> just make "exception" be a named struct and refer to a pointer of that here?

I have no idea, but that's beyond this patch.
> 
>   struct kvm_arm_exception *e = &events->exception;
> 
>   if (e->serror_pending && e->serror_has_esr) {
>     ...
>   } else if (e->ext_dabt_pending) {
>     ...
>   }
> 
> Copying the values into their own local bools looks a bit convoluted to me.
> In fact, why do we copy u8s into bools in the first place?
> 
 I don't know, but probably another version of the if (!!foo)
 construct.  It could definitely be written differently, but it's easy
 enough to understand.

> >   	if (serror_pending && has_esr) {
> >   		if (!cpus_have_const_cap(ARM64_HAS_RAS_EXTN))
> > @@ -731,6 +732,8 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> >   			return -EINVAL;
> >   	} else if (serror_pending) {
> >   		kvm_inject_vabt(vcpu);
> > +	} else if (has_ext_dabt_pending) {
> > +		kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> 
> I'm a bit confused on the else if here. I understand that we probably don't
> want to inject an serror at the same time as a dabt, but shouldn't the API
> express that dependency?

Interesting point.  Re-reading the general definition of the
KVM_SET_VCPU_EVENTS api, I actually think the cleanest thing here is to
allow setting both at the same time.  I can't come up with a valid case
where the VMM would validly need to do that, but there'd be no harm in
doing it as far as I can tell.

I'll rework this so that checking
ext_dabt_pending is orthogonal for the serror stuff.
> 
> Do we have guarantees on the value of "serror_pending"? Can we assume it's
> always either 0 or 1 today given all known user space? Maybe we can create a
> union and make this an "exception_pending" enum instead which indicates
> which exception we want to inject?

With the change proposed above, I believe this concern goes away, as the
two fields are decoupled.

Thanks,

    Christoffer
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm



[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux