Merge tag '6.6-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / arch / x86 / hyperv / hv_init.c
index 507d98331e7c469439207c80b23d0d3a679d11b5..783ed339f3415c09bf83830b0bd854d92e5f31dc 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/hyperv-tlfs.h>
 #include <asm/mshyperv.h>
 #include <asm/idtentry.h>
+#include <asm/set_memory.h>
 #include <linux/kexec.h>
 #include <linux/version.h>
 #include <linux/vmalloc.h>
@@ -52,7 +53,7 @@ static int hyperv_init_ghcb(void)
        void *ghcb_va;
        void **ghcb_base;
 
-       if (!hv_isolation_type_snp())
+       if (!ms_hyperv.paravisor_present || !hv_isolation_type_snp())
                return 0;
 
        if (!hv_ghcb_pg)
@@ -80,7 +81,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 +91,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
@@ -107,8 +109,21 @@ static int hv_cpu_init(unsigned int cpu)
                 * in hv_cpu_die(), otherwise a CPU may not be stopped in the
                 * case of CPU offlining and the VM will hang.
                 */
-               if (!*hvp)
+               if (!*hvp) {
                        *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO);
+
+                       /*
+                        * Hyper-V should never specify a VM that is a Confidential
+                        * VM and also running in the root partition. Root partition
+                        * is blocked to run in Confidential VM. So only decrypt assist
+                        * page in non-root partition here.
+                        */
+                       if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
+                               WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));
+                               memset(*hvp, 0, PAGE_SIZE);
+                       }
+               }
+
                if (*hvp)
                        msr.pfn = vmalloc_to_pfn(*hvp);
 
@@ -379,6 +394,36 @@ static void __init hv_get_partition_id(void)
        local_irq_restore(flags);
 }
 
+static u8 __init get_vtl(void)
+{
+       u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_REGISTERS;
+       struct hv_get_vp_registers_input *input;
+       struct hv_get_vp_registers_output *output;
+       unsigned long flags;
+       u64 ret;
+
+       local_irq_save(flags);
+       input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+       output = (struct hv_get_vp_registers_output *)input;
+
+       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 = 0;
+       input->element[0].name0 = HV_X64_REGISTER_VSM_VP_STATUS;
+
+       ret = hv_do_hypercall(control, input, output);
+       if (hv_result_success(ret)) {
+               ret = output->as64.low & HV_X64_VTL_MASK;
+       } else {
+               pr_err("Failed to get VTL(%lld) and set VTL to zero by default.\n", ret);
+               ret = 0;
+       }
+
+       local_irq_restore(flags);
+       return ret;
+}
+
 /*
  * This function is to be invoked early in the boot sequence after the
  * hypervisor has been detected.
@@ -399,14 +444,24 @@ 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()) {
+       if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
                /* Negotiate GHCB Version. */
                if (!hv_ghcb_negotiate_protocol())
                        hv_ghcb_terminate(SEV_TERM_SET_GEN,
@@ -426,12 +481,32 @@ void __init hyperv_init(void)
         * Setup the hypercall page and enable hypercalls.
         * 1. Register the guest ID
         * 2. Enable the hypercall and register the hypercall page
+        *
+        * A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg:
+        * when the hypercall input is a page, such a VM must pass a decrypted
+        * page to Hyper-V, e.g. hv_post_message() uses the per-CPU page
+        * hyperv_pcpu_input_arg, which is decrypted if no paravisor is present.
+        *
+        * A TDX VM with the paravisor uses hv_hypercall_pg for most hypercalls,
+        * which are handled by the paravisor and the VM must use an encrypted
+        * input page: in such a VM, the hyperv_pcpu_input_arg is encrypted and
+        * used in the hypercalls, e.g. see hv_mark_gpa_visibility() and
+        * hv_arch_irq_unmask(). Such a VM uses TDX GHCI for two hypercalls:
+        * 1. HVCALL_SIGNAL_EVENT: see vmbus_set_event() and _hv_do_fast_hypercall8().
+        * 2. HVCALL_POST_MESSAGE: the input page must be a decrypted page, i.e.
+        * hv_post_message() in such a VM can't use the encrypted hyperv_pcpu_input_arg;
+        * instead, hv_post_message() uses the post_msg_page, which is decrypted
+        * in such a VM and is only used in such a VM.
         */
        guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
        wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
 
-       /* Hyper-V requires to write guest os id via ghcb in SNP IVM. */
-       hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
+       /* With the paravisor, the VM must also write the ID via GHCB/GHCI */
+       hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
+
+       /* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg */
+       if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
+               goto skip_hypercall_pg_init;
 
        hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START,
                        VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_ROX,
@@ -472,6 +547,7 @@ void __init hyperv_init(void)
                wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
        }
 
+skip_hypercall_pg_init:
        /*
         * Some versions of Hyper-V that provide IBT in guest VMs have a bug
         * in that there's no ENDBR64 instruction at the entry to the
@@ -527,11 +603,15 @@ void __init hyperv_init(void)
        /* Query the VMs extended capability once, so that it can be cached. */
        hv_query_ext_cap(0);
 
+       /* Find the VTL */
+       if (!ms_hyperv.paravisor_present && hv_isolation_type_snp())
+               ms_hyperv.vtl = get_vtl();
+
        return;
 
 clean_guest_os_id:
        wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
-       hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
+       hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
        cpuhp_remove_state(cpuhp);
 free_ghcb_page:
        free_percpu(hv_ghcb_pg);
@@ -552,7 +632,7 @@ void hyperv_cleanup(void)
 
        /* Reset our OS id */
        wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
-       hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
+       hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
 
        /*
         * Reset hypercall page reference before reset the page,
@@ -615,6 +695,9 @@ bool hv_is_hyperv_initialized(void)
        if (x86_hyper_type != X86_HYPER_MS_HYPERV)
                return false;
 
+       /* A TDX VM with no paravisor uses TDX GHCI call rather than hv_hypercall_pg */
+       if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
+               return true;
        /*
         * Verify that earlier initialization succeeded by checking
         * that the hypercall page is setup