Skip to content

Commit faff440

Browse files
Tianyu Lanliuw
authored andcommitted
x86/hyperv: Add Write/Read MSR registers via ghcb page
Hyperv provides GHCB protocol to write Synthetic Interrupt Controller MSR registers in Isolation VM with AMD SEV SNP and these registers are emulated by hypervisor directly. Hyperv requires to write SINTx MSR registers twice. First writes MSR via GHCB page to communicate with hypervisor and then writes wrmsr instruction to talk with paravisor which runs in VMPL0. Guest OS ID MSR also needs to be set via GHCB page. Reviewed-by: Michael Kelley <[email protected]> Signed-off-by: Tianyu Lan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Wei Liu <[email protected]>
1 parent d4dccf3 commit faff440

File tree

6 files changed

+232
-59
lines changed

6 files changed

+232
-59
lines changed

arch/x86/hyperv/hv_init.c

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ EXPORT_SYMBOL_GPL(hv_current_partition_id);
3737
void *hv_hypercall_pg;
3838
EXPORT_SYMBOL_GPL(hv_hypercall_pg);
3939

40-
void __percpu **hv_ghcb_pg;
40+
union hv_ghcb __percpu **hv_ghcb_pg;
4141

4242
/* Storage to save the hypercall page temporarily for hibernation */
4343
static void *hv_hypercall_pg_saved;
@@ -406,7 +406,7 @@ void __init hyperv_init(void)
406406
}
407407

408408
if (hv_isolation_type_snp()) {
409-
hv_ghcb_pg = alloc_percpu(void *);
409+
hv_ghcb_pg = alloc_percpu(union hv_ghcb *);
410410
if (!hv_ghcb_pg)
411411
goto free_vp_assist_page;
412412
}
@@ -424,6 +424,9 @@ void __init hyperv_init(void)
424424
guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0);
425425
wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
426426

427+
/* Hyper-V requires to write guest os id via ghcb in SNP IVM. */
428+
hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
429+
427430
hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START,
428431
VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_ROX,
429432
VM_FLUSH_RESET_PERMS, NUMA_NO_NODE,
@@ -501,6 +504,7 @@ void __init hyperv_init(void)
501504

502505
clean_guest_os_id:
503506
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
507+
hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
504508
cpuhp_remove_state(cpuhp);
505509
free_ghcb_page:
506510
free_percpu(hv_ghcb_pg);
@@ -522,6 +526,7 @@ void hyperv_cleanup(void)
522526

523527
/* Reset our OS id */
524528
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
529+
hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
525530

526531
/*
527532
* Reset hypercall page reference before reset the page,
@@ -592,30 +597,3 @@ bool hv_is_hyperv_initialized(void)
592597
return hypercall_msr.enable;
593598
}
594599
EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
595-
596-
enum hv_isolation_type hv_get_isolation_type(void)
597-
{
598-
if (!(ms_hyperv.priv_high & HV_ISOLATION))
599-
return HV_ISOLATION_TYPE_NONE;
600-
return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
601-
}
602-
EXPORT_SYMBOL_GPL(hv_get_isolation_type);
603-
604-
bool hv_is_isolation_supported(void)
605-
{
606-
if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
607-
return false;
608-
609-
if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
610-
return false;
611-
612-
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
613-
}
614-
615-
DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
616-
617-
bool hv_isolation_type_snp(void)
618-
{
619-
return static_branch_unlikely(&isolation_type_snp);
620-
}
621-
EXPORT_SYMBOL_GPL(hv_isolation_type_snp);

arch/x86/hyperv/ivm.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,123 @@
66
* Tianyu Lan <[email protected]>
77
*/
88

