All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gavin Shan <gshan@redhat.com>
To: kvmarm@lists.cs.columbia.edu
Cc: linux-kernel@vger.kernel.org, eauger@redhat.com,
	oupton@google.com, Jonathan.Cameron@huawei.com,
	vkuznets@redhat.com, will@kernel.org, shannon.zhaosl@gmail.com,
	james.morse@arm.com, mark.rutland@arm.com, maz@kernel.org,
	pbonzini@redhat.com, shan.gavin@gmail.com
Subject: [PATCH v6 03/18] KVM: arm64: Add SDEI virtualization infrastructure
Date: Sun,  3 Apr 2022 23:38:56 +0800	[thread overview]
Message-ID: <20220403153911.12332-4-gshan@redhat.com> (raw)
In-Reply-To: <20220403153911.12332-1-gshan@redhat.com>

Software Delegated Exception Interface (SDEI) provides a mechanism
for registering and servicing system events, as defined by ARM DEN0054C
specification. One of these events will be used by Asynchronous Page
Fault (Async PF) to deliver notifications from host to guest.

The events are classified into shared and private ones according to
their scopes. The shared events are system or VM scoped, but the
private events are CPU or VCPU scoped. The shared events can be
registered, enabled, unregistered and reset through hypercalls
issued from any VCPU. However, the private events are registered,
enabled, unregistered and reset on the calling VCPU through
hypercalls. Besides, the events are also classified into critical
and normal events according their priority. During event delivery
and handling, the normal event can be preempted by another critical
event, but not in reverse way. The critical event is never preempted
by another normal event.

This introduces SDEI virtualization infrastructure for various objects
used in the implementation. Currently, we don't support the shared
event.

  * kvm_sdei_exposed_event
    The event which are defined and exposed by KVM. The event can't
    be registered until it's exposed. Besides, all the information
    in this event can't be changed after it's exposed.

  * kvm_sdei_event
    The events are created based on the exposed events. Their states
    are changed when hypercalls are received or they are delivered
    to guest for handling.

  * kvm_sdei_vcpu_context
    The vcpu context helps to handle events. The interrupted context
    is saved before the event handler is executed, and restored after
    the event handler is to finish.

  * kvm_sdei_vcpu
    Place holder for all objects for one particular VCPU.

The error of SDEI_NOT_SUPPORTED is returned for all hypercalls for now.
They will be supported one by one in the subsequent patches.

