Skip to content

Commit b7cfc0b

Browse files
kelleymhSasha Levin
authored andcommitted
arm64: hyperv: Add interrupt handlers for VMbus and stimer
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 <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 6dce7bc commit b7cfc0b

File tree

3 files changed

+204
-1
lines changed

3 files changed

+204
-1
lines changed

arch/arm64/hyperv/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# SPDX-License-Identifier: GPL-2.0
2-
obj-y := hv_core.o
2+
obj-y := hv_core.o mshyperv.o

arch/arm64/hyperv/mshyperv.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Core routines for interacting with Microsoft's Hyper-V hypervisor,
5+
* including setting up VMbus and STIMER interrupts, and handling
6+
* crashes and kexecs. These interactions are through a set of
7+
* static "handler" variables set by the architecture independent
8+
* VMbus and STIMER drivers.
9+
*
10+
* Copyright (C) 2019, Microsoft, Inc.
11+
*
12+
* Author : Michael Kelley <[email protected]>
13+
*/
14+
15+
#include <linux/types.h>
16+
#include <linux/export.h>
17+
#include <linux/interrupt.h>
18+
#include <linux/kexec.h>
19+
#include <linux/acpi.h>
20+
#include <linux/ptrace.h>
21+
#include <asm/hyperv-tlfs.h>
22+
#include <asm/mshyperv.h>
23+
24+
static void (*vmbus_handler)(void);
25+
static void (*hv_stimer0_handler)(void);
26+
27+
static int vmbus_irq;
28+
static long __percpu *vmbus_evt;
29+
static long __percpu *stimer0_evt;
30+
31+
irqreturn_t hyperv_vector_handler(int irq, void *dev_id)
32+
{
33+
vmbus_handler();
34+
return IRQ_HANDLED;
35+
}
36+
37+
/* Must be done just once */
38+
int hv_setup_vmbus_irq(int irq, void (*handler)(void))
39+
{
40+
int result;
41+
42+
vmbus_handler = handler;
43+
44+
vmbus_evt = alloc_percpu(long);
45+
result = request_percpu_irq(irq, hyperv_vector_handler,
46+
"Hyper-V VMbus", vmbus_evt);
47+
if (result) {
48+
pr_err("Can't request Hyper-V VMBus IRQ %d. Error %d",
49+
irq, result);
50+
free_percpu(vmbus_evt);
51+
return result;
52+
}
53+
54+
vmbus_irq = irq;
55+
return 0;
56+
}
57+
EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
58+
59+
/* Must be done just once */
60+
void hv_remove_vmbus_irq(void)
61+
{
62+
if (vmbus_irq) {
63+
free_percpu_irq(vmbus_irq, vmbus_evt);
64+
free_percpu(vmbus_evt);
65+
}
66+
}
67+
EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
68+
69+
/* Must be done by each CPU */
70+
void hv_enable_vmbus_irq(void)
71+
{
72+
enable_percpu_irq(vmbus_irq, 0);
73+
}
74+
EXPORT_SYMBOL_GPL(hv_enable_vmbus_irq);
75+
76+
/* Must be done by each CPU */
77+
void hv_disable_vmbus_irq(void)
78+
{
79+
disable_percpu_irq(vmbus_irq);
80+
}
81+
EXPORT_SYMBOL_GPL(hv_disable_vmbus_irq);
82+
83+
/* Routines to do per-architecture handling of STIMER0 when in Direct Mode */
84+
85+
static irqreturn_t hv_stimer0_vector_handler(int irq, void *dev_id)
86+
{
87+
if (hv_stimer0_handler)
88+
hv_stimer0_handler();
89+
return IRQ_HANDLED;
90+
}
91+
92+
int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void))
93+
{
94+
int localirq;
95+
int result;
96+
97+
localirq = acpi_register_gsi(NULL, HV_STIMER0_INTID,
98+
ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH);
99+
if (localirq <= 0) {
100+
pr_err("Can't register Hyper-V stimer0 GSI. Error %d",
101+
localirq);
102+
*irq = 0;
103+
return -1;
104+
}
105+
stimer0_evt = alloc_percpu(long);
106+
result = request_percpu_irq(localirq, hv_stimer0_vector_handler,
107+
"Hyper-V stimer0", stimer0_evt);
108+
if (result) {
109+
pr_err("Can't request Hyper-V stimer0 IRQ %d. Error %d",
110+
localirq, result);
111+
free_percpu(stimer0_evt);
112+
acpi_unregister_gsi(localirq);
113+
*irq = 0;
114+
return result;
115+
}
116+
117+
hv_stimer0_handler = handler;
118+
*vector = HV_STIMER0_INTID;
119+
*irq = localirq;
120+
return 0;
121+
}
122+
EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
123+
124+
void hv_remove_stimer0_irq(int irq)
125+
{
126+
hv_stimer0_handler = NULL;
127+
if (irq) {
128+
free_percpu_irq(irq, stimer0_evt);
129+
free_percpu(stimer0_evt);
130+
acpi_unregister_gsi(irq);
131+
}
132+
}
133+
EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);

