[PATCH 17/18] arm64/kvm: Expose SDEI capability

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

 



This exposes SDEI capability, which is identified by KVM_CAP_ARM_SDEI.
Also, the ioctl interface (KVM_ARM_SDEI_INJECT) is introduced to allow
injecting KVM originated event from user space.

Besides, this implements the following APIs to register the notifier
and cancel the pending SDEI event: kvm_sdei_register_notifier() and
kvm_sdei_cancel().

Signed-off-by: Gavin Shan <gshan@xxxxxxxxxx>
---
 arch/arm64/include/asm/kvm_sdei.h |   4 +
 arch/arm64/kvm/arm.c              |   8 ++
 arch/arm64/kvm/reset.c            |   3 +
 arch/arm64/kvm/sdei.c             | 134 ++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h          |   4 +
 5 files changed, 153 insertions(+)

diff --git a/arch/arm64/include/asm/kvm_sdei.h b/arch/arm64/include/asm/kvm_sdei.h
index 70e613941577..67a5a398fe10 100644
--- a/arch/arm64/include/asm/kvm_sdei.h
+++ b/arch/arm64/include/asm/kvm_sdei.h
@@ -105,6 +105,10 @@ static inline bool kvm_sdei_num_is_valid(unsigned long num)
 
 int kvm_sdei_hypercall(struct kvm_vcpu *vcpu);
 void kvm_sdei_deliver(struct kvm_vcpu *vcpu);
+int kvm_sdei_register_notifier(unsigned long num,
+			       kvm_sdei_notify_func_t func);
+int kvm_sdei_inject(struct kvm_vcpu *vcpu, unsigned long num, bool force);
+int kvm_sdei_cancel(struct kvm_vcpu *vcpu, unsigned long num);
 void kvm_sdei_init(void);
 void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu);
 void kvm_sdei_vcpu_load(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index a79a4343bac6..4bec6c9ece18 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1201,6 +1201,14 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 
 		return kvm_arm_vcpu_finalize(vcpu, what);
 	}
+	case KVM_ARM_SDEI_INJECT: {
+		unsigned long num;
+
+		if (copy_from_user(&num, argp, sizeof(num)))
+			return -EFAULT;
+
+		return kvm_sdei_inject(vcpu, num, true);
+	}
 	default:
 		r = -EINVAL;
 	}
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index 6ed36be51b4b..f292bed61147 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -83,6 +83,9 @@ int kvm_arch_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 		r = has_vhe() && system_supports_address_auth() &&
 				 system_supports_generic_auth();
 		break;
+	case KVM_CAP_ARM_SDEI:
+		r = 1;
+		break;
 	default:
 		r = 0;
 	}
diff --git a/arch/arm64/kvm/sdei.c b/arch/arm64/kvm/sdei.c
index 0c5a16e8cbac..2c05edcb3fbb 100644
--- a/arch/arm64/kvm/sdei.c
+++ b/arch/arm64/kvm/sdei.c
@@ -229,6 +229,18 @@ void kvm_sdei_deliver(struct kvm_vcpu *vcpu)
 	spin_unlock(&vcpu->arch.sdei_lock);
 }
 
