Skip to content

Commit 8db985f

Browse files
sunilmutkelsey-steele
authored andcommitted
Hyper-V: ARM64: Always use the Hyper-V hypercall interface
This patch forces the use of the Hyper-V hypercall interface, instead of the architectural SMCCC interface on ARM64 because not all versions of Windows support the SMCCC interface. All versions of Windows will support the Hyper-V hypercall interface, so this change should be both forward and backward compatible. Signed-off-by: Sunil Muthuswamy <[email protected]> [tyhicks: Forward ported to v5.15] Signed-off-by: Tyler Hicks <[email protected]> [kms: Forward ported to v6.6] Signed-off-by: Kelsey Steele <[email protected]>
1 parent ffc2532 commit 8db985f

File tree

4 files changed

+91
-33
lines changed

4 files changed

+91
-33
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 mshyperv.o
2+
obj-y := hv_core.o mshyperv.o hv_hvc.o

arch/arm64/hyperv/hv_core.c

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,13 @@
2323
*/
2424
u64 hv_do_hypercall(u64 control, void *input, void *output)
2525
{
26-
struct arm_smccc_res res;
2726
u64 input_address;
2827
u64 output_address;
2928

3029
input_address = input ? virt_to_phys(input) : 0;
3130
output_address = output ? virt_to_phys(output) : 0;
3231

33-
arm_smccc_1_1_hvc(HV_FUNC_ID, control,
34-
input_address, output_address, &res);
35-
return res.a0;
32+
return hv_do_hvc(control, input_address, output_address);
3633
}
3734
EXPORT_SYMBOL_GPL(hv_do_hypercall);
3835

@@ -41,43 +38,48 @@ EXPORT_SYMBOL_GPL(hv_do_hypercall);
4138
* with arguments in registers instead of physical memory.
4239
* Avoids the overhead of virt_to_phys for simple hypercalls.
4340
*/
44-
4541
u64 hv_do_fast_hypercall8(u16 code, u64 input)
4642
{
47-
struct arm_smccc_res res;
4843
u64 control;
4944

5045
control = (u64)code | HV_HYPERCALL_FAST_BIT;
51-
52-
arm_smccc_1_1_hvc(HV_FUNC_ID, control, input, &res);
53-
return res.a0;
46+
return hv_do_hvc(control, input);
5447
}
5548
EXPORT_SYMBOL_GPL(hv_do_fast_hypercall8);
5649

50+
union hv_hypercall_status {
51+
u64 as_uint64;
52+
struct {
53+
u16 status;
54+
u16 reserved;
55+
u16 reps_completed; /* Low 12 bits */
56+
u16 reserved2;
57+
};
58+
};
59+
5760
/*
5861
* Set a single VP register to a 64-bit value.
5962
*/
6063
void hv_set_vpreg(u32 msr, u64 value)
6164
{
62-
struct arm_smccc_res res;
65+
union hv_hypercall_status status;
6366

64-
arm_smccc_1_1_hvc(HV_FUNC_ID,
67+
status.as_uint64 = hv_do_hvc(
6568
HVCALL_SET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
6669
HV_HYPERCALL_REP_COMP_1,
6770
HV_PARTITION_ID_SELF,
6871
HV_VP_INDEX_SELF,
6972
msr,
7073
0,
7174
value,
72-
0,
73-
&res);
75+
0);
7476

7577
/*
7678
* Something is fundamentally broken in the hypervisor if
7779
* setting a VP register fails. There's really no way to
7880
* continue as a guest VM, so panic.
7981
*/
80-
BUG_ON(!hv_result_success(res.a0));
82+
BUG_ON(status.status != HV_STATUS_SUCCESS);
8183
}
8284
EXPORT_SYMBOL_GPL(hv_set_vpreg);
8385

@@ -90,31 +92,22 @@ EXPORT_SYMBOL_GPL(hv_set_vpreg);
9092

