[PATCH v7 19/22] KVM: arm64: Support SDEI event migration

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

 



This supports migration for SDEI event handlers, states and context.
Several pseudo firmware registers are added to assist the migration
work.

   * KVM_REG_ARM_SDEI_EVENT_HANDLER_0
     KVM_REG_ARM_SDEI_EVENT_HANDLER_1
     KVM_REG_ARM_SDEI_EVENT_HANDLER_2
     KVM_REG_ARM_SDEI_EVENT_HANDLER_3

     128-bits in length. They're mapped to the handler's address
     and argument for Software Signaled event and Async PF event.
     Additinal two events are reserved for future needs without
     too much considerations to the compatible issue. Not too much
     bandwidth needed to migrate those two additional registers.

  * KVM_REG_ARM_SDEI_EVENT_REGISTERED
    KVM_REG_ARM_SDEI_EVENT_ENABLED
    KVM_REG_ARM_SDEI_EVENT_RUNNING
    KVM_REG_ARM_SDEI_EVENT_PENDING

    64-bits in length. They're mapped to registered, enabled,
    running and pending bitmap.

  * KVM_REG_ARM_SDEI_EVENT_CONTEXT

    2048-bits in length. It's mapped to saved or interrupted
    context.

  * KVM_REG_ARM_SDEI_PE_STATE

    64-bits in length. It's mapped to PE's states, which is masked
    or unmasked.

Signed-off-by: Gavin Shan <gshan@xxxxxxxxxx>
---
 arch/arm64/include/asm/kvm_sdei.h |   2 +
 arch/arm64/include/uapi/asm/kvm.h |  17 ++++
 arch/arm64/kvm/hypercalls.c       |  32 ++++++
 arch/arm64/kvm/sdei.c             | 161 ++++++++++++++++++++++++++++++
 4 files changed, 212 insertions(+)

diff --git a/arch/arm64/include/asm/kvm_sdei.h b/arch/arm64/include/asm/kvm_sdei.h
index a68d40137a88..d11964b88b58 100644
--- a/arch/arm64/include/asm/kvm_sdei.h
+++ b/arch/arm64/include/asm/kvm_sdei.h
@@ -73,6 +73,8 @@ int kvm_sdei_inject_event(struct kvm_vcpu *vcpu,
 			  unsigned int num, bool immediate);
 int kvm_sdei_cancel_event(struct kvm_vcpu *vcpu, unsigned int num);
 void kvm_sdei_deliver_event(struct kvm_vcpu *vcpu);
+int kvm_sdei_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+int kvm_sdei_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
 void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu);
 void kvm_sdei_destroy_vcpu(struct kvm_vcpu *vcpu);
 
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index cc3251381960..90247ce8de59 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -262,6 +262,11 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_FW			(0x0014 << KVM_REG_ARM_COPROC_SHIFT)
 #define KVM_REG_ARM_FW_REG(r)		(KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
 					 KVM_REG_ARM_FW | ((r) & 0xffff))
+#define KVM_REG_ARM_FW_REG_128(r)	(KVM_REG_ARM64 | KVM_REG_SIZE_U128 | \
+					 KVM_REG_ARM_FW | ((r) & 0xffff))
+#define KVM_REG_ARM_FW_REG_2048(r)	(KVM_REG_ARM64 | KVM_REG_SIZE_U2048 | \
+					 KVM_REG_ARM_FW | ((r) & 0xffff))
+
 #define KVM_REG_ARM_PSCI_VERSION	KVM_REG_ARM_FW_REG(0)
 #define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1	KVM_REG_ARM_FW_REG(1)
 #define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL		0