9+
#include <linux/types.h>
10+
#include <linux/bitfield.h>
911
#include <linux/hyperv.h>
1012
#include <linux/types.h>
1113
#include <linux/bitfield.h>
1214
#include <linux/slab.h>
15+
#include <asm/svm.h>
16+
#include <asm/sev.h>
1317
#include <asm/io.h>
1418
#include <asm/mshyperv.h>
19+
#include <asm/hypervisor.h>
20+
21+
#ifdef CONFIG_AMD_MEM_ENCRYPT
22+
union hv_ghcb {
23+
struct ghcb ghcb;
24+
} __packed __aligned(HV_HYP_PAGE_SIZE);
25+
26+
void hv_ghcb_msr_write(u64 msr, u64 value)
27+
{
28+
union hv_ghcb *hv_ghcb;
29+
void **ghcb_base;
30+
unsigned long flags;
31+
struct es_em_ctxt ctxt;
32+
33+
if (!hv_ghcb_pg)
34+
return;
35+
36+
WARN_ON(in_nmi());
37+
38+
local_irq_save(flags);
39+
ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
40+
hv_ghcb = (union hv_ghcb *)*ghcb_base;
41+
if (!hv_ghcb) {
42+
local_irq_restore(flags);
43+
return;
44+
}
45+
46+
ghcb_set_rcx(&hv_ghcb->ghcb, msr);
47+
ghcb_set_rax(&hv_ghcb->ghcb, lower_32_bits(value));
48+
ghcb_set_rdx(&hv_ghcb->ghcb, upper_32_bits(value));
49+
50+
if (sev_es_ghcb_hv_call(&hv_ghcb->ghcb, false, &ctxt,
51+
SVM_EXIT_MSR, 1, 0))
52+
pr_warn("Fail to write msr via ghcb %llx.\n", msr);
53+
54+
local_irq_restore(flags);
55+
}
56+
EXPORT_SYMBOL_GPL(hv_ghcb_msr_write);
57+
58+
void hv_ghcb_msr_read(u64 msr, u64 *value)
59+
{
60+
union hv_ghcb *hv_ghcb;
61+
void **ghcb_base;
62+
unsigned long flags;
63+
struct es_em_ctxt ctxt;
64+
65+
/* Check size of union hv_ghcb here. */
66+
BUILD_BUG_ON(sizeof(union hv_ghcb) != HV_HYP_PAGE_SIZE);
67+
68+
if (!hv_ghcb_pg)
69+
return;
70+
71+
WARN_ON(in_nmi());
72+
73+
local_irq_save(flags);
74+
ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
75+
hv_ghcb = (union hv_ghcb *)*ghcb_base;
76+
if (!hv_ghcb) {
77+
local_irq_restore(flags);
78+
return;
79+
}
80+
81+
ghcb_set_rcx(&hv_ghcb->ghcb, msr);
82+
if (sev_es_ghcb_hv_call(&hv_ghcb->ghcb, false, &ctxt,
83+
SVM_EXIT_MSR, 0, 0))
84+
pr_warn("Fail to read msr via ghcb %llx.\n", msr);
85+
else
86+
*value = (u64)lower_32_bits(hv_ghcb->ghcb.save.rax)
87+
| ((u64)lower_32_bits(hv_ghcb->ghcb.save.rdx) << 32);
88+
local_irq_restore(flags);
89+
}
90+
EXPORT_SYMBOL_GPL(hv_ghcb_msr_read);
91+
#endif
92+
93+
enum hv_isolation_type hv_get_isolation_type(void)
94+
{
95+
if (!(ms_hyperv.priv_high & HV_ISOLATION))
96+
return HV_ISOLATION_TYPE_NONE;
97+
return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
98+
}
99+
EXPORT_SYMBOL_GPL(hv_get_isolation_type);
100+
101+
/*
102+
* hv_is_isolation_supported - Check system runs in the Hyper-V
103+
* isolation VM.
104+
*/
105+
bool hv_is_isolation_supported(void)
106+
{
107+
if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
108+
return false;
109+
110+
if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
111+
return false;
112+
113+
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
114+
}
115+
116+
DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
117+
118+
/*
119+
* hv_isolation_type_snp - Check system runs in the AMD SEV-SNP based
120+
* isolation VM.
121+
*/
122+
bool hv_isolation_type_snp(void)
123+
{
124+
return static_branch_unlikely(&isolation_type_snp);
125+
}
15126

