]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86/hyperv: Fix APIC ID and VP index confusion in hv_snp_boot_ap()
authorRoman Kisel <romank@linux.microsoft.com>
Wed, 7 May 2025 18:22:25 +0000 (11:22 -0700)
committerWei Liu <wei.liu@kernel.org>
Fri, 23 May 2025 16:30:56 +0000 (16:30 +0000)
To start an application processor in SNP-isolated guest, a hypercall
is used that takes a virtual processor index. The hv_snp_boot_ap()
function uses that START_VP hypercall but passes as VP index to it
what it receives as a wakeup_secondary_cpu_64 callback: the APIC ID.

As those two aren't generally interchangeable, that may lead to hung
APs if the VP index and the APIC ID don't match up.

Update the parameter names to avoid confusion as to what the parameter
is. Use the APIC ID to the VP index conversion to provide the correct
input to the hypercall.

Cc: stable@vger.kernel.org
Fixes: 44676bb9d566 ("x86/hyperv: Add smp support for SEV-SNP guest")
Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Link: https://lore.kernel.org/r/20250507182227.7421-2-romank@linux.microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Message-ID: <20250507182227.7421-2-romank@linux.microsoft.com>

arch/x86/hyperv/hv_init.c
arch/x86/hyperv/hv_vtl.c
arch/x86/hyperv/ivm.c
arch/x86/include/asm/mshyperv.h
include/hyperv/hvgdk_mini.h

index 3b569291dfede2b966061a5f7d1f89bc495a0013..9a8fc144e195f3ee11bc33c9a62a85baf74f8c32 100644 (file)
@@ -672,3 +672,36 @@ bool hv_is_hyperv_initialized(void)
        return hypercall_msr.enable;
 }
 EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
+
+int hv_apicid_to_vp_index(u32 apic_id)
+{
+       u64 control;
+       u64 status;
+       unsigned long irq_flags;
+       struct hv_get_vp_from_apic_id_in *input;
+       u32 *output, ret;
+
+       local_irq_save(irq_flags);
+
+       input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+       memset(input, 0, sizeof(*input));
+       input->partition_id = HV_PARTITION_ID_SELF;
+       input->apic_ids[0] = apic_id;
+
+       output = *this_cpu_ptr(hyperv_pcpu_output_arg);
+
+       control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_INDEX_FROM_APIC_ID;
+       status = hv_do_hypercall(control, input, output);
+       ret = output[0];
+
+       local_irq_restore(irq_flags);
+
+       if (!hv_result_success(status)) {
+               pr_err("failed to get vp index from apic id %d, status %#llx\n",
+                      apic_id, status);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hv_apicid_to_vp_index);
index ac3d27a766d52af4179e9df757d19c58e358b763..2f32ac1ae40eab0d2f308196c94f94874a7ee59b 100644 (file)
@@ -211,41 +211,9 @@ free_lock:
        return ret;
 }
 