+int kvm_sdei_register_notifier(unsigned long num, kvm_sdei_notify_func_t func)
+{
+	struct kvm_sdei_priv *priv = kvm_sdei_find_priv(num);
+
+	if (!priv)
+		return -ENOENT;
+
+	priv->notifier = func;
+
+	return 0;
+}
+
 static int kvm_sdei_queue_event(struct kvm_vcpu *vcpu,
 				struct kvm_sdei_kvm_event *kevent)
 {
@@ -272,6 +284,128 @@ static int kvm_sdei_queue_event(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+int kvm_sdei_inject(struct kvm_vcpu *vcpu, unsigned long num, bool force)
+{
+	struct kvm_sdei_event *event = NULL;
+	struct kvm_sdei_kvm_event *kevent = NULL;
+	unsigned long event_type, event_priority;
+	int index, ret = 0;
+
+	/* Find the event */
+	spin_lock(&kvm_sdei_lock);
+	event = kvm_sdei_find_event(vcpu->kvm, num, &kevent, NULL, NULL);
+	if (!kevent || !event->priv) {
+		ret = -ENOENT;
+		goto unlock;
+	}
+
+	/*
+	 * We're unable to inject passthrou event, which means the
+	 * event should have been associated with KVM private event
+	 * or descriptor.
+	 */
+	spin_lock(&event->lock);
+	if (!event->priv) {
+		ret = -EINVAL;
+		goto unlock_event;
+	}
+
+	/*
+	 * The event should have been registered and enabled on the
+	 * target vCPU.
+	 */
+	event_type = event->priv->type;
+	event_priority = event->priv->priority;
+	index = (event_type == SDEI_EVENT_TYPE_PRIVATE) ? vcpu->vcpu_idx : 0;
+	if (!test_bit(index, kevent->registered) ||
+	    !test_bit(index, kevent->enabled)) {
+		ret = -EPERM;
+		goto unlock_event;
+	}
+
+	/*
+	 * We need deliver the event immediately when @force is
+	 * false. For this case, we need check if there is space
+	 * to do so.
+	 */
+	spin_lock(&vcpu->arch.sdei_lock);
+	if (!force) {
+		if (vcpu->arch.sdei_critical_event) {
+			ret = -ENOSPC;
+			goto unlock_vcpu;
+		}
+
+		if (vcpu->arch.sdei_normal_event &&
+		    event_type != SDEI_EVENT_PRIORITY_CRITICAL) {
+			ret = -ENOSPC;
+			goto unlock_vcpu;
+		}
+	}
+
+	ret = kvm_sdei_queue_event(vcpu, kevent);
+
+unlock_vcpu:
+	spin_unlock(&vcpu->arch.sdei_lock);
+unlock_event:
+	spin_unlock(&event->lock);
+unlock:
+	spin_unlock(&kvm_sdei_lock);
+	return ret;
+}
+
+int kvm_sdei_cancel(struct kvm_vcpu *vcpu, unsigned long num)
+{
+	struct kvm_sdei_event *event;
+	struct kvm_sdei_kvm_event *kevent = NULL;
+	struct kvm_sdei_vcpu_event *e, *vevent = NULL;
+	unsigned long event_num;
+	int ret = 0;
+
+	spin_lock(&vcpu->arch.sdei_lock);
+
+	list_for_each_entry(e, &vcpu->arch.sdei_events, link) {
+		event = e->event->event;
+		event_num = event->priv ? event->priv->num :
+					  event->event->event_num;
+		if (event_num == num) {
+			vevent = e;
+			break;
+		}
+	}
+
+	if (!vevent) {
+		ret = -ENOENT;
+		goto unlock;
+	}
+
+	/* The event can't be cancelled if it has been delivered */
+	if (vevent->users == 1 &&
+	    (vevent == vcpu->arch.sdei_critical_event ||
+	     vevent == vcpu->arch.sdei_normal_event)) {
+		ret = -EINPROGRESS;
+		goto unlock;
+	}
+
+	/* Release the vCPU event if necessary */
+	kevent = vevent->event;
+	vevent->users--;
+	if (!vevent->users) {
+		list_del(&vevent->link);
+		kfree(vevent);
+	}
+
+unlock:
+	spin_unlock(&vcpu->arch.sdei_lock);
+
+	if (kevent) {
+		spin_lock(&event->lock);
+		kevent->users--;
+		spin_unlock(&event->lock);
+	}
+
+	return ret;
+}
+
 /*
  * Queue the shared event to the target VMs where the event have been
  * registered and enabled. For the particular VM, the event is delivered
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index f6d86033c4fa..c9731fad8bf5 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1035,6 +1035,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_LAST_CPU 184
 #define KVM_CAP_SMALLER_MAXPHYADDR 185
 #define KVM_CAP_S390_DIAG318 186
+#define KVM_CAP_ARM_SDEI 187
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1536,6 +1537,9 @@ struct kvm_pv_cmd {
 /* Available with KVM_CAP_S390_PROTECTED */
 #define KVM_S390_PV_COMMAND		_IOWR(KVMIO, 0xc5, struct kvm_pv_cmd)
 
+/* Available with KVM_CAP_ARM_SDEI */
+#define KVM_ARM_SDEI_INJECT		_IOW(KVMIO, 0xc6, __u64)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
-- 
2.23.0

_______________________________________________
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