Secure AVIC hardware provides APIC backing page to aid the guest in limiting which interrupt vectors can be injected into the guest. Hyper-V introduces a new register HV_X64_REGISTER_SEV_GPA_PAGE to notify hypervisor with APIC backing page and call it in Secure AVIC driver.
Setting APIC backing page for APs takes place before allocating hyperv_pcpu_input_arg and so allocate hv_vp_early_input_arg to handle such case. Signed-off-by: Roman Kisel <[email protected]> Signed-off-by: Tianyu Lan <[email protected]> --- arch/x86/hyperv/hv_init.c | 24 +++++++++++++++++- arch/x86/hyperv/ivm.c | 38 ++++++++++++++++++++++++++++ arch/x86/include/asm/mshyperv.h | 2 ++ arch/x86/kernel/apic/x2apic_savic.c | 9 ++++++- include/hyperv/hvgdk_mini.h | 39 +++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index a38bb96c9f5e..3fa8e91cd03f 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -39,6 +39,7 @@ void *hv_hypercall_pg; EXPORT_SYMBOL_GPL(hv_hypercall_pg); +void *hv_vp_early_input_arg; union hv_ghcb * __percpu *hv_ghcb_pg; /* Storage to save the hypercall page temporarily for hibernation */ @@ -412,6 +413,7 @@ void __init hyperv_init(void) u64 guest_id; union hv_x64_msr_hypercall_contents hypercall_msr; int cpuhp; + int ret; if (x86_hyper_type != X86_HYPER_MS_HYPERV) return; @@ -419,6 +421,22 @@ void __init hyperv_init(void) if (hv_common_init()) return; + if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) { + hv_vp_early_input_arg = kcalloc(num_possible_cpus(), + PAGE_SIZE, + GFP_KERNEL); + if (hv_vp_early_input_arg) { + ret = set_memory_decrypted((u64)hv_vp_early_input_arg, + num_possible_cpus()); + if (ret) { + kfree(hv_vp_early_input_arg); + goto common_free; + } + } else { + goto common_free; + } + } + /* * The VP assist page is useless to a TDX guest: the only use we * would have for it is lazy EOI, which can not be used with TDX. @@ -433,7 +451,7 @@ void __init hyperv_init(void) ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED; if (!hv_isolation_type_tdx()) - goto common_free; + goto free_vp_early_input_arg; } if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) { @@ -591,6 +609,10 @@ void __init hyperv_init(void) free_vp_assist_page: kfree(hv_vp_assist_page); hv_vp_assist_page = NULL; +free_vp_early_input_arg: + set_memory_encrypted((u64)hv_vp_early_input_arg, num_possible_cpus()); + kfree(hv_vp_early_input_arg); + hv_vp_early_input_arg = NULL; common_free: hv_common_free(); } diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c index ade6c665c97e..e69dae57730c 100644 --- a/arch/x86/hyperv/ivm.c +++ b/arch/x86/hyperv/ivm.c @@ -291,6 +291,44 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa) free_page((unsigned long)vmsa); } +enum es_result hv_set_savic_backing_page(u64 gfn) +{ + u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_SET_VP_REGISTERS; + struct hv_set_vp_registers_input *input + = hv_vp_early_input_arg + smp_processor_id() * PAGE_SIZE; + union hv_x64_register_sev_gpa_page value; + unsigned long flags; + int retry = 5; + u64 ret; + + local_irq_save(flags); + + value.enabled = 1; + value.reserved = 0; + value.pagenumber = gfn; + + memset(input, 0, struct_size(input, element, 1)); + input->header.partitionid = HV_PARTITION_ID_SELF; + input->header.vpindex = HV_VP_INDEX_SELF; + input->header.inputvtl = ms_hyperv.vtl; + input->element[0].name = HV_X64_REGISTER_SEV_AVIC_GPA; + input->element[0].value.reg64 = value.u64; + + do { + ret = hv_do_hypercall(control, input, NULL); + } while (ret == HV_STATUS_TIME_OUT && retry--); + + if (!hv_result_success(ret)) + pr_err("Failed to set Secure AVIC backing page %llx.\n", ret); + + local_irq_restore(flags); + + if (hv_result_success(ret)) + return ES_OK; + else + return ES_VMM_ERROR; +} + int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu) { struct sev_es_save_area *vmsa = (struct sev_es_save_area *) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index abc4659f5809..b140558816de 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -43,6 +43,7 @@ static inline unsigned char hv_get_nmi_reason(void) extern bool hyperv_paravisor_present; extern void *hv_hypercall_pg; +extern void *hv_vp_early_input_arg; extern union hv_ghcb * __percpu *hv_ghcb_pg; @@ -252,6 +253,7 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); bool hv_ghcb_negotiate_protocol(void); void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason); int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip, unsigned int cpu); +enum es_result hv_set_savic_backing_page(u64 gfn); #else static inline bool hv_ghcb_negotiate_protocol(void) { return false; } static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {} diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c index dbc5678bc3b6..60bdb524de53 100644 --- a/arch/x86/kernel/apic/x2apic_savic.c +++ b/arch/x86/kernel/apic/x2apic_savic.c @@ -14,6 +14,7 @@ #include <asm/apic.h> #include <asm/sev.h> +#include <asm/mshyperv.h> #include "local.h" @@ -342,6 +343,7 @@ static void savic_setup(void) void *ap = this_cpu_ptr(savic_page); enum es_result res; unsigned long gpa; + unsigned long gfn; /* * Before Secure AVIC is enabled, APIC MSR reads are intercepted. @@ -350,6 +352,7 @@ static void savic_setup(void) apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID)); gpa = __pa(ap); + gfn = gpa >> PAGE_SHIFT; /* * The NPT entry for a vCPU's APIC backing page must always be @@ -361,7 +364,11 @@ static void savic_setup(void) * VMRUN, the hypervisor makes use of this information to make sure * the APIC backing page is mapped in NPT. */ - res = savic_register_gpa(gpa); + if (hv_isolation_type_snp()) + res = hv_set_savic_backing_page(gfn); + else + res = savic_register_gpa(gpa); + if (res != ES_OK) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL); diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 1be7f6a02304..e3092469aafe 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -1170,6 +1170,28 @@ union hv_register_value { union hv_arm64_pending_synthetic_exception_event pending_synthetic_exception_event; }; +/* HvSetVpRegisters hypercall with variable size reg name/value list*/ +struct hv_set_vp_registers_input { + struct { + u64 partitionid; + u32 vpindex; + u8 inputvtl; + u8 padding[3]; + } header; + struct { + u32 name; + u32 padding1; + u64 padding2; + union { + union hv_register_value value; + struct { + u64 valuelow; + u64 valuehigh; + }; + }; + } element[]; +} __packed; + /* NOTE: Linux helper struct - NOT from Hyper-V code. */ struct hv_output_get_vp_registers { DECLARE_FLEX_ARRAY(union hv_register_value, values); @@ -1210,6 +1232,15 @@ struct hv_input_get_vp_registers { u32 names[]; } __packed; +union hv_x64_register_sev_gpa_page { + u64 u64; + struct { + u64 enabled:1; + u64 reserved:11; + u64 pagenumber:52; + }; +} __packed; + struct hv_input_set_vp_registers { u64 partition_id; u32 vp_index; @@ -1230,6 +1261,14 @@ struct hv_send_ipi { /* HV_INPUT_SEND_SYNTHETIC_CLUSTER_IPI */ #define HV_VTL_MASK GENMASK(3, 0) +/* + * Registers are only accessible via HVCALL_GET_VP_REGISTERS hvcall and + * there is not associated MSR address. + */ +#define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003 +#define HV_X64_VTL_MASK GENMASK(3, 0) +#define HV_X64_REGISTER_SEV_AVIC_GPA 0x00090043 + /* Hyper-V memory host visibility */ enum hv_mem_host_visibility { VMBUS_PAGE_NOT_VISIBLE = 0, -- 2.25.1