Link: https://developer.arm.com/documentation/den0054/latest
Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/include/asm/kvm_sdei.h | 148 ++++++++++++++++++++++++++++++
 arch/arm64/kvm/Makefile           |   2 +-
 arch/arm64/kvm/arm.c              |   4 +
 arch/arm64/kvm/hypercalls.c       |   3 +
 arch/arm64/kvm/sdei.c             |  98 ++++++++++++++++++++
 include/uapi/linux/arm_sdei.h     |   4 +
 7 files changed, 259 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/include/asm/kvm_sdei.h
 create mode 100644 arch/arm64/kvm/sdei.c

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e3b25dc6c367..7644a400c4a8 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -343,6 +343,7 @@ struct kvm_vcpu_arch {
 	 * Anything that is not used directly from assembly code goes
 	 * here.
 	 */
+	struct kvm_sdei_vcpu *sdei;
 
 	/*
 	 * Guest registers we preserve during guest debugging.
diff --git a/arch/arm64/include/asm/kvm_sdei.h b/arch/arm64/include/asm/kvm_sdei.h
new file mode 100644
index 000000000000..2dbfb3ae0a48
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_sdei.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Definitions of various KVM SDEI events.
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author(s): Gavin Shan <gshan@redhat.com>
+ */
+
+#ifndef __ARM64_KVM_SDEI_H__
+#define __ARM64_KVM_SDEI_H__
+
+#include <uapi/linux/arm_sdei.h>
+#include <linux/arm-smccc.h>
+#include <linux/bits.h>
+#include <linux/spinlock.h>
+
+/*
+ * The event which are defined and exposed by KVM. The event can't
+ * be registered until it's exposed. Besides, all the information
+ * in this event can't be changed after it's exposed.
+ */
+struct kvm_sdei_exposed_event {
+	unsigned int	num;
+	unsigned char	type;
+	unsigned char	signaled;
+	unsigned char	priority;
+};
+
+/*
+ * Currently, only the private events are supported. The events are
+ * created based on the exposed events and their states are changed
+ * when hypercalls are received or they are delivered to guest for
+ * handling.
+ */
+struct kvm_sdei_event {
+	struct kvm_sdei_exposed_event	*exposed_event;
+
+	unsigned char			route_mode;
+	unsigned long			route_affinity;
+	unsigned long			ep_address;
+	unsigned long			ep_arg;
+#define KVM_SDEI_EVENT_STATE_REGISTERED		BIT(0)
+#define KVM_SDEI_EVENT_STATE_ENABLED		BIT(1)
+#define KVM_SDEI_EVENT_STATE_UNREGISTER_PENDING	BIT(2)
+	unsigned long			state;
+	unsigned long			event_count;
+};
+
+/*
+ * The vcpu context helps to handle events. The preempted or interrupted
+ * context is saved before the event handler is executed, and restored
+ * after the event handler is to finish. The event with normal priority
+ * can be preempted by the one with critical priority. So there can be
+ * two contexts on one particular vcpu for the events with normal and
+ * critical priority separately.
+ */
+struct kvm_sdei_vcpu_context {
+	struct kvm_sdei_event	*event;
+	unsigned long		regs[18];
+	unsigned long		pc;
+	unsigned long		pstate;
+};
+
+struct kvm_sdei_vcpu {
+	spinlock_t			lock;
+	struct kvm_sdei_event		*events;
+	unsigned char			masked;
+	unsigned long			critical_event_count;
+	unsigned long			normal_event_count;
+	struct kvm_sdei_vcpu_context	context[SDEI_EVENT_PRIORITY_CRITICAL + 1];
+};
+
+/*
+ * According to SDEI specification (v1.1), the event number spans 32-bits
+ * and the lower 24-bits are used as the (real) event number. I don't
+ * think we can use that much event numbers in one system. So we reserve
+ * two bits from the 24-bits real event number, to indicate its types:
+ * physical or virtual event. One reserved bit is enough for now, but
+ * two bits are reserved for possible extension in future.
+ *
+ * The physical events are owned by firmware while the virtual events
+ * are used by VMM and KVM.
+ */
+#define KVM_SDEI_EVENT_NUM_TYPE_SHIFT	22
+#define KVM_SDEI_EVENT_NUM_TYPE_MASK	(3 << KVM_SDEI_EVENT_NUM_TYPE_SHIFT)
+#define KVM_SDEI_EVENT_NUM_TYPE_PHYS	0
+#define KVM_SDEI_EVENT_NUM_TYPE_VIRT	1
+
+static inline bool kvm_sdei_is_virtual(unsigned int num)
+{
+	unsigned int type;
+
+	type = (num & KVM_SDEI_EVENT_NUM_TYPE_MASK) >>
+	       KVM_SDEI_EVENT_NUM_TYPE_SHIFT;
+	if (type == KVM_SDEI_EVENT_NUM_TYPE_VIRT)
+		return true;
+
+	return false;
+}
+
+static inline bool kvm_sdei_is_sw_signaled(unsigned int num)
+{
+	return num == SDEI_SW_SIGNALED_EVENT;
+}
+
+static inline bool kvm_sdei_is_supported(unsigned int num)
+{
+	return kvm_sdei_is_sw_signaled(num) ||
+	       kvm_sdei_is_virtual(num);
+}
+
+static inline bool kvm_sdei_is_critical(unsigned char priority)
+{
+	return priority == SDEI_EVENT_PRIORITY_CRITICAL;
+}
+
+static inline bool kvm_sdei_is_normal(unsigned char priority)
+{
+	return priority == SDEI_EVENT_PRIORITY_NORMAL;
+}
+
+#define KVM_SDEI_REGISTERED_EVENT_FUNC(func, field)			\
+static inline bool kvm_sdei_is_##func(struct kvm_sdei_event *event)	\
+{									\
+	return !!(event->state & KVM_SDEI_EVENT_STATE_##field);		\
+}									\
+									\
+static inline void kvm_sdei_set_##func(struct kvm_sdei_event *event)	\
+{									\
+	event->state |= KVM_SDEI_EVENT_STATE_##field;			\
+}									\
+									\
+static inline void kvm_sdei_clear_##func(struct kvm_sdei_event *event)	\
+{									\
+	event->state &= ~KVM_SDEI_EVENT_STATE_##field;			\
+}
+
+KVM_SDEI_REGISTERED_EVENT_FUNC(registered, REGISTERED)
+KVM_SDEI_REGISTERED_EVENT_FUNC(enabled, ENABLED)
+KVM_SDEI_REGISTERED_EVENT_FUNC(unregister_pending, UNREGISTER_PENDING)
+
+/* APIs */
+int kvm_sdei_call(struct kvm_vcpu *vcpu);
+void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu);
+void kvm_sdei_destroy_vcpu(struct kvm_vcpu *vcpu);
+
+#endif /* __ARM64_KVM_SDEI_H__ */
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 261644b1a6bb..d6ced92ae3f0 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -14,7 +14,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
 	 inject_fault.o va_layout.o handle_exit.o \
 	 guest.o debug.o reset.o sys_regs.o \
 	 vgic-sys-reg-v3.o fpsimd.o pmu.o pkvm.o \
-	 arch_timer.o trng.o vmid.o \
+	 arch_timer.o trng.o vmid.o sdei.o \
 	 vgic/vgic.o vgic/vgic-init.o \
 	 vgic/vgic-irqfd.o vgic/vgic-v2.o \
 	 vgic/vgic-v3.o vgic/vgic-v4.o \
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 523bc934fe2f..227c0e390571 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -38,6 +38,7 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_sdei.h>
 #include <asm/sections.h>
 
 #include <kvm/arm_hypercalls.h>
@@ -331,6 +332,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 
 	kvm_arm_pvtime_vcpu_init(&vcpu->arch);
 
+	kvm_sdei_create_vcpu(vcpu);
+
 	vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
 
 	err = kvm_vgic_vcpu_init(vcpu);
@@ -352,6 +355,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 	kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
 	kvm_timer_vcpu_terminate(vcpu);
 	kvm_pmu_vcpu_destroy(vcpu);
+	kvm_sdei_destroy_vcpu(vcpu);
 
 	kvm_arm_vcpu_destroy(vcpu);
 }
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index b659387d8919..6aa027a4cee8 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -5,6 +5,7 @@
 #include <linux/kvm_host.h>
 
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_sdei.h>
 
 #include <kvm/arm_hypercalls.h>
 #include <kvm/arm_psci.h>
