Skip to content

Commit 86c4827

Browse files
romank-msftliuw
authored andcommitted
x86/hyperv: Fix APIC ID and VP index confusion in hv_snp_boot_ap()
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: 44676bb ("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>
1 parent d684f9b commit 86c4827

File tree

5 files changed

+64
-43
lines changed

5 files changed

+64
-43
lines changed

arch/x86/hyperv/hv_init.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,36 @@ bool hv_is_hyperv_initialized(void)
672672
return hypercall_msr.enable;
673673
}
674674
EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
675+
676+
int hv_apicid_to_vp_index(u32 apic_id)
677+
{
678+
u64 control;
679+
u64 status;
680+
unsigned long irq_flags;
681+
struct hv_get_vp_from_apic_id_in *input;
682+
u32 *output, ret;
683+
684+
local_irq_save(irq_flags);
685+
686+
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
687+
memset(input, 0, sizeof(*input));
688+
input->partition_id = HV_PARTITION_ID_SELF;
689+
input->apic_ids[0] = apic_id;
690+
691+
output = *this_cpu_ptr(hyperv_pcpu_output_arg);
692+
693+
control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_INDEX_FROM_APIC_ID;
694+
status = hv_do_hypercall(control, input, output);
695+
ret = output[0];
696+
697+
local_irq_restore(irq_flags);
698+
699+
if (!hv_result_success(status)) {
700+
pr_err("failed to get vp index from apic id %d, status %#llx\n",
701+
apic_id, status);
702+
return -EINVAL;
703+
}
704+
705+
return ret;
706+
}
707+
EXPORT_SYMBOL_GPL(hv_apicid_to_vp_index);

arch/x86/hyperv/hv_vtl.c

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -211,41 +211,9 @@ static int hv_vtl_bringup_vcpu(u32 target_vp_index, int cpu, u64 eip_ignored)
211211
return ret;
212212
}
213213