9193
void hv_get_vpreg_128(u32 msr, struct hv_get_vp_registers_output *result)
9294
{
93-
struct arm_smccc_1_2_regs args;
94-
struct arm_smccc_1_2_regs res;
95-
96-
args.a0 = HV_FUNC_ID;
97-
args.a1 = HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
98-
HV_HYPERCALL_REP_COMP_1;
99-
args.a2 = HV_PARTITION_ID_SELF;
100-
args.a3 = HV_VP_INDEX_SELF;
101-
args.a4 = msr;
95+
u64 status;
10296

103-
/*
104-
* Use the SMCCC 1.2 interface because the results are in registers
105-
* beyond X0-X3.
106-
*/
107-
arm_smccc_1_2_hvc(&args, &res);
97+
status = hv_do_hvc_fast_get(
98+
HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
99+
HV_HYPERCALL_REP_COMP_1,
100+
HV_PARTITION_ID_SELF,
101+
HV_VP_INDEX_SELF,
102+
msr,
103+
result);
108104

109105
/*
110106
* Something is fundamentally broken in the hypervisor if
111107
* getting a VP register fails. There's really no way to
112108
* continue as a guest VM, so panic.
113109
*/
114-
BUG_ON(!hv_result_success(res.a0));
115-
116-
result->as64.low = res.a6;
117-
result->as64.high = res.a7;
110+
BUG_ON((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
118111
}
119112
EXPORT_SYMBOL_GPL(hv_get_vpreg_128);
120113

arch/arm64/hyperv/hv_hvc.S

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
/*
4+
* Microsoft Hyper-V hypervisor invocation routines
5+
*
6+
* Copyright (C) 2018, Microsoft, Inc.
7+
*
8+
* Author : Michael Kelley <[email protected]>
9+
*
10+
* This program is free software; you can redistribute it and/or modify it
11+
* under the terms of the GNU General Public License version 2 as published
12+
* by the Free Software Foundation.
13+
*
14+
* This program is distributed in the hope that it will be useful, but
15+
* WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
17+
* NON INFRINGEMENT. See the GNU General Public License for more
18+
* details.
19+
*/
20+
21+
#include <linux/linkage.h>
22+
#include <asm/assembler.h>
23+
24+
.text
25+
/*
26+
* Do the HVC instruction. For Hyper-V the argument is always 1.
27+
* x0 contains the hypercall control value, while additional registers
28+
* vary depending on the hypercall, and whether the hypercall arguments
29+
* are in memory or in registers (a "fast" hypercall per the Hyper-V
30+
* TLFS). When the arguments are in memory x1 is the guest physical
31+
* address of the input arguments, and x2 is the guest physical
32+
* address of the output arguments. When the arguments are in
33+
* registers, the register values depends on the hypercall. Note
34+
* that this version cannot return any values in registers.
35+
*/
36+
SYM_FUNC_START(hv_do_hvc)
37+
hvc #1
38+
ret
39+
SYM_FUNC_END(hv_do_hvc)
40+
41+
/*
42+
* This variant of HVC invocation is for hv_get_vpreg and
43+
* hv_get_vpreg_128. The input parameters are passed in registers
44+
* along with a pointer in x4 to where the output result should
45+
* be stored. The output is returned in x15 and x16. x19 is used as
46+
* scratch space to avoid buildng a stack frame, as Hyper-V does
47+
* not preserve registers x0-x17.
48+
*/
49+
SYM_FUNC_START(hv_do_hvc_fast_get)
50+
/*
51+
* Stash away x19 register so that it can be used as a scratch
52+
* register and pop it at the end.
53+
*/
54+
str x19, [sp, #-16]!
55+
mov x19, x4
56+
hvc #1
57+
str x15,[x19]
58+
str x16,[x19,#8]
59+
ldr x19, [sp], #16
60+
ret
61+
SYM_FUNC_END(hv_do_hvc_fast_get)

arch/arm64/include/asm/mshyperv.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
#include <linux/arm-smccc.h>
2323
#include <asm/hyperv-tlfs.h>
2424

25+
extern u64 hv_do_hvc(u64 control, ...);
26+
extern u64 hv_do_hvc_fast_get(u64 control, u64 input1, u64 input2, u64 input3,
27+
struct hv_get_vp_registers_output *output);
28+
2529
/*
2630
* Declare calls to get and set Hyper-V VP register values on ARM64, which
2731
* requires a hypercall.

0 commit comments

Comments
 (0)