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