x86/hyperv: Initialize GHCB page in Isolation VM

Hyperv exposes GHCB page via SEV ES GHCB MSR for SNP guest
to communicate with hypervisor. Map GHCB page for all
cpus to read/write MSR register and submit hvcall request
via ghcb page.

Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
Link: https://lore.kernel.org/r/20211025122116.264793-2-ltykernel@gmail.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
This commit is contained in:
Tianyu Lan 2021-10-25 08:21:06 -04:00 committed by Wei Liu
parent e82f2069b5
commit 0cc4f6d9f0
4 changed files with 74 additions and 7 deletions

View File

@ -20,6 +20,7 @@
#include <linux/kexec.h> #include <linux/kexec.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/io.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -36,12 +37,42 @@ EXPORT_SYMBOL_GPL(hv_current_partition_id);
void *hv_hypercall_pg; void *hv_hypercall_pg;
EXPORT_SYMBOL_GPL(hv_hypercall_pg); EXPORT_SYMBOL_GPL(hv_hypercall_pg);
void __percpu **hv_ghcb_pg;
/* Storage to save the hypercall page temporarily for hibernation */ /* Storage to save the hypercall page temporarily for hibernation */
static void *hv_hypercall_pg_saved; static void *hv_hypercall_pg_saved;
struct hv_vp_assist_page **hv_vp_assist_page; struct hv_vp_assist_page **hv_vp_assist_page;
EXPORT_SYMBOL_GPL(hv_vp_assist_page); EXPORT_SYMBOL_GPL(hv_vp_assist_page);
static int hyperv_init_ghcb(void)
{
u64 ghcb_gpa;
void *ghcb_va;
void **ghcb_base;
if (!hv_isolation_type_snp())
return 0;
if (!hv_ghcb_pg)
return -EINVAL;
/*
* GHCB page is allocated by paravisor. The address
* returned by MSR_AMD64_SEV_ES_GHCB is above shared
* memory boundary and map it here.
*/
rdmsrl(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
ghcb_va = memremap(ghcb_gpa, HV_HYP_PAGE_SIZE, MEMREMAP_WB);
if (!ghcb_va)
return -ENOMEM;
ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
*ghcb_base = ghcb_va;
return 0;
}
static int hv_cpu_init(unsigned int cpu) static int hv_cpu_init(unsigned int cpu)
{ {
union hv_vp_assist_msr_contents msr = { 0 }; union hv_vp_assist_msr_contents msr = { 0 };
@ -85,7 +116,7 @@ static int hv_cpu_init(unsigned int cpu)
} }
} }
return 0; return hyperv_init_ghcb();
} }
static void (*hv_reenlightenment_cb)(void); static void (*hv_reenlightenment_cb)(void);
@ -177,6 +208,14 @@ static int hv_cpu_die(unsigned int cpu)
{ {
struct hv_reenlightenment_control re_ctrl; struct hv_reenlightenment_control re_ctrl;
unsigned int new_cpu; unsigned int new_cpu;
void **ghcb_va;
if (hv_ghcb_pg) {
ghcb_va = (void **)this_cpu_ptr(hv_ghcb_pg);
if (*ghcb_va)
memunmap(*ghcb_va);
*ghcb_va = NULL;
}
hv_common_cpu_die(cpu); hv_common_cpu_die(cpu);
@ -366,10 +405,16 @@ void __init hyperv_init(void)
goto common_free; goto common_free;
} }
if (hv_isolation_type_snp()) {
hv_ghcb_pg = alloc_percpu(void *);
if (!hv_ghcb_pg)
goto free_vp_assist_page;
}
cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv_init:online", cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv_init:online",
hv_cpu_init, hv_cpu_die); hv_cpu_init, hv_cpu_die);
if (cpuhp < 0) if (cpuhp < 0)
goto free_vp_assist_page; goto free_ghcb_page;
/* /*
* Setup the hypercall page and enable hypercalls. * Setup the hypercall page and enable hypercalls.
@ -383,10 +428,8 @@ void __init hyperv_init(void)
VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_ROX, VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_ROX,
VM_FLUSH_RESET_PERMS, NUMA_NO_NODE, VM_FLUSH_RESET_PERMS, NUMA_NO_NODE,
__builtin_return_address(0)); __builtin_return_address(0));
if (hv_hypercall_pg == NULL) { if (hv_hypercall_pg == NULL)
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); goto clean_guest_os_id;
goto remove_cpuhp_state;
}
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
hypercall_msr.enable = 1; hypercall_msr.enable = 1;
@ -456,8 +499,11 @@ void __init hyperv_init(void)
hv_query_ext_cap(0); hv_query_ext_cap(0);
return; return;
remove_cpuhp_state: clean_guest_os_id:
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
cpuhp_remove_state(cpuhp); cpuhp_remove_state(cpuhp);
free_ghcb_page:
free_percpu(hv_ghcb_pg);
free_vp_assist_page: free_vp_assist_page:
kfree(hv_vp_assist_page); kfree(hv_vp_assist_page);
hv_vp_assist_page = NULL; hv_vp_assist_page = NULL;
@ -559,3 +605,11 @@ bool hv_is_isolation_supported(void)
{ {
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE; return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
} }
DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
bool hv_isolation_type_snp(void)
{
return static_branch_unlikely(&isolation_type_snp);
}
EXPORT_SYMBOL_GPL(hv_isolation_type_snp);

View File

@ -11,6 +11,8 @@
#include <asm/paravirt.h> #include <asm/paravirt.h>
#include <asm/mshyperv.h> #include <asm/mshyperv.h>
DECLARE_STATIC_KEY_FALSE(isolation_type_snp);
typedef int (*hyperv_fill_flush_list_func)( typedef int (*hyperv_fill_flush_list_func)(
struct hv_guest_mapping_flush_list *flush, struct hv_guest_mapping_flush_list *flush,
void *data); void *data);
@ -39,6 +41,8 @@ extern void *hv_hypercall_pg;
extern u64 hv_current_partition_id; extern u64 hv_current_partition_id;
extern void __percpu **hv_ghcb_pg;
int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages); int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages);
int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id);
int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags);

View File

@ -316,6 +316,9 @@ static void __init ms_hyperv_init_platform(void)
pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n", pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n",
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b); ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP)
static_branch_enable(&isolation_type_snp);
} }
if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) { if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {

View File

@ -254,12 +254,18 @@ bool hv_is_hyperv_initialized(void);
bool hv_is_hibernation_supported(void); bool hv_is_hibernation_supported(void);
enum hv_isolation_type hv_get_isolation_type(void); enum hv_isolation_type hv_get_isolation_type(void);
bool hv_is_isolation_supported(void); bool hv_is_isolation_supported(void);
bool hv_isolation_type_snp(void);
void hyperv_cleanup(void); void hyperv_cleanup(void);
bool hv_query_ext_cap(u64 cap_query); bool hv_query_ext_cap(u64 cap_query);
#else /* CONFIG_HYPERV */ #else /* CONFIG_HYPERV */
static inline bool hv_is_hyperv_initialized(void) { return false; } static inline bool hv_is_hyperv_initialized(void) { return false; }
static inline bool hv_is_hibernation_supported(void) { return false; } static inline bool hv_is_hibernation_supported(void) { return false; }
static inline void hyperv_cleanup(void) {} static inline void hyperv_cleanup(void) {}
static inline bool hv_is_isolation_supported(void) { return false; }
static inline enum hv_isolation_type hv_get_isolation_type(void)
{
return HV_ISOLATION_TYPE_NONE;
}
#endif /* CONFIG_HYPERV */ #endif /* CONFIG_HYPERV */
#endif #endif