@@ -93,6 +94,8 @@ static int kvm_hvc_standard(struct kvm_vcpu *vcpu, u32 func)
 	case PSCI_1_1_FN_SYSTEM_RESET2:
 	case PSCI_1_1_FN64_SYSTEM_RESET2:
 		return kvm_psci_call(vcpu);
+	case SDEI_1_0_FN_SDEI_VERSION ... SDEI_1_1_FN_SDEI_FEATURES:
+		return kvm_sdei_call(vcpu);
 	}
 
 	smccc_set_retval(vcpu, val, 0, 0, 0);
diff --git a/arch/arm64/kvm/sdei.c b/arch/arm64/kvm/sdei.c
new file mode 100644
index 000000000000..3507e33ec00e
--- /dev/null
+++ b/arch/arm64/kvm/sdei.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SDEI virtualization support.
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author(s): Gavin Shan <gshan@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/kvm_host.h>
+#include <linux/slab.h>
+#include <kvm/arm_hypercalls.h>
+#include <asm/kvm_sdei.h>
+
+static struct kvm_sdei_exposed_event exposed_events[] = {
+	{ .num      = SDEI_SW_SIGNALED_EVENT,
+	  .type     = SDEI_EVENT_TYPE_PRIVATE,
+	  .signaled = 1,
+	  .priority = SDEI_EVENT_PRIORITY_NORMAL,
+	},
+};
+
+#define kvm_sdei_for_each_exposed_event(event, idx)	\
+	for (idx = 0, event = &exposed_events[0];	\
+	     idx < ARRAY_SIZE(exposed_events);		\
+	     idx++, event++)
+
+int kvm_sdei_call(struct kvm_vcpu *vcpu)
+{
+	struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
+	u32 func = smccc_get_function(vcpu);
+	bool has_result = true;
+	unsigned long ret;
+
+	/*
+	 * We don't have return value for COMPLETE or COMPLETE_AND_RESUME
+	 * hypercalls. Otherwise, the restored context will be corrupted.
+	 */
+	if (func == SDEI_1_0_FN_SDEI_EVENT_COMPLETE ||
+	    func == SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME)
+		has_result = false;
+
+	if (!vsdei) {
+		ret = SDEI_NOT_SUPPORTED;
+		goto out;
+	}
+
+	switch (func) {
+	default:
+		ret = SDEI_NOT_SUPPORTED;
+	}
+
+out:
+	if (has_result)
+		smccc_set_retval(vcpu, ret, 0, 0, 0);
+
+	return 1;
+}
+
+void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu)
+{
+	struct kvm_sdei_vcpu *vsdei;
+	struct kvm_sdei_exposed_event *exposed_event;
+	struct kvm_sdei_event *events;
+	unsigned int i;
+
+	vsdei = kzalloc(sizeof(*vsdei), GFP_KERNEL_ACCOUNT);
+	if (!vsdei)
+		return;
+
+	events = kcalloc(ARRAY_SIZE(exposed_events), sizeof(*events),
+			 GFP_KERNEL_ACCOUNT);
+	if (!events) {
+		kfree(vsdei);
+		return;
+	}
+
+	kvm_sdei_for_each_exposed_event(exposed_event, i)
+		events[i].exposed_event = exposed_event;
+
+	spin_lock_init(&vsdei->lock);
+	vsdei->events = events;
+	vsdei->masked = 1;
+	vcpu->arch.sdei = vsdei;
+}
+
+void kvm_sdei_destroy_vcpu(struct kvm_vcpu *vcpu)
+{
+	struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
+
+	if (!vsdei)
+		return;
+
+	vcpu->arch.sdei = NULL;
+	kfree(vsdei->events);
+	kfree(vsdei);
+}
diff --git a/include/uapi/linux/arm_sdei.h b/include/uapi/linux/arm_sdei.h
index af0630ba5437..572c77c59af6 100644
--- a/include/uapi/linux/arm_sdei.h
+++ b/include/uapi/linux/arm_sdei.h
@@ -22,8 +22,12 @@
 #define SDEI_1_0_FN_SDEI_PE_UNMASK			SDEI_1_0_FN(0x0C)
 #define SDEI_1_0_FN_SDEI_INTERRUPT_BIND			SDEI_1_0_FN(0x0D)
 #define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE		SDEI_1_0_FN(0x0E)
