[PATCH v7 05/10] arm64: hyperv: Add interrupt handlers for VMbus and stimer

Michael Kelley mikelley at microsoft.com
Mon Aug 24 12:46:18 EDT 2020


Add ARM64-specific code to set up and handle the interrupts
generated by Hyper-V for VMbus messages and for stimer expiration.

This code is architecture dependent and is mostly driven by
architecture independent code in the VMbus driver and the
Hyper-V timer clocksource driver.

This code is built only when CONFIG_HYPERV is enabled.

Signed-off-by: Michael Kelley <mikelley at microsoft.com>
---
 arch/arm64/hyperv/Makefile        |   2 +-
 arch/arm64/hyperv/mshyperv.c      | 133 ++++++++++++++++++++++++++++++++++++++
 arch/arm64/include/asm/mshyperv.h |  70 ++++++++++++++++++++
 3 files changed, 204 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/hyperv/mshyperv.c

diff --git a/arch/arm64/hyperv/Makefile b/arch/arm64/hyperv/Makefile
index 1697d30..87c31c0 100644
--- a/arch/arm64/hyperv/Makefile
+++ b/arch/arm64/hyperv/Makefile
@@ -1,2 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-y		:= hv_core.o
+obj-y		:= hv_core.o mshyperv.o
diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
new file mode 100644
index 0000000..be2cd2f
--- /dev/null
+++ b/arch/arm64/hyperv/mshyperv.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Core routines for interacting with Microsoft's Hyper-V hypervisor,
+ * including setting up VMbus and STIMER interrupts, and handling
+ * crashes and kexecs. These interactions are through a set of
+ * static "handler" variables set by the architecture independent
+ * VMbus and STIMER drivers.
+ *
+ * Copyright (C) 2019, Microsoft, Inc.
+ *
+ * Author : Michael Kelley <mikelley at microsoft.com>
+ */
+
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/kexec.h>
+#include <linux/acpi.h>
+#include <linux/ptrace.h>
+#include <asm/hyperv-tlfs.h>
+#include <asm/mshyperv.h>
+
+static void (*vmbus_handler)(void);
+static void (*hv_stimer0_handler)(void);
+
+static int vmbus_irq;
+static long __percpu *vmbus_evt;
+static long __percpu *stimer0_evt;
+
+irqreturn_t hyperv_vector_handler(int irq, void *dev_id)
+{
+	vmbus_handler();
+	return IRQ_HANDLED;
+}
+
+/* Must be done just once */
+int hv_setup_vmbus_irq(int irq, void (*handler)(void))
+{
+	int result;
+
+	vmbus_handler = handler;
+
+	vmbus_evt = alloc_percpu(long);
+	result = request_percpu_irq(irq, hyperv_vector_handler,
+			"Hyper-V VMbus", vmbus_evt);
+	if (result) {
+		pr_err("Can't request Hyper-V VMBus IRQ %d. Error %d",
+			irq, result);
+		free_percpu(vmbus_evt);
+		return result;
+	}
+
+	vmbus_irq = irq;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
+
+/* Must be done just once */
+void hv_remove_vmbus_irq(void)
+{
+	if (vmbus_irq) {
+		free_percpu_irq(vmbus_irq, vmbus_evt);
+		free_percpu(vmbus_evt);
+	}
+}
+EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
+
+/* Must be done by each CPU */
+void hv_enable_vmbus_irq(void)
+{
+	enable_percpu_irq(vmbus_irq, 0);
+}
+EXPORT_SYMBOL_GPL(hv_enable_vmbus_irq);
+
+/* Must be done by each CPU */
+void hv_disable_vmbus_irq(void)
+{
+	disable_percpu_irq(vmbus_irq);
+}
+EXPORT_SYMBOL_GPL(hv_disable_vmbus_irq);
+
+/* Routines to do per-architecture handling of STIMER0 when in Direct Mode */
+
+static irqreturn_t hv_stimer0_vector_handler(int irq, void *dev_id)
+{
+	if (hv_stimer0_handler)
+		hv_stimer0_handler();
+	return IRQ_HANDLED;
+}
+
+int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void))
+{
+	int localirq;
+	int result;
+
+	localirq = acpi_register_gsi(NULL, HV_STIMER0_INTID,
+			ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH);
+	if (localirq <= 0) {
+		pr_err("Can't register Hyper-V stimer0 GSI. Error %d",
+			localirq);
+		*irq = 0;
+		return -1;
+	}
+	stimer0_evt = alloc_percpu(long);
+	result = request_percpu_irq(localirq, hv_stimer0_vector_handler,
+					 "Hyper-V stimer0", stimer0_evt);
+	if (result) {
+		pr_err("Can't request Hyper-V stimer0 IRQ %d. Error %d",
+			localirq, result);
+		free_percpu(stimer0_evt);
+		acpi_unregister_gsi(localirq);
+		*irq = 0;
+		return result;
+	}
+
+	hv_stimer0_handler = handler;
+	*vector = HV_STIMER0_INTID;
+	*irq = localirq;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
+
+void hv_remove_stimer0_irq(int irq)
+{
+	hv_stimer0_handler = NULL;
+	if (irq) {
+		free_percpu_irq(irq, stimer0_evt);
+		free_percpu(stimer0_evt);
+		acpi_unregister_gsi(irq);
+	}
+}
+EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
index b17d4a1..2ea64e54 100644
--- a/arch/arm64/include/asm/mshyperv.h
+++ b/arch/arm64/include/asm/mshyperv.h
@@ -23,6 +23,7 @@
 #include <linux/clocksource.h>
 #include <linux/irq.h>
 #include <linux/irqdesc.h>
+#include <linux/sched_clock.h>
 #include <linux/arm-smccc.h>
 #include <asm/hyperv-tlfs.h>
 
@@ -80,6 +81,75 @@ static inline void hv_set_synint_state(u32 sint_num, u64 val)
 		(val = hv_get_vpreg(HV_REGISTER_SINT0 + sint_num))
 
 
+/*
+ * Define the INTID used by STIMER0 Direct Mode interrupts.  This
+ * value can't come from ACPI tables because it is needed before
+ * the Linux ACPI subsystem is initialized.
+ */
+#define	HV_STIMER0_INTID	31
+
+/*
+ * Use the Hyper-V provided stimer0 as the timer that is made
+ * available to the architecture independent Hyper-V drivers.
+ */
+static inline void hv_init_timer(u32 timer, u64 tick)
+{
+	hv_set_vpreg(HV_REGISTER_STIMER0_COUNT + (2*timer), tick);
+}
+
+static inline void hv_init_timer_config(u32 timer, u64 val)
+{
+	hv_set_vpreg(HV_REGISTER_STIMER0_CONFIG + (2*timer), val);
+}
+
+#define hv_get_time_ref_count(val) \
+		(val = hv_get_vpreg(HV_REGISTER_TIME_REFCOUNT))
+#define hv_get_reference_tsc(val) \
+		(val = hv_get_vpreg(HV_REGISTER_REFERENCE_TSC))
+
+static inline void hv_set_reference_tsc(u64 val)
+{
+	hv_set_vpreg(HV_REGISTER_REFERENCE_TSC, val);
+}
+
+#define hv_set_clocksource_vdso(val) \
+		((val).vdso_clock_mode = VDSO_CLOCKMODE_NONE)
+
+static inline void hv_enable_vdso_clocksource(void) {}
+
+static inline void hv_enable_stimer0_percpu_irq(int irq)
+{
+	enable_percpu_irq(irq, 0);
+}
+
+static inline void hv_disable_stimer0_percpu_irq(int irq)
+{
+	disable_percpu_irq(irq);
+}
+
+static inline u64 hv_get_raw_timer(void)
+{
+	return arch_timer_read_counter();
+}
+
+static inline void hv_setup_sched_clock(void *sched_clock)
+{
+	/*
+	 * The Hyper-V sched clock read function returns nanoseconds,
+	 * not the normal 100ns units of the Hyper-V synthetic clock,
+	 * so specify 1 GHz here as the rate.
+	 */
+	sched_clock_register(sched_clock, 64, NSEC_PER_SEC);
+}
+
+extern int vmbus_interrupt;
+
+static inline int hv_get_vector(void)
+{
+	return vmbus_interrupt;
+}
+
+
 /* SMCCC hypercall parameters */
 #define HV_SMCCC_FUNC_NUMBER	1
 #define HV_FUNC_ID	ARM_SMCCC_CALL_VAL(			\
-- 
1.8.3.1




More information about the linux-arm-kernel mailing list