Skip to content

Commit a7e6159

Browse files
kelleymhSasha Levin
authored andcommitted
arm64: hyperv: Add hypercall and register access functions
Add ARM64-specific code to make Hyper-V hypercalls and to access virtual processor synthetic registers via hypercalls. Hypercalls follow the SMC Calling Convention spec v1.1. 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 6385bba commit a7e6159

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8154,6 +8154,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
81548154
F: Documentation/ABI/stable/sysfs-bus-vmbus
81558155
F: Documentation/ABI/testing/debugfs-hyperv
81568156
F: Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
8157+
F: arch/arm64/hyperv
81578158
F: arch/arm64/include/asm/hyperv-tlfs.h
81588159
F: arch/arm64/include/asm/mshyperv.h
81598160
F: arch/x86/hyperv

arch/arm64/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ obj-y += kernel/ mm/
33
obj-$(CONFIG_NET) += net/
44
obj-$(CONFIG_KVM) += kvm/
55
obj-$(CONFIG_XEN) += xen/
6+
obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/
67
obj-$(CONFIG_CRYPTO) += crypto/

arch/arm64/hyperv/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
obj-y := hv_core.o

arch/arm64/hyperv/hv_core.c

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Initialization of the interface with Microsoft's Hyper-V hypervisor,
5+
* and various low level utility routines for interacting with Hyper-V.
6+
*
7+
* Copyright (C) 2019, Microsoft, Inc.
8+
*
9+
* Author : Michael Kelley <[email protected]>
10+
*/
11+
12+
13+
#include <linux/types.h>
14+
#include <linux/log2.h>
15+
#include <linux/version.h>
16+
#include <linux/export.h>
17+
#include <linux/mm.h>
18+
#include <linux/slab.h>
19+
#include <linux/hyperv.h>
20+
#include <linux/arm-smccc.h>
21+
#include <asm-generic/bug.h>
22+
#include <asm/hyperv-tlfs.h>
23+
#include <asm/mshyperv.h>
24+
25+
26+
/*
27+
* hv_do_hypercall- Invoke the specified hypercall
28+
*/
29+
u64 hv_do_hypercall(u64 control, void *input, void *output)
30+
{
31+
u64 input_address;
32+
u64 output_address;
33+
struct arm_smccc_res res;
34+
35+
input_address = input ? virt_to_phys(input) : 0;
36+
output_address = output ? virt_to_phys(output) : 0;
37+
38+
arm_smccc_1_1_hvc(HV_FUNC_ID, control,
39+
input_address, output_address, &res);
40+
return res.a0;
41+
}
42+
EXPORT_SYMBOL_GPL(hv_do_hypercall);
43+
44+
/*
45+
* hv_do_fast_hypercall8 -- Invoke the specified hypercall
46+
* with arguments in registers instead of physical memory.
47+
* Avoids the overhead of virt_to_phys for simple hypercalls.
48+
*/
49+
50+
u64 hv_do_fast_hypercall8(u16 code, u64 input)
51+
{
52+
u64 control;
53+
struct arm_smccc_res res;
54+
55+
control = (u64)code | HV_HYPERCALL_FAST_BIT;
56+
57+
arm_smccc_1_1_hvc(HV_FUNC_ID, control, input, &res);
58+
return res.a0;
59+
}
60+
EXPORT_SYMBOL_GPL(hv_do_fast_hypercall8);
61+
62+
63+
/*
64+
* Set a single VP register to a 64-bit value.
65+
*/
66+
void hv_set_vpreg(u32 msr, u64 value)
67+
{
68+
struct arm_smccc_res res;
69+
70+
arm_smccc_1_1_hvc(
71+
HV_FUNC_ID,
72+
HVCALL_SET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
73+
HV_HYPERCALL_REP_COMP_1,
74+
HV_PARTITION_ID_SELF,
75+
HV_VP_INDEX_SELF,
76+
msr,
77+
0,
78+
value,
79+
0,
80+
&res);
81+
82+
/*
83+
* Something is fundamentally broken in the hypervisor if
84+
* setting a VP register fails. There's really no way to
85+
* continue as a guest VM, so panic.
86+
*/
87+
BUG_ON((res.a0 & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
88+
}
89+
EXPORT_SYMBOL_GPL(hv_set_vpreg);
90+
91+
/*
92+
* Get the value of a single VP register. One version
93+
* returns just 64 bits and another returns the full 128 bits.
94+
* The two versions are separate to avoid complicating the
95+
* calling sequence for the more frequently used 64 bit version.
96+
*/
97+
98+
static void __hv_get_vpreg_128(u32 msr, struct hv_get_vp_registers_output *res)
99+
{
100+
struct hv_get_vp_registers_input *input;
101+
u64 status;
102+
103+
/*
104+
* Allocate a power of 2 size so alignment to that size is
105+
* guaranteed, since the hypercall input area must not cross
106+
* a page boundary.
107+
*/
108+
109+
input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
110+
sizeof(input->element[0])), GFP_ATOMIC);
111+
112+
input->header.partitionid = HV_PARTITION_ID_SELF;
113+
input->header.vpindex = HV_VP_INDEX_SELF;
114+
input->header.inputvtl = 0;
115+
input->element[0].name0 = msr;
116+
input->element[0].name1 = 0;
117+
118+
119+
status = hv_do_hypercall(
120+
HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_REP_COMP_1,
121+
input, res);
122+
123+
/*
124+
* Something is fundamentally broken in the hypervisor if
125+
* getting a VP register fails. There's really no way to
126+
* continue as a guest VM, so panic.
127+
*/
128+
BUG_ON((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
129+
130+
kfree(input);
131+
}
132+
133+
u64 hv_get_vpreg(u32 msr)
134+
{
135+
struct hv_get_vp_registers_output *output;
136+
u64 result;
137+
138+
/*
139+
* Allocate a power of 2 size so alignment to that size is
140+
* guaranteed, since the hypercall output area must not cross
141+
* a page boundary.
142+
*/
143+
output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
144+
145+
__hv_get_vpreg_128(msr, output);
146+
147+
result = output->as64.low;
148+
kfree(output);
149+
return result;
150+
}
151+
EXPORT_SYMBOL_GPL(hv_get_vpreg);
152+
153+
void hv_get_vpreg_128(u32 msr, struct hv_get_vp_registers_output *res)
154+
{
155+
struct hv_get_vp_registers_output *output;
156+
157+
/*
158+
* Allocate a power of 2 size so alignment to that size is
159+
* guaranteed, since the hypercall output area must not cross
160+
* a page boundary.
161+
*/
162+
output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
163+
164+
__hv_get_vpreg_128(msr, output);
165+
166+
res->as64.low = output->as64.low;
167+
res->as64.high = output->as64.high;
168+
kfree(output);
169+
}
170+
EXPORT_SYMBOL_GPL(hv_get_vpreg_128);

arch/arm64/include/asm/mshyperv.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
#include <linux/arm-smccc.h>
2727
#include <asm/hyperv-tlfs.h>
2828

29+
/*
30+
* Declare calls to get and set Hyper-V VP register values on ARM64, which
31+
* requires a hypercall.
32+
*/
33+
extern void hv_set_vpreg(u32 reg, u64 value);
34+
extern u64 hv_get_vpreg(u32 reg);
35+
extern void hv_get_vpreg_128(u32 reg, struct hv_get_vp_registers_output *result);
36+
2937
/* Access various Hyper-V synthetic registers */
3038
static inline void hv_set_simp(u64 val)
3139
{
@@ -71,6 +79,15 @@ static inline void hv_set_synint_state(u32 sint_num, u64 val)
7179
#define hv_get_synint_state(sint_num, val) \
7280
(val = hv_get_vpreg(HV_REGISTER_SINT0 + sint_num))
7381

82+
83+
/* SMCCC hypercall parameters */
84+
#define HV_SMCCC_FUNC_NUMBER 1
85+
#define HV_FUNC_ID ARM_SMCCC_CALL_VAL( \
86+
ARM_SMCCC_STD_CALL, \
87+
ARM_SMCCC_SMC_64, \
88+
ARM_SMCCC_OWNER_VENDOR_HYP, \
89+
HV_SMCCC_FUNC_NUMBER)
90+
7491
#include <asm-generic/mshyperv.h>
7592

7693
#endif

0 commit comments

Comments
 (0)