+#define SDEI_1_1_FN_SDEI_EVENT_SIGNAL			SDEI_1_0_FN(0x0F)
 #define SDEI_1_0_FN_SDEI_PRIVATE_RESET			SDEI_1_0_FN(0x11)
 #define SDEI_1_0_FN_SDEI_SHARED_RESET			SDEI_1_0_FN(0x12)
+#define SDEI_1_1_FN_SDEI_FEATURES			SDEI_1_0_FN(0x30)
+
+#define SDEI_SW_SIGNALED_EVENT	0
 
 #define SDEI_VERSION_MAJOR_SHIFT			48
 #define SDEI_VERSION_MAJOR_MASK				0x7fff
-- 
2.23.0


WARNING: multiple messages have this Message-ID (diff)
From: Gavin Shan <gshan@redhat.com>
To: kvmarm@lists.cs.columbia.edu
Cc: maz@kernel.org, linux-kernel@vger.kernel.org, eauger@redhat.com,
	shan.gavin@gmail.com, Jonathan.Cameron@huawei.com,
	pbonzini@redhat.com, vkuznets@redhat.com, will@kernel.org
Subject: [PATCH v6 03/18] KVM: arm64: Add SDEI virtualization infrastructure
Date: Sun,  3 Apr 2022 23:38:56 +0800	[thread overview]
Message-ID: <20220403153911.12332-4-gshan@redhat.com> (raw)
In-Reply-To: <20220403153911.12332-1-gshan@redhat.com>

Software Delegated Exception Interface (SDEI) provides a mechanism
for registering and servicing system events, as defined by ARM DEN0054C
specification. One of these events will be used by Asynchronous Page
Fault (Async PF) to deliver notifications from host to guest.

The events are classified into shared and private ones according to
their scopes. The shared events are system or VM scoped, but the
private events are CPU or VCPU scoped. The shared events can be
registered, enabled, unregistered and reset through hypercalls
issued from any VCPU. However, the private events are registered,
enabled, unregistered and reset on the calling VCPU through
hypercalls. Besides, the events are also classified into critical
and normal events according their priority. During event delivery
and handling, the normal event can be preempted by another critical
event, but not in reverse way. The critical event is never preempted
by another normal event.

This introduces SDEI virtualization infrastructure for various objects
used in the implementation. Currently, we don't support the shared
event.

  * kvm_sdei_exposed_event
    The event which are defined and exposed by KVM. The event can't
    be registered until it's exposed. Besides, all the information
    in this event can't be changed after it's exposed.

  * kvm_sdei_event
    The events are created based on the exposed events. Their states
    are changed when hypercalls are received or they are delivered
    to guest for handling.

  * kvm_sdei_vcpu_context
    The vcpu context helps to handle events. The interrupted context
    is saved before the event handler is executed, and restored after
    the event handler is to finish.

  * kvm_sdei_vcpu
    Place holder for all objects for one particular VCPU.

The error of SDEI_NOT_SUPPORTED is returned for all hypercalls for now.
They will be supported one by one in the subsequent patches.