arch/arm64/include/asm/mshyperv.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/clocksource.h>
2424
#include <linux/irq.h>
2525
#include <linux/irqdesc.h>
26+
#include <linux/sched_clock.h>
2627
#include <linux/arm-smccc.h>
2728
#include <asm/hyperv-tlfs.h>
2829

@@ -80,6 +81,75 @@ static inline void hv_set_synint_state(u32 sint_num, u64 val)
8081
(val = hv_get_vpreg(HV_REGISTER_SINT0 + sint_num))
8182

8283

84+
/*
85+
* Define the INTID used by STIMER0 Direct Mode interrupts. This
86+
* value can't come from ACPI tables because it is needed before
87+
* the Linux ACPI subsystem is initialized.
88+
*/
89+
#define HV_STIMER0_INTID 31
90+
91+
/*
92+
* Use the Hyper-V provided stimer0 as the timer that is made
93+
* available to the architecture independent Hyper-V drivers.
94+
*/
95+
static inline void hv_init_timer(u32 timer, u64 tick)
96+
{
97+
hv_set_vpreg(HV_REGISTER_STIMER0_COUNT + (2*timer), tick);
98+
}
99+
100+
static inline void hv_init_timer_config(u32 timer, u64 val)
101+
{
102+
hv_set_vpreg(HV_REGISTER_STIMER0_CONFIG + (2*timer), val);
103+
}
104+
105+
#define hv_get_time_ref_count(val) \
106+
(val = hv_get_vpreg(HV_REGISTER_TIME_REFCOUNT))
107+
#define hv_get_reference_tsc(val) \
108+
(val = hv_get_vpreg(HV_REGISTER_REFERENCE_TSC))
109+
110+
static inline void hv_set_reference_tsc(u64 val)
111+
{
112+
hv_set_vpreg(HV_REGISTER_REFERENCE_TSC, val);
113+
}
114+
115+
#define hv_set_clocksource_vdso(val) \
116+
((val).vdso_clock_mode = VDSO_CLOCKMODE_NONE)
117+
118+
static inline void hv_enable_vdso_clocksource(void) {}
119+
120+
static inline void hv_enable_stimer0_percpu_irq(int irq)
121+
{
122+
enable_percpu_irq(irq, 0);
123+
}
124+
125+
static inline void hv_disable_stimer0_percpu_irq(int irq)
126+
{
127+
disable_percpu_irq(irq);
128+
}
129+
130+
static inline u64 hv_get_raw_timer(void)
131+
{
132+
return arch_timer_read_counter();
133+
}
134+
135+
static inline void hv_setup_sched_clock(void *sched_clock)
136+
{
137+
/*
138+
* The Hyper-V sched clock read function returns nanoseconds,
139+
* not the normal 100ns units of the Hyper-V synthetic clock,
140+
* so specify 1 GHz here as the rate.
141+
*/
142+
sched_clock_register(sched_clock, 64, NSEC_PER_SEC);
143+
}
144+
145+
extern int vmbus_interrupt;
146+
147+
static inline int hv_get_vector(void)
148+
{
149+
return vmbus_interrupt;
150+
}
151+
152+
83153
/* SMCCC hypercall parameters */
84154
#define HV_SMCCC_FUNC_NUMBER 1
85155
#define HV_FUNC_ID ARM_SMCCC_CALL_VAL( \

0 commit comments

Comments
 (0)