-static int hv_vtl_apicid_to_vp_id(u32 apic_id)
-{
-       u64 control;
-       u64 status;
-       unsigned long irq_flags;
-       struct hv_get_vp_from_apic_id_in *input;
-       u32 *output, ret;
-
-       local_irq_save(irq_flags);
-
-       input = *this_cpu_ptr(hyperv_pcpu_input_arg);
-       memset(input, 0, sizeof(*input));
-       input->partition_id = HV_PARTITION_ID_SELF;
-       input->apic_ids[0] = apic_id;
-
-       output = *this_cpu_ptr(hyperv_pcpu_output_arg);
-
-       control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_ID_FROM_APIC_ID;
-       status = hv_do_hypercall(control, input, output);
-       ret = output[0];
-
-       local_irq_restore(irq_flags);
-
-       if (!hv_result_success(status)) {
-               pr_err("failed to get vp id from apic id %d, status %#llx\n",
-                      apic_id, status);
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
 static int hv_vtl_wakeup_secondary_cpu(u32 apicid, unsigned long start_eip)
 {
-       int vp_id, cpu;
+       int vp_index, cpu;
 
        /* Find the logical CPU for the APIC ID */
        for_each_present_cpu(cpu) {
@@ -256,18 +224,18 @@ static int hv_vtl_wakeup_secondary_cpu(u32 apicid, unsigned long start_eip)
                return -EINVAL;
 
        pr_debug("Bringing up CPU with APIC ID %d in VTL2...\n", apicid);
-       vp_id = hv_vtl_apicid_to_vp_id(apicid);
+       vp_index = hv_apicid_to_vp_index(apicid);
 
-       if (vp_id < 0) {
+       if (vp_index < 0) {
                pr_err("Couldn't find CPU with APIC ID %d\n", apicid);
                return -EINVAL;
        }
-       if (vp_id > ms_hyperv.max_vp_index) {
-               pr_err("Invalid CPU id %d for APIC ID %d\n", vp_id, apicid);
+       if (vp_index > ms_hyperv.max_vp_index) {
+               pr_err("Invalid CPU id %d for APIC ID %d\n", vp_index, apicid);
                return -EINVAL;
        }
 
-       return hv_vtl_bringup_vcpu(vp_id, cpu, start_eip);
+       return hv_vtl_bringup_vcpu(vp_index, cpu, start_eip);
 }
 
 int __init hv_vtl_early_init(void)
index 77bf05f06b9efaf0ab16e681905161d0fb9a8346..0cc239cdb4dad87cd6a518fcb19a19f0d8bf8d22 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/bitfield.h>
 #include <linux/types.h>
 #include <linux/slab.h>
+#include <linux/cpu.h>
 #include <asm/svm.h>
 #include <asm/sev.h>
 #include <asm/io.h>
@@ -288,7 +289,7 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
                free_page((unsigned long)vmsa);
 }
 
-int hv_snp_boot_ap(u32 cpu, unsigned long start_ip)
+int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip)
 {
        struct sev_es_save_area *vmsa = (struct sev_es_save_area *)
                __get_free_page(GFP_KERNEL | __GFP_ZERO);
@@ -297,10 +298,27 @@ int hv_snp_boot_ap(u32 cpu, unsigned long start_ip)
        u64 ret, retry = 5;
        struct hv_enable_vp_vtl *start_vp_input;
        unsigned long flags;
+       int cpu, vp_index;
 
        if (!vmsa)
                return -ENOMEM;
 
+       /* Find the Hyper-V VP index which might be not the same as APIC ID */
+       vp_index = hv_apicid_to_vp_index(apic_id);
+       if (vp_index < 0 || vp_index > ms_hyperv.max_vp_index)
+               return -EINVAL;
+
+       /*
+        * Find the Linux CPU number for addressing the per-CPU data, and it
+        * might not be the same as APIC ID.
+        */
+       for_each_present_cpu(cpu) {
+               if (arch_match_cpu_phys_id(cpu, apic_id))
+                       break;
+       }
+       if (cpu >= nr_cpu_ids)
+               return -EINVAL;
+
        native_store_gdt(&gdtr);
 
        vmsa->gdtr.base = gdtr.address;
@@ -348,7 +366,7 @@ int hv_snp_boot_ap(u32 cpu, unsigned long start_ip)
        start_vp_input = (struct hv_enable_vp_vtl *)ap_start_input_arg;
        memset(start_vp_input, 0, sizeof(*start_vp_input));
        start_vp_input->partition_id = -1;
-       start_vp_input->vp_index = cpu;
+       start_vp_input->vp_index = vp_index;
        start_vp_input->target_vtl.target_vtl = ms_hyperv.vtl;
        *(u64 *)&start_vp_input->vp_context = __pa(vmsa) | 1;
 
index bab5ccfc60a7485989bc322a98742ee404cea039..0b9a3a307d06554452131709fbeb501cc08d09e5 100644 (file)
@@ -268,11 +268,11 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
 #ifdef CONFIG_AMD_MEM_ENCRYPT
 bool hv_ghcb_negotiate_protocol(void);
 void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason);
-int hv_snp_boot_ap(u32 cpu, unsigned long start_ip);
+int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip);
 #else
 static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
 static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
-static inline int hv_snp_boot_ap(u32 cpu, unsigned long start_ip) { return 0; }
+static inline int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip) { return 0; }
 #endif
 
 #if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
@@ -306,6 +306,7 @@ static __always_inline u64 hv_raw_get_msr(unsigned int reg)
 {
        return __rdmsr(reg);
 }
+int hv_apicid_to_vp_index(u32 apic_id);
 
 #else /* CONFIG_HYPERV */
 static inline void hyperv_init(void) {}
@@ -327,6 +328,7 @@ static inline void hv_set_msr(unsigned int reg, u64 value) { }
 static inline u64 hv_get_msr(unsigned int reg) { return 0; }
 static inline void hv_set_non_nested_msr(unsigned int reg, u64 value) { }
 static inline u64 hv_get_non_nested_msr(unsigned int reg) { return 0; }
+static inline int hv_apicid_to_vp_index(u32 apic_id) { return -EINVAL; }
 #endif /* CONFIG_HYPERV */
 
 
index cf0923dc727daa08fb05e8d8ef23fd65a3440e6b..2d431b53f587743a8510876ca1bbf6a620ec2840 100644 (file)
@@ -475,7 +475,7 @@ union hv_vp_assist_msr_contents {    /* HV_REGISTER_VP_ASSIST_PAGE */
 #define HVCALL_CREATE_PORT                             0x0095
 #define HVCALL_CONNECT_PORT                            0x0096
 #define HVCALL_START_VP                                        0x0099
-#define HVCALL_GET_VP_ID_FROM_APIC_ID                  0x009a
+#define HVCALL_GET_VP_INDEX_FROM_APIC_ID                       0x009a
 #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE      0x00af
 #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST       0x00b0
 #define HVCALL_SIGNAL_EVENT_DIRECT                     0x00c0