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

From: Gavin Shan
Date: Fri May 27 2022 - 04:07:23 EST


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