@@ -288,6 +293,18 @@ struct kvm_arm_copy_mte_tags {
 #define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL		1
 #define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED	2
 
+/* SDEI registers */
+#define KVM_REG_ARM_SDEI_EVENT_HANDLER_0	KVM_REG_ARM_FW_REG_128(4)
+#define KVM_REG_ARM_SDEI_EVENT_HANDLER_1	KVM_REG_ARM_FW_REG_128(5)
+#define KVM_REG_ARM_SDEI_EVENT_HANDLER_2	KVM_REG_ARM_FW_REG_128(6)
+#define KVM_REG_ARM_SDEI_EVENT_HANDLER_3	KVM_REG_ARM_FW_REG_128(7)
+#define KVM_REG_ARM_SDEI_EVENT_REGISTERED	KVM_REG_ARM_FW_REG(8)
+#define KVM_REG_ARM_SDEI_EVENT_ENABLED		KVM_REG_ARM_FW_REG(9)
+#define KVM_REG_ARM_SDEI_EVENT_RUNNING		KVM_REG_ARM_FW_REG(10)
+#define KVM_REG_ARM_SDEI_EVENT_PENDING		KVM_REG_ARM_FW_REG(11)
+#define KVM_REG_ARM_SDEI_EVENT_CONTEXT		KVM_REG_ARM_FW_REG_2048(12)
+#define KVM_REG_ARM_SDEI_PE_STATE		KVM_REG_ARM_FW_REG(13)
+
 /* SVE registers */
 #define KVM_REG_ARM64_SVE		(0x15 << KVM_REG_ARM_COPROC_SHIFT)
 
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index 8e0df54d1422..d330aee968a9 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -290,6 +290,16 @@ static const u64 kvm_arm_fw_reg_ids[] = {
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2,
 	KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3,
+	KVM_REG_ARM_SDEI_EVENT_HANDLER_0,
+	KVM_REG_ARM_SDEI_EVENT_HANDLER_1,
+	KVM_REG_ARM_SDEI_EVENT_HANDLER_2,
+	KVM_REG_ARM_SDEI_EVENT_HANDLER_3,
+	KVM_REG_ARM_SDEI_EVENT_REGISTERED,
+	KVM_REG_ARM_SDEI_EVENT_ENABLED,
+	KVM_REG_ARM_SDEI_EVENT_RUNNING,
+	KVM_REG_ARM_SDEI_EVENT_PENDING,
+	KVM_REG_ARM_SDEI_EVENT_CONTEXT,
+	KVM_REG_ARM_SDEI_PE_STATE,
 	KVM_REG_ARM_STD_BMAP,
 	KVM_REG_ARM_STD_HYP_BMAP,
 	KVM_REG_ARM_VENDOR_HYP_BMAP,
@@ -387,6 +397,17 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
 		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
 		break;
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_0:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_1:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_2:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_3:
+	case KVM_REG_ARM_SDEI_EVENT_REGISTERED:
+	case KVM_REG_ARM_SDEI_EVENT_ENABLED:
+	case KVM_REG_ARM_SDEI_EVENT_RUNNING:
+	case KVM_REG_ARM_SDEI_EVENT_PENDING:
+	case KVM_REG_ARM_SDEI_EVENT_CONTEXT:
+	case KVM_REG_ARM_SDEI_PE_STATE:
+		return kvm_sdei_get_reg(vcpu, reg);
 	case KVM_REG_ARM_STD_BMAP:
 		val = READ_ONCE(smccc_feat->std_bmap);
 		break;
@@ -537,6 +558,17 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 			return -EINVAL;
 
 		return 0;
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_0:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_1:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_2:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_3:
+	case KVM_REG_ARM_SDEI_EVENT_REGISTERED:
+	case KVM_REG_ARM_SDEI_EVENT_ENABLED:
+	case KVM_REG_ARM_SDEI_EVENT_RUNNING:
+	case KVM_REG_ARM_SDEI_EVENT_PENDING:
+	case KVM_REG_ARM_SDEI_EVENT_CONTEXT:
+	case KVM_REG_ARM_SDEI_PE_STATE:
+		return kvm_sdei_set_reg(vcpu, reg);
 	case KVM_REG_ARM_STD_BMAP:
 	case KVM_REG_ARM_STD_HYP_BMAP:
 	case KVM_REG_ARM_VENDOR_HYP_BMAP:
diff --git a/arch/arm64/kvm/sdei.c b/arch/arm64/kvm/sdei.c
index f95b9bcce13c..c14532de48f5 100644
--- a/arch/arm64/kvm/sdei.c
+++ b/arch/arm64/kvm/sdei.c
@@ -498,6 +498,167 @@ void kvm_sdei_deliver_event(struct kvm_vcpu *vcpu)
 	set_bit(num, &vsdei->running);
 }
 
+int kvm_sdei_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
+	struct kvm_sdei_event_context *ctxt = &vsdei->ctxt;
+	struct kvm_sdei_event_handler handler;
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	unsigned int num, i;
+	unsigned long val, *pstate = NULL;
+
+	if (!vsdei)
+		return -EPERM;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_0:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_1:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_2:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_3:
+		 num = (reg->id & 0xffff) -
+		       (KVM_REG_ARM_SDEI_EVENT_HANDLER_0 & 0xffff);
+
+		if (num < KVM_NR_SDEI_EVENTS)
+			handler = vsdei->handlers[num];
+		else
+			memset(&handler, 0, sizeof(handler));
+
+		if (copy_to_user(uaddr, &handler, KVM_REG_SIZE(reg->id)))
+			return -EFAULT;
+
+		break;
+	case KVM_REG_ARM_SDEI_EVENT_REGISTERED:
+		pstate = &vsdei->registered;
+		fallthrough;
+	case KVM_REG_ARM_SDEI_EVENT_ENABLED:
+		pstate = pstate ? : &vsdei->enabled;
+		fallthrough;
+	case KVM_REG_ARM_SDEI_EVENT_RUNNING:
+		pstate = pstate ? : &vsdei->running;
+		fallthrough;
+	case KVM_REG_ARM_SDEI_EVENT_PENDING:
+		pstate = pstate ? : &vsdei->pending;
+		if (copy_to_user(uaddr, pstate, KVM_REG_SIZE(reg->id)))
+			return -EFAULT;
+
+		break;
+	case KVM_REG_ARM_SDEI_EVENT_CONTEXT:
+		if (copy_to_user(uaddr, &ctxt->pc, sizeof(ctxt->pc)))
+			return -EFAULT;
+
+		uaddr += sizeof(ctxt->pc);
+		if (copy_to_user(uaddr, &ctxt->pstate, sizeof(ctxt->pstate)))
+			return -EFAULT;
+
+		uaddr += sizeof(ctxt->pstate);
+		for (i = 0; i < ARRAY_SIZE(ctxt->regs); i++) {
+			if (copy_to_user(uaddr, &ctxt->regs[i],
+					 sizeof(ctxt->regs[i])))
+				return -EFAULT;
+
+			uaddr += sizeof(ctxt->regs[i]);
+		}
+
+		break;
+	case KVM_REG_ARM_SDEI_PE_STATE:
+		val = (vcpu->arch.flags & KVM_ARM64_SDEI_MASKED) ? 1 : 0;
+		if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
+			return -EFAULT;
+
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+int kvm_sdei_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
+	struct kvm_sdei_event_context *ctxt = &vsdei->ctxt;
+	void __user *uaddr = (void __user *)(long)reg->addr;
+	unsigned int num, i;
+	unsigned long val, *pstate = NULL;
+
+	if (!vsdei)
+		return -EPERM;
+
+	switch (reg->id) {
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_0:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_1:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_2:
+	case KVM_REG_ARM_SDEI_EVENT_HANDLER_3:
+		num = (reg->id & 0xffff) -
+		      (KVM_REG_ARM_SDEI_EVENT_HANDLER_0 & 0xffff);
+		if (num >= KVM_NR_SDEI_EVENTS)
+			break;
+
+		if (copy_from_user(&vsdei->handlers[num], uaddr,
+				   sizeof(vsdei->handlers[num])))
+			return -EFAULT;
+
+		break;
+	case KVM_REG_ARM_SDEI_EVENT_REGISTERED:
+		pstate = &vsdei->registered;
+		fallthrough;
+	case KVM_REG_ARM_SDEI_EVENT_ENABLED:
+		pstate = pstate ? : &vsdei->enabled;
+		fallthrough;
+	case KVM_REG_ARM_SDEI_EVENT_RUNNING:
+		pstate = pstate ? : &vsdei->running;
+		fallthrough;
+	case KVM_REG_ARM_SDEI_EVENT_PENDING:
+		pstate = pstate ? : &vsdei->pending;
+		if (copy_from_user(&val, uaddr, sizeof(val)))
+			return -EFAULT;
+
+		*pstate = (val & GENMASK(KVM_NR_SDEI_EVENTS - 1, 0));
+		if (!(vcpu->arch.flags & KVM_ARM64_SDEI_MASKED) &&
+		    pstate == &vsdei->pending &&
+		    vsdei->pending)
+			kvm_make_request(KVM_REQ_SDEI, vcpu);
+
+		break;
+	case KVM_REG_ARM_SDEI_EVENT_CONTEXT:
+		if (copy_from_user(&ctxt->pc, uaddr, sizeof(ctxt->pc)))
+			return -EFAULT;
+
+		uaddr += sizeof(ctxt->pc);
+		if (copy_from_user(&ctxt->pstate, uaddr, sizeof(ctxt->pstate)))
+			return -EFAULT;
+
+		uaddr += sizeof(ctxt->pstate);
+		for (i = 0; i < ARRAY_SIZE(ctxt->regs); i++) {
+			if (copy_from_user(&ctxt->regs[i], uaddr,
+					   sizeof(ctxt->regs[i])))
+				return -EFAULT;
+
+			uaddr += sizeof(ctxt->regs[i]);
+		}
+
+		break;
+	case KVM_REG_ARM_SDEI_PE_STATE:
+		if (copy_from_user(&val, uaddr, sizeof(val)))
+			return -EFAULT;
+
+		if (val) {
+			vcpu->arch.flags |= KVM_ARM64_SDEI_MASKED;
+		} else {
+			vcpu->arch.flags &= ~KVM_ARM64_SDEI_MASKED;
+			if (vsdei->pending)
+				kvm_make_request(KVM_REQ_SDEI, vcpu);
+		}
+
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return 0;
+
+}
+
 void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu)
 {
 	struct kvm_sdei_vcpu *vsdei;
-- 
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