Drivers: hv: vmbus: Support fully enlightened TDX guests
authorDexuan Cui <decui@microsoft.com>
Thu, 24 Aug 2023 08:07:05 +0000 (01:07 -0700)
committerWei Liu <wei.liu@kernel.org>
Fri, 25 Aug 2023 00:04:56 +0000 (00:04 +0000)
Add Hyper-V specific code so that a fully enlightened TDX guest (i.e.
without the paravisor) can run on Hyper-V:
  Don't use hv_vp_assist_page. Use GHCI instead.
  Don't try to use the unsupported HV_REGISTER_CRASH_CTL.
  Don't trust (use) Hyper-V's TLB-flushing hypercalls.
  Don't use lazy EOI.
  Share the SynIC Event/Message pages with the hypervisor.
  Don't use the Hyper-V TSC page for now, because non-trivial work is
    required to share the page with the hypervisor.

Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Signed-off-by: Dexuan Cui <decui@microsoft.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Link: https://lore.kernel.org/r/20230824080712.30327-4-decui@microsoft.com
arch/x86/hyperv/hv_apic.c
arch/x86/hyperv/hv_init.c
arch/x86/kernel/cpu/mshyperv.c
drivers/hv/hv.c

index 1fbda2f..cb74290 100644 (file)
@@ -177,8 +177,11 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector,
            (exclude_self && weight == 1 && cpumask_test_cpu(this_cpu, mask)))
                return true;
 
-       if (!hv_hypercall_pg)
-               return false;
+       /* A fully enlightened TDX VM uses GHCI rather than hv_hypercall_pg. */
+       if (!hv_hypercall_pg) {
+               if (ms_hyperv.paravisor_present || !hv_isolation_type_tdx())
+                       return false;
+       }
 
        if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR))
                return false;
@@ -231,9 +234,15 @@ static bool __send_ipi_one(int cpu, int vector)
 
        trace_hyperv_send_ipi_one(cpu, vector);
 
-       if (!hv_hypercall_pg || (vp == VP_INVAL))
+       if (vp == VP_INVAL)
                return false;
 
+       /* A fully enlightened TDX VM uses GHCI rather than hv_hypercall_pg. */
+       if (!hv_hypercall_pg) {
+               if (ms_hyperv.paravisor_present || !hv_isolation_type_tdx())
+                       return false;
+       }
+
        if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR))
                return false;
 
index 255e02e..c1c1b4e 100644 (file)
@@ -80,7 +80,7 @@ static int hyperv_init_ghcb(void)
 static int hv_cpu_init(unsigned int cpu)
 {
        union hv_vp_assist_msr_contents msr = { 0 };
-       struct hv_vp_assist_page **hvp = &hv_vp_assist_page[cpu];
+       struct hv_vp_assist_page **hvp;
        int ret;
 
        ret = hv_common_cpu_init(cpu);
@@ -90,6 +90,7 @@ static int hv_cpu_init(unsigned int cpu)
        if (!hv_vp_assist_page)
                return 0;
 
+       hvp = &hv_vp_assist_page[cpu];
        if (hv_root_partition) {
                /*
                 * For root partition we get the hypervisor provided VP assist
@@ -442,11 +443,21 @@ void __init hyperv_init(void)
        if (hv_common_init())
                return;
 
-       hv_vp_assist_page = kcalloc(num_possible_cpus(),
-                                   sizeof(*hv_vp_assist_page), GFP_KERNEL);
+       /*
+        * 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.
+        */
+       if (hv_isolation_type_tdx())
+               hv_vp_assist_page = NULL;
+       else
+               hv_vp_assist_page = kcalloc(num_possible_cpus(),
+                                           sizeof(*hv_vp_assist_page),
+                                           GFP_KERNEL);
        if (!hv_vp_assist_page) {
                ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
-               goto common_free;
+
+               if (!hv_isolation_type_tdx())
+                       goto common_free;
        }
 
        if (hv_isolation_type_snp()) {
index 6309387..ff3d9c5 100644 (file)
@@ -420,6 +420,20 @@ static void __init ms_hyperv_init_platform(void)
                                static_branch_enable(&isolation_type_en_snp);
                } else if (hv_get_isolation_type() == HV_ISOLATION_TYPE_TDX) {
                        static_branch_enable(&isolation_type_tdx);
+
+                       /* A TDX VM must use x2APIC and doesn't use lazy EOI. */
+                       ms_hyperv.hints &= ~HV_X64_APIC_ACCESS_RECOMMENDED;
+
+                       if (!ms_hyperv.paravisor_present) {
+                               /* To be supported: more work is required.  */
+                               ms_hyperv.features &= ~HV_MSR_REFERENCE_TSC_AVAILABLE;
+
+                               /* HV_REGISTER_CRASH_CTL is unsupported. */
+                               ms_hyperv.misc_features &= ~HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE;
+
+                               /* Don't trust Hyper-V's TLB-flushing hypercalls. */
+                               ms_hyperv.hints &= ~HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED;
+                       }
                }
        }
 
index ec6e35a..d106411 100644 (file)
@@ -121,11 +121,15 @@ int hv_synic_alloc(void)
                                (void *)get_zeroed_page(GFP_ATOMIC);
                        if (hv_cpu->synic_event_page == NULL) {
                                pr_err("Unable to allocate SYNIC event page\n");
+
+                               free_page((unsigned long)hv_cpu->synic_message_page);
+                               hv_cpu->synic_message_page = NULL;
                                goto err;
                        }
                }
 
-               if (hv_isolation_type_en_snp()) {
+               if (!ms_hyperv.paravisor_present &&
+                   (hv_isolation_type_en_snp() || hv_isolation_type_tdx())) {
                        ret = set_memory_decrypted((unsigned long)
                                hv_cpu->synic_message_page, 1);
                        if (ret) {
@@ -174,7 +178,8 @@ void hv_synic_free(void)
                        = per_cpu_ptr(hv_context.cpu_context, cpu);
 
                /* It's better to leak the page if the encryption fails. */
-               if (hv_isolation_type_en_snp()) {
+               if (!ms_hyperv.paravisor_present &&
+                   (hv_isolation_type_en_snp() || hv_isolation_type_tdx())) {
                        if (hv_cpu->synic_message_page) {
                                ret = set_memory_encrypted((unsigned long)
                                        hv_cpu->synic_message_page, 1);