Link: https://developer.arm.com/documentation/den0054/latest
Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 arch/arm64/include/asm/kvm_host.h |   1 +
 arch/arm64/include/asm/kvm_sdei.h | 148 ++++++++++++++++++++++++++++++
 arch/arm64/kvm/Makefile           |   2 +-
 arch/arm64/kvm/arm.c              |   4 +
 arch/arm64/kvm/hypercalls.c       |   3 +
 arch/arm64/kvm/sdei.c             |  98 ++++++++++++++++++++
 include/uapi/linux/arm_sdei.h     |   4 +
 7 files changed, 259 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/include/asm/kvm_sdei.h
 create mode 100644 arch/arm64/kvm/sdei.c

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e3b25dc6c367..7644a400c4a8 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -343,6 +343,7 @@ struct kvm_vcpu_arch {
 	 * Anything that is not used directly from assembly code goes
 	 * here.
 	 */
+	struct kvm_sdei_vcpu *sdei;
 
 	/*
 	 * Guest registers we preserve during guest debugging.
diff --git a/arch/arm64/include/asm/kvm_sdei.h b/arch/arm64/include/asm/kvm_sdei.h
new file mode 100644
index 000000000000..2dbfb3ae0a48
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_sdei.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Definitions of various KVM SDEI events.
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author(s): Gavin Shan <gshan@redhat.com>
+ */
+
+#ifndef __ARM64_KVM_SDEI_H__
+#define __ARM64_KVM_SDEI_H__
+
+#include <uapi/linux/arm_sdei.h>
+#include <linux/arm-smccc.h>
+#include <linux/bits.h>
+#include <linux/spinlock.h>
+
+/*
+ * The event which are defined and exposed by KVM. The event can't
+ * be registered until it's exposed. Besides, all the information
+ * in this event can't be changed after it's exposed.
+ */
+struct kvm_sdei_exposed_event {
+	unsigned int	num;
+	unsigned char	type;
+	unsigned char	signaled;
+	unsigned char	priority;
+};
+
+/*
+ * Currently, only the private events are supported. The events are
+ * created based on the exposed events and their states are changed
+ * when hypercalls are received or they are delivered to guest for
+ * handling.
+ */
+struct kvm_sdei_event {
+	struct kvm_sdei_exposed_event	*exposed_event;
+
+	unsigned char			route_mode;
+	unsigned long			route_affinity;
+	unsigned long			ep_address;
+	unsigned long			ep_arg;
+#define KVM_SDEI_EVENT_STATE_REGISTERED		BIT(0)
+#define KVM_SDEI_EVENT_STATE_ENABLED		BIT(1)
+#define KVM_SDEI_EVENT_STATE_UNREGISTER_PENDING	BIT(2)
+	unsigned long			state;
+	unsigned long			event_count;
+};
+
+/*
+ * The vcpu context helps to handle events. The preempted or interrupted
+ * context is saved before the event handler is executed, and restored
+ * after the event handler is to finish. The event with normal priority
+ * can be preempted by the one with critical priority. So there can be
+ * two contexts on one particular vcpu for the events with normal and
+ * critical priority separately.
+ */
+struct kvm_sdei_vcpu_context {
+	struct kvm_sdei_event	*event;
+	unsigned long		regs[18];
+	unsigned long		pc;
+	unsigned long		pstate;
+};
+
+struct kvm_sdei_vcpu {
+	spinlock_t			lock;
+	struct kvm_sdei_event		*events;
+	unsigned char			masked;
+	unsigned long			critical_event_count;
+	unsigned long			normal_event_count;
+	struct kvm_sdei_vcpu_context	context[SDEI_EVENT_PRIORITY_CRITICAL + 1];
+};
+
+/*
+ * According to SDEI specification (v1.1), the event number spans 32-bits
+ * and the lower 24-bits are used as the (real) event number. I don't
+ * think we can use that much event numbers in one system. So we reserve
+ * two bits from the 24-bits real event number, to indicate its types:
+ * physical or virtual event. One reserved bit is enough for now, but
+ * two bits are reserved for possible extension in future.
+ *
+ * The physical events are owned by firmware while the virtual events
+ * are used by VMM and KVM.
+ */
+#define KVM_SDEI_EVENT_NUM_TYPE_SHIFT	22
+#define KVM_SDEI_EVENT_NUM_TYPE_MASK	(3 << KVM_SDEI_EVENT_NUM_TYPE_SHIFT)
+#define KVM_SDEI_EVENT_NUM_TYPE_PHYS	0
+#define KVM_SDEI_EVENT_NUM_TYPE_VIRT	1
+
+static inline bool kvm_sdei_is_virtual(unsigned int num)
+{
+	unsigned int type;
+
+	type = (num & KVM_SDEI_EVENT_NUM_TYPE_MASK) >>
+	       KVM_SDEI_EVENT_NUM_TYPE_SHIFT;
+	if (type == KVM_SDEI_EVENT_NUM_TYPE_VIRT)
+		return true;
+
+	return false;
+}
+
+static inline bool kvm_sdei_is_sw_signaled(unsigned int num)
+{
+	return num == SDEI_SW_SIGNALED_EVENT;
+}
+
+static inline bool kvm_sdei_is_supported(unsigned int num)
+{
+	return kvm_sdei_is_sw_signaled(num) ||
+	       kvm_sdei_is_virtual(num);
+}
+
+static inline bool kvm_sdei_is_critical(unsigned char priority)
+{
+	return priority == SDEI_EVENT_PRIORITY_CRITICAL;
+}
+
+static inline bool kvm_sdei_is_normal(unsigned char priority)
+{
+	return priority == SDEI_EVENT_PRIORITY_NORMAL;
+}
+
+#define KVM_SDEI_REGISTERED_EVENT_FUNC(func, field)			\
+static inline bool kvm_sdei_is_##func(struct kvm_sdei_event *event)	\
+{									\
+	return !!(event->state & KVM_SDEI_EVENT_STATE_##field);		\
+}									\
+									\
+static inline void kvm_sdei_set_##func(struct kvm_sdei_event *event)	\
+{									\
+	event->state |= KVM_SDEI_EVENT_STATE_##field;			\
+}									\
+									\
+static inline void kvm_sdei_clear_##func(struct kvm_sdei_event *event)	\
+{									\
+	event->state &= ~KVM_SDEI_EVENT_STATE_##field;			\
+}
+
+KVM_SDEI_REGISTERED_EVENT_FUNC(registered, REGISTERED)
+KVM_SDEI_REGISTERED_EVENT_FUNC(enabled, ENABLED)
+KVM_SDEI_REGISTERED_EVENT_FUNC(unregister_pending, UNREGISTER_PENDING)
+
+/* APIs */
+int kvm_sdei_call(struct kvm_vcpu *vcpu);
+void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu);
+void kvm_sdei_destroy_vcpu(struct kvm_vcpu *vcpu);
+
+#endif /* __ARM64_KVM_SDEI_H__ */
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 261644b1a6bb..d6ced92ae3f0 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -14,7 +14,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
 	 inject_fault.o va_layout.o handle_exit.o \
 	 guest.o debug.o reset.o sys_regs.o \
 	 vgic-sys-reg-v3.o fpsimd.o pmu.o pkvm.o \
-	 arch_timer.o trng.o vmid.o \
+	 arch_timer.o trng.o vmid.o sdei.o \
 	 vgic/vgic.o vgic/vgic-init.o \
 	 vgic/vgic-irqfd.o vgic/vgic-v2.o \
 	 vgic/vgic-v3.o vgic/vgic-v4.o \
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 523bc934fe2f..227c0e390571 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -38,6 +38,7 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_sdei.h>
 #include <asm/sections.h>
 
 #include <kvm/arm_hypercalls.h>
@@ -331,6 +332,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 
 	kvm_arm_pvtime_vcpu_init(&vcpu->arch);
 
+	kvm_sdei_create_vcpu(vcpu);
+
 	vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
 
 	err = kvm_vgic_vcpu_init(vcpu);
@@ -352,6 +355,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 	kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
 	kvm_timer_vcpu_terminate(vcpu);
 	kvm_pmu_vcpu_destroy(vcpu);
+	kvm_sdei_destroy_vcpu(vcpu);
 
 	kvm_arm_vcpu_destroy(vcpu);
 }
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index b659387d8919..6aa027a4cee8 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -5,6 +5,7 @@
 #include <linux/kvm_host.h>
 
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_sdei.h>
 
 #include <kvm/arm_hypercalls.h>
 #include <kvm/arm_psci.h>