214-
static int hv_vtl_apicid_to_vp_id(u32 apic_id)
215-
{
216-
u64 control;
217-
u64 status;
218-
unsigned long irq_flags;
219-
struct hv_get_vp_from_apic_id_in *input;
220-
u32 *output, ret;
221-
222-
local_irq_save(irq_flags);
223-
224-
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
225-
memset(input, 0, sizeof(*input));
226-
input->partition_id = HV_PARTITION_ID_SELF;
227-
input->apic_ids[0] = apic_id;
228-
229-
output = *this_cpu_ptr(hyperv_pcpu_output_arg);
230-
231-
control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_ID_FROM_APIC_ID;
232-
status = hv_do_hypercall(control, input, output);
233-
ret = output[0];
234-
235-
local_irq_restore(irq_flags);
236-
237-
if (!hv_result_success(status)) {
238-
pr_err("failed to get vp id from apic id %d, status %#llx\n",
239-
apic_id, status);
240-
return -EINVAL;
241-
}
242-
243-
return ret;
244-
}
245-
246214
static int hv_vtl_wakeup_secondary_cpu(u32 apicid, unsigned long start_eip)
247215
{
248-
int vp_id, cpu;
216+
int vp_index, cpu;
249217

250218
/* Find the logical CPU for the APIC ID */
251219
for_each_present_cpu(cpu) {
@@ -256,18 +224,18 @@ static int hv_vtl_wakeup_secondary_cpu(u32 apicid, unsigned long start_eip)
256224
return -EINVAL;
257225

258226
pr_debug("Bringing up CPU with APIC ID %d in VTL2...\n", apicid);
259-
vp_id = hv_vtl_apicid_to_vp_id(apicid);
227+
vp_index = hv_apicid_to_vp_index(apicid);
260228

261-
if (vp_id < 0) {
229+
if (vp_index < 0) {
262230
pr_err("Couldn't find CPU with APIC ID %d\n", apicid);
263231
return -EINVAL;
264232
}
265-
if (vp_id > ms_hyperv.max_vp_index) {
266-
pr_err("Invalid CPU id %d for APIC ID %d\n", vp_id, apicid);
233+
if (vp_index > ms_hyperv.max_vp_index) {
234+
pr_err("Invalid CPU id %d for APIC ID %d\n", vp_index, apicid);
267235
return -EINVAL;
268236
}
269237

270-
return hv_vtl_bringup_vcpu(vp_id, cpu, start_eip);
238+
return hv_vtl_bringup_vcpu(vp_index, cpu, start_eip);
271239
}
272240

273241
int __init hv_vtl_early_init(void)

arch/x86/hyperv/ivm.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/bitfield.h>
1010
#include <linux/types.h>
1111
#include <linux/slab.h>
12+
#include <linux/cpu.h>
1213
#include <asm/svm.h>
1314
#include <asm/sev.h>
1415
#include <asm/io.h>
@@ -288,7 +289,7 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
288289
free_page((unsigned long)vmsa);
289290
}
290291

291-
int hv_snp_boot_ap(u32 cpu, unsigned long start_ip)
292+
int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip)
292293
{
293294
struct sev_es_save_area *vmsa = (struct sev_es_save_area *)
294295
__get_free_page(GFP_KERNEL | __GFP_ZERO);
@@ -297,10 +298,27 @@ int hv_snp_boot_ap(u32 cpu, unsigned long start_ip)
297298
u64 ret, retry = 5;
298299
struct hv_enable_vp_vtl *start_vp_input;
299300
unsigned long flags;
301+
int cpu, vp_index;
300302

301303
if (!vmsa)
302304
return -ENOMEM;
303305

306+
/* Find the Hyper-V VP index which might be not the same as APIC ID */
307+
vp_index = hv_apicid_to_vp_index(apic_id);
308+
if (vp_index < 0 || vp_index > ms_hyperv.max_vp_index)
309+
return -EINVAL;
310+
311+
/*
312+
* Find the Linux CPU number for addressing the per-CPU data, and it
313+
* might not be the same as APIC ID.
314+
*/
315+
for_each_present_cpu(cpu) {
316+
if (arch_match_cpu_phys_id(cpu, apic_id))
317+
break;
318+
}
319+
if (cpu >= nr_cpu_ids)
320+
return -EINVAL;
321+
304322
native_store_gdt(&gdtr);
305323

306324
vmsa->gdtr.base = gdtr.address;
@@ -348,7 +366,7 @@ int hv_snp_boot_ap(u32 cpu, unsigned long start_ip)
348366
start_vp_input = (struct hv_enable_vp_vtl *)ap_start_input_arg;
349367
memset(start_vp_input, 0, sizeof(*start_vp_input));
350368
start_vp_input->partition_id = -1;
351-
start_vp_input->vp_index = cpu;
369+
start_vp_input->vp_index = vp_index;
352370
start_vp_input->target_vtl.target_vtl = ms_hyperv.vtl;
353371
*(u64 *)&start_vp_input->vp_context = __pa(vmsa) | 1;
354372

arch/x86/include/asm/mshyperv.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,11 @@ int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
268268
#ifdef CONFIG_AMD_MEM_ENCRYPT
269269
bool hv_ghcb_negotiate_protocol(void);
270270
void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason);
271-
int hv_snp_boot_ap(u32 cpu, unsigned long start_ip);
271+
int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip);
272272
#else
273273
static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
274274
static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
275-
static inline int hv_snp_boot_ap(u32 cpu, unsigned long start_ip) { return 0; }
275+
static inline int hv_snp_boot_ap(u32 apic_id, unsigned long start_ip) { return 0; }
276276
#endif
277277

278278
#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)
306306
{
307307
return __rdmsr(reg);
308308
}
309+
int hv_apicid_to_vp_index(u32 apic_id);
309310

310311
#else /* CONFIG_HYPERV */
311312
static inline void hyperv_init(void) {}
@@ -327,6 +328,7 @@ static inline void hv_set_msr(unsigned int reg, u64 value) { }
327328
static inline u64 hv_get_msr(unsigned int reg) { return 0; }
328329
static inline void hv_set_non_nested_msr(unsigned int reg, u64 value) { }
329330
static inline u64 hv_get_non_nested_msr(unsigned int reg) { return 0; }
331+
static inline int hv_apicid_to_vp_index(u32 apic_id) { return -EINVAL; }
330332
#endif /* CONFIG_HYPERV */
331333

332334

include/hyperv/hvgdk_mini.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
475475
#define HVCALL_CREATE_PORT 0x0095
476476
#define HVCALL_CONNECT_PORT 0x0096
477477
#define HVCALL_START_VP 0x0099
478-
#define HVCALL_GET_VP_ID_FROM_APIC_ID 0x009a
478+
#define HVCALL_GET_VP_INDEX_FROM_APIC_ID 0x009a
479479
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
480480
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
481481
#define HVCALL_SIGNAL_EVENT_DIRECT 0x00c0

0 commit comments

Comments
 (0)