16127
/*
17128
* hv_mark_gpa_visibility - Set pages visible to host via hvcall.

arch/x86/include/asm/mshyperv.h

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,14 @@
1111
#include <asm/paravirt.h>
1212
#include <asm/mshyperv.h>
1313

14+
union hv_ghcb;
15+
1416
DECLARE_STATIC_KEY_FALSE(isolation_type_snp);
1517

1618
typedef int (*hyperv_fill_flush_list_func)(
1719
struct hv_guest_mapping_flush_list *flush,
1820
void *data);
1921

20-
static inline void hv_set_register(unsigned int reg, u64 value)
21-
{
22-
wrmsrl(reg, value);
23-
}
24-
25-
static inline u64 hv_get_register(unsigned int reg)
26-
{
27-
u64 value;
28-
29-
rdmsrl(reg, value);
30-
return value;
31-
}
32-
3322
#define hv_get_raw_timer() rdtsc_ordered()
3423

3524
void hyperv_vector_handler(struct pt_regs *regs);
@@ -41,7 +30,7 @@ extern void *hv_hypercall_pg;
4130

4231
extern u64 hv_current_partition_id;
4332

44-
extern void __percpu **hv_ghcb_pg;
33+
extern union hv_ghcb __percpu **hv_ghcb_pg;
4534

4635
int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages);
4736
int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id);
@@ -193,6 +182,50 @@ int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector,
193182
struct hv_interrupt_entry *entry);
194183
int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
195184
int hv_set_mem_host_visibility(unsigned long addr, int numpages, bool visible);
185+
186+
#ifdef CONFIG_AMD_MEM_ENCRYPT
187+
void hv_ghcb_msr_write(u64 msr, u64 value);
188+
void hv_ghcb_msr_read(u64 msr, u64 *value);
189+
#else
190+
static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
191+
static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
192+
#endif
193+
194+
extern bool hv_isolation_type_snp(void);
195+
196+
static inline bool hv_is_synic_reg(unsigned int reg)
197+
{
198+
if ((reg >= HV_REGISTER_SCONTROL) &&
199+
(reg <= HV_REGISTER_SINT15))
200+
return true;
201+
return false;
202+
}
203+
204+
static inline u64 hv_get_register(unsigned int reg)
205+
{
206+
u64 value;
207+
208+
if (hv_is_synic_reg(reg) && hv_isolation_type_snp())
209+
hv_ghcb_msr_read(reg, &value);
210+
else
211+
rdmsrl(reg, value);
212+
return value;
213+
}
214+
215+
static inline void hv_set_register(unsigned int reg, u64 value)
216+
{
217+
if (hv_is_synic_reg(reg) && hv_isolation_type_snp()) {
218+
hv_ghcb_msr_write(reg, value);
219+
220+
/* Write proxy bit via wrmsl instruction */
221+
if (reg >= HV_REGISTER_SINT0 &&
222+
reg <= HV_REGISTER_SINT15)
223+
wrmsrl(reg, value | 1 << 20);
224+
} else {
225+
wrmsrl(reg, value);
226+
}
227+
}
228+
196229
#else /* CONFIG_HYPERV */
197230
static inline void hyperv_init(void) {}
198231
static inline void hyperv_setup_mmu_ops(void) {}
@@ -209,6 +242,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as,
209242
{
210243
return -1;
211244
}
245+
static inline void hv_set_register(unsigned int reg, u64 value) { }
246+
static inline u64 hv_get_register(unsigned int reg) { return 0; }
212247
static inline int hv_set_mem_host_visibility(unsigned long addr, int numpages,
213248
bool visible)
214249
{

0 commit comments

Comments
 (0)