@@ -93,6 +94,8 @@ static int kvm_hvc_standard(struct kvm_vcpu *vcpu, u32 func)
 	case PSCI_1_1_FN_SYSTEM_RESET2:
 	case PSCI_1_1_FN64_SYSTEM_RESET2:
 		return kvm_psci_call(vcpu);
+	case SDEI_1_0_FN_SDEI_VERSION ... SDEI_1_1_FN_SDEI_FEATURES:
+		return kvm_sdei_call(vcpu);
 	}
 
 	smccc_set_retval(vcpu, val, 0, 0, 0);
diff --git a/arch/arm64/kvm/sdei.c b/arch/arm64/kvm/sdei.c
new file mode 100644
index 000000000000..3507e33ec00e
--- /dev/null
+++ b/arch/arm64/kvm/sdei.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SDEI virtualization support.
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author(s): Gavin Shan <gshan@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/kvm_host.h>
+#include <linux/slab.h>
+#include <kvm/arm_hypercalls.h>
+#include <asm/kvm_sdei.h>
+
+static struct kvm_sdei_exposed_event exposed_events[] = {
+	{ .num      = SDEI_SW_SIGNALED_EVENT,
+	  .type     = SDEI_EVENT_TYPE_PRIVATE,
+	  .signaled = 1,
+	  .priority = SDEI_EVENT_PRIORITY_NORMAL,
+	},
+};
+
+#define kvm_sdei_for_each_exposed_event(event, idx)	\
+	for (idx = 0, event = &exposed_events[0];	\
+	     idx < ARRAY_SIZE(exposed_events);		\
+	     idx++, event++)
+
+int kvm_sdei_call(struct kvm_vcpu *vcpu)
+{
+	struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
+	u32 func = smccc_get_function(vcpu);
+	bool has_result = true;
+	unsigned long ret;
+
+	/*
+	 * We don't have return value for COMPLETE or COMPLETE_AND_RESUME
+	 * hypercalls. Otherwise, the restored context will be corrupted.
+	 */
+	if (func == SDEI_1_0_FN_SDEI_EVENT_COMPLETE ||
+	    func == SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME)
+		has_result = false;
+
+	if (!vsdei) {
+		ret = SDEI_NOT_SUPPORTED;
+		goto out;
+	}
+
+	switch (func) {
+	default:
+		ret = SDEI_NOT_SUPPORTED;
+	}
+
+out:
+	if (has_result)
+		smccc_set_retval(vcpu, ret, 0, 0, 0);
+
+	return 1;
+}
+
+void kvm_sdei_create_vcpu(struct kvm_vcpu *vcpu)
+{
+	struct kvm_sdei_vcpu *vsdei;
+	struct kvm_sdei_exposed_event *exposed_event;
+	struct kvm_sdei_event *events;
+	unsigned int i;
+
+	vsdei = kzalloc(sizeof(*vsdei), GFP_KERNEL_ACCOUNT);
+	if (!vsdei)
+		return;
+
+	events = kcalloc(ARRAY_SIZE(exposed_events), sizeof(*events),
+			 GFP_KERNEL_ACCOUNT);
+	if (!events) {
+		kfree(vsdei);
+		return;
+	}
+
+	kvm_sdei_for_each_exposed_event(exposed_event, i)
+		events[i].exposed_event = exposed_event;
+
+	spin_lock_init(&vsdei->lock);
+	vsdei->events = events;
+	vsdei->masked = 1;
+	vcpu->arch.sdei = vsdei;
+}
+
+void kvm_sdei_destroy_vcpu(struct kvm_vcpu *vcpu)
+{
+	struct kvm_sdei_vcpu *vsdei = vcpu->arch.sdei;
+
+	if (!vsdei)
+		return;
+
+	vcpu->arch.sdei = NULL;
+	kfree(vsdei->events);
+	kfree(vsdei);
+}
diff --git a/include/uapi/linux/arm_sdei.h b/include/uapi/linux/arm_sdei.h
index af0630ba5437..572c77c59af6 100644
--- a/include/uapi/linux/arm_sdei.h
+++ b/include/uapi/linux/arm_sdei.h
@@ -22,8 +22,12 @@
 #define SDEI_1_0_FN_SDEI_PE_UNMASK			SDEI_1_0_FN(0x0C)
 #define SDEI_1_0_FN_SDEI_INTERRUPT_BIND			SDEI_1_0_FN(0x0D)
 #define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE		SDEI_1_0_FN(0x0E)
+#define SDEI_1_1_FN_SDEI_EVENT_SIGNAL			SDEI_1_0_FN(0x0F)
 #define SDEI_1_0_FN_SDEI_PRIVATE_RESET			SDEI_1_0_FN(0x11)
 #define SDEI_1_0_FN_SDEI_SHARED_RESET			SDEI_1_0_FN(0x12)
+#define SDEI_1_1_FN_SDEI_FEATURES			SDEI_1_0_FN(0x30)
+
+#define SDEI_SW_SIGNALED_EVENT	0
 
 #define SDEI_VERSION_MAJOR_SHIFT			48
 #define SDEI_VERSION_MAJOR_MASK				0x7fff
-- 
2.23.0

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

  parent reply	other threads:[~2022-04-03 15:40 UTC|newest]

Thread overview: 111+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-03 15:38 [PATCH v6 00/18] Support SDEI Virtualization Gavin Shan
2022-04-03 15:38 ` Gavin Shan
2022-04-03 15:38 ` [PATCH v6 01/18] KVM: arm64: Extend smccc_get_argx() Gavin Shan
2022-04-03 15:38   ` Gavin Shan
2022-04-03 15:38 ` [PATCH v6 02/18] KVM: arm64: Route hypercalls based on their owner Gavin Shan
2022-04-03 15:38   ` Gavin Shan
2022-04-21  8:19   ` Oliver Upton
2022-04-21  8:19     ` Oliver Upton
2022-04-22 12:20     ` Gavin Shan
2022-04-22 12:20       ` Gavin Shan
2022-04-22 17:59       ` Oliver Upton
2022-04-22 17:59         ` Oliver Upton
2022-04-23 12:48         ` Gavin Shan
2022-04-23 12:48           ` Gavin Shan
2022-04-03 15:38 ` Gavin Shan [this message]
2022-04-03 15:38   ` [PATCH v6 03/18] KVM: arm64: Add SDEI virtualization infrastructure Gavin Shan
2022-04-22 21:48   ` Oliver Upton
2022-04-22 21:48     ` Oliver Upton
2022-04-23 14:18     ` Gavin Shan
2022-04-23 14:18       ` Gavin Shan
2022-04-23 18:43       ` Oliver Upton
2022-04-23 18:43         ` Oliver Upton
2022-04-24  3:00         ` Gavin Shan
2022-04-24  3:00           ` Gavin Shan
2022-04-28 20:28           ` Oliver Upton
2022-04-28 20:28             ` Oliver Upton
2022-04-30 11:38             ` Gavin Shan
2022-04-30 11:38               ` Gavin Shan
2022-04-30 14:16               ` Oliver Upton
2022-04-30 14:16                 ` Oliver Upton
2022-05-02  2:35                 ` Gavin Shan
2022-05-02  2:35                   ` Gavin Shan
2022-05-02  3:40                   ` Oliver Upton
2022-05-02  3:40                     ` Oliver Upton
2022-05-02  7:25                     ` Gavin Shan
2022-05-02  7:25                       ` Gavin Shan
2022-05-02  7:57                       ` Oliver Upton
2022-05-02  7:57                         ` Oliver Upton
2022-05-02  8:23                         ` Gavin Shan
2022-05-02  8:23                           ` Gavin Shan
2022-04-03 15:38 ` [PATCH v6 04/18] KVM: arm64: Support SDEI_EVENT_REGISTER hypercall Gavin Shan
2022-04-03 15:38   ` Gavin Shan
2022-04-30 14:54   ` Oliver Upton
2022-04-30 14:54     ` Oliver Upton
2022-05-02  2:55     ` Gavin Shan
2022-05-02  2:55       ` Gavin Shan
2022-05-02  3:43       ` Oliver Upton
2022-05-02  3:43         ` Oliver Upton
2022-05-02  7:28         ` Gavin Shan
2022-05-02  7:28           ` Gavin Shan
2022-04-03 15:38 ` [PATCH v6 05/18] KVM: arm64: Support SDEI_EVENT_{ENABLE, DISABLE} Gavin Shan
2022-04-03 15:38   ` Gavin Shan
2022-04-03 15:38 ` [PATCH v6 06/18] KVM: arm64: Support SDEI_EVENT_CONTEXT hypercall Gavin Shan
2022-04-03 15:38   ` Gavin Shan
2022-04-30 15:03   ` Oliver Upton
2022-04-30 15:03     ` Oliver Upton
2022-05-02  2:57     ` Gavin Shan
2022-05-02  2:57       ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 07/18] KVM: arm64: Support SDEI_EVENT_UNREGISTER hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 08/18] KVM: arm64: Support SDEI_EVENT_STATUS hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 09/18] KVM: arm64: Support SDEI_EVENT_GET_INFO hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 10/18] KVM: arm64: Support SDEI_PE_{MASK, UNMASK} hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-04 10:26   ` [PATCH] KVM: arm64: fix returnvar.cocci warnings kernel test robot
2022-04-04 10:26     ` kernel test robot
2022-04-04 10:54     ` Gavin Shan
2022-04-04 10:54       ` Gavin Shan
2022-04-04 10:54       ` Gavin Shan
2022-04-04 10:29   ` [PATCH v6 10/18] KVM: arm64: Support SDEI_PE_{MASK, UNMASK} hypercall kernel test robot
2022-04-04 10:29     ` kernel test robot
2022-04-03 15:39 ` [PATCH v6 11/18] KVM: arm64: Support SDEI_{PRIVATE, SHARED}_RESET Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 12/18] KVM: arm64: Support SDEI event injection, delivery Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 13/18] KVM: arm64: Support SDEI_EVENT_{COMPLETE,COMPLETE_AND_RESUME} hypercall Gavin Shan
2022-04-03 15:39   ` [PATCH v6 13/18] KVM: arm64: Support SDEI_EVENT_{COMPLETE, COMPLETE_AND_RESUME} hypercall Gavin Shan
2022-05-01  6:50   ` [PATCH v6 13/18] KVM: arm64: Support SDEI_EVENT_{COMPLETE,COMPLETE_AND_RESUME} hypercall Oliver Upton
2022-05-01  6:50     ` Oliver Upton
2022-05-02  6:19     ` Gavin Shan
2022-05-02  6:19       ` Gavin Shan
2022-05-02  7:38       ` Oliver Upton
2022-05-02  7:38         ` Oliver Upton
2022-05-02  7:51         ` Gavin Shan
2022-05-02  7:51           ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 14/18] KVM: arm64: Support SDEI_EVENT_SIGNAL hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-30 21:32   ` Oliver Upton
2022-04-30 21:32     ` Oliver Upton
2022-05-02  3:04     ` Gavin Shan
2022-05-02  3:04       ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 15/18] KVM: arm64: Support SDEI_FEATURES hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-05-01  6:55   ` Oliver Upton
2022-05-01  6:55     ` Oliver Upton
2022-05-02  3:05     ` Gavin Shan
2022-05-02  3:05       ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 16/18] KVM: arm64: Support SDEI_VERSION hypercall Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 17/18] KVM: arm64: Expose SDEI capability Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:39 ` [PATCH v6 18/18] KVM: selftests: Add SDEI test case Gavin Shan
2022-04-03 15:39   ` Gavin Shan
2022-04-03 15:47 ` [PATCH v6 00/18] Support SDEI Virtualization Gavin Shan
2022-04-03 15:47   ` Gavin Shan
2022-04-04  6:09   ` Oliver Upton
2022-04-04  6:09     ` Oliver Upton
2022-04-04 10:53     ` Gavin Shan
2022-04-04 10:53       ` Gavin Shan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220403153911.12332-4-gshan@redhat.com \
    --to=gshan@redhat.com \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=eauger@redhat.com \
    --cc=james.morse@arm.com \
    --cc=kvmarm@lists.cs.columbia.edu \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=maz@kernel.org \
    --cc=oupton@google.com \
    --cc=pbonzini@redhat.com \
    --cc=shan.gavin@gmail.com \
    --cc=shannon.zhaosl@gmail.com \
    --cc=vkuznets@redhat.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.