lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <8a235e4f-f4ce-445e-9714-380573033455@linux.microsoft.com>
Date: Fri, 25 Apr 2025 09:35:49 -0700
From: Roman Kisel <romank@...ux.microsoft.com>
To: Michael Kelley <mhklinux@...look.com>, "bp@...en8.de" <bp@...en8.de>,
 "dave.hansen@...ux.intel.com" <dave.hansen@...ux.intel.com>,
 "decui@...rosoft.com" <decui@...rosoft.com>,
 "haiyangz@...rosoft.com" <haiyangz@...rosoft.com>,
 "hpa@...or.com" <hpa@...or.com>, "kys@...rosoft.com" <kys@...rosoft.com>,
 "mikelley@...rosoft.com" <mikelley@...rosoft.com>,
 "mingo@...hat.com" <mingo@...hat.com>,
 "tglx@...utronix.de" <tglx@...utronix.de>,
 "tiala@...rosoft.com" <tiala@...rosoft.com>,
 "wei.liu@...nel.org" <wei.liu@...nel.org>,
 "linux-hyperv@...r.kernel.org" <linux-hyperv@...r.kernel.org>,
 "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
 "x86@...nel.org" <x86@...nel.org>
Cc: "apais@...rosoft.com" <apais@...rosoft.com>,
 "benhill@...rosoft.com" <benhill@...rosoft.com>,
 "bperkins@...rosoft.com" <bperkins@...rosoft.com>,
 "sunilmut@...rosoft.com" <sunilmut@...rosoft.com>
Subject: Re: [PATCH hyperv-next] x86/hyperv: Fix APIC ID and VP ID confusion
 in hv_snp_boot_ap()



On 4/25/2025 8:12 AM, Michael Kelley wrote:
> From: Roman Kisel <romank@...ux.microsoft.com> Sent: Thursday, April 24, 2025 2:58 PM
>>
>> 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 ID 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 VP IDs and APIC IDs don't match, e.g. APIC IDs might be sparse
>> whereas VP IDs never are.
> 
> I agree that VP IDs (a.k.a. VP indexes) and APIC IDs don't necessary match,
> and that APIC IDs might be sparse. But I'm not aware of any statement
> in the TLFS about the nature of VP indexes, except that
> 
>     "A virtual processor index must be less than the maximum number of
>     virtual processors per partition."
> 
> But that maximum is the Hyper-V implementation maximum, not the
> maximum for a particular VM. So the statement does not imply
> denseness unless the number of CPUs in the VM is equal to the
> Hyper-V implementation max. In other parts of Linux kernel code,
> we assume that VP indexes might be sparse as well.
> 
> All that said, this is just a comment about the precise accuracy of
> your commit message, and doesn't affect the code.
> 

I appreciate your help with the precision. I used loose language,
agreed, would like to fix that. The patch was applied though but not yet
sent to the Linus'es tree as I understand. I'd appreciate guidance on
the process! Should I send a v2 nevertheless and explain the situation
in the cover letter?

IOW, how do I make this easier for the maintainer(s)?

>>
>> Update the parameter names to avoid confusion as to what the parameter
>> is. Use the APIC ID to VP ID conversion to provide correct input to the
>> hypercall.
> 
> Terminology:  The TLFS calls this the "VP Index", not the "VP ID".  In
> other Linux code, we also call it the "VP Index".  See the hv_vp_index
> array, for example.  The exception is the hypercall itself, which the TLFS
> calls HvCallGetVpIndexFromApicId, but which our Linux code calls
> HVCALL_GET_VP_ID_FROM_APIC_ID for some unknown reason.
> 
> Could you fix the terminology to be consistent?  And maybe fix the
> HVCALL_* string name as well.  I know you are just moving the
> existing VTL code, but let's take the opportunity to avoid any
> terminology inconsistency.
> 

I percieved ID as both "index" and "identificator" I guess but maybe
"idx" is more like "index". I'll send out a fix for the terminology,
thanks for your help!

>>
>> Cc: stable@...r.kernel.org
>> Fixes: 44676bb9d566 ("x86/hyperv: Add smp support for SEV-SNP guest")
>> Signed-off-by: Roman Kisel <romank@...ux.microsoft.com>
>> ---
>>   arch/x86/hyperv/hv_init.c       | 33 ++++++++++++++++++++++++++++++++
>>   arch/x86/hyperv/hv_vtl.c        | 34 +--------------------------------
>>   arch/x86/hyperv/ivm.c           | 11 +++++++++--
>>   arch/x86/include/asm/mshyperv.h |  5 +++--
>>   4 files changed, 46 insertions(+), 37 deletions(-)
>>
>> diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
>> index ddeb40930bc8..23422342a091 100644
>> --- a/arch/x86/hyperv/hv_init.c
>> +++ b/arch/x86/hyperv/hv_init.c
>> @@ -706,3 +706,36 @@ bool hv_is_hyperv_initialized(void)
>>   	return hypercall_msr.enable;
>>   }
>>   EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
>> +
>> +int hv_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;
>> +}
>> +EXPORT_SYMBOL_GPL(hv_apicid_to_vp_id);
>> diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c
>> index 582fe820e29c..8bc4f0121e5e 100644
>> --- a/arch/x86/hyperv/hv_vtl.c
>> +++ b/arch/x86/hyperv/hv_vtl.c
>> @@ -205,38 +205,6 @@ static int hv_vtl_bringup_vcpu(u32 target_vp_index, int cpu, u64 eip_ignored)
>>   	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;
>> @@ -250,7 +218,7 @@ 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_id = hv_apicid_to_vp_id(apicid);
>>
>>   	if (vp_id < 0) {
>>   		pr_err("Couldn't find CPU with APIC ID %d\n", apicid);
>> diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c
>> index c0039a90e9e0..e3c32bb0d0cf 100644
>> --- a/arch/x86/hyperv/ivm.c
>> +++ b/arch/x86/hyperv/ivm.c
>> @@ -288,7 +288,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 +297,17 @@ 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 vp_id;
>>
>>   	if (!vmsa)
>>   		return -ENOMEM;
>>
>> +	vp_id = hv_apicid_to_vp_id(apic_id);
>> +
>> +	/* The BSP or an error */
>> +	if (vp_id <= 0)
> 
> Returning an error on value 0 may be problematic here. Consider
> the panic case where a CPU other than the BSP takes a panic and
> initiates kdump. If the kdump kernel runs with more than 1 CPU, it
> may try to start the CPU that was originally the BSP. To my
> knowledge, SEV-SNP guests on Hyper-V don't support kdump at
> the moment so this problem is currently theoretical, but let's not
> leave a potential future problem by excluding 0 here.
> 
> Also, since I assert that we really don't know anything about the
> VP index values, we can't exclude 0.  It may or may not be the
> original BSP.
> 

I believed that the BSP is always 0 yet as long as that's not in TLFS,
that's not true, I agree on that. Probably not this function's job to
check that the processor shouldn't be attempted to start, will fix!

> Michael
> 
>> +		return -EINVAL;
>> +
>>   	native_store_gdt(&gdtr);
>>
>>   	vmsa->gdtr.base = gdtr.address;
>> @@ -348,7 +355,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_id;
>>   	start_vp_input->target_vtl.target_vtl = ms_hyperv.vtl;
>>   	*(u64 *)&start_vp_input->vp_context = __pa(vmsa) | 1;
>>
>> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
>> index 07aadf0e839f..ae62a34bfd1e 100644
>> --- a/arch/x86/include/asm/mshyperv.h
>> +++ b/arch/x86/include/asm/mshyperv.h
>> @@ -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)
>> @@ -329,6 +329,7 @@ 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; }
>>   #endif /* CONFIG_HYPERV */
>>
>> +int hv_apicid_to_vp_id(u32 apic_id);
>>
>>   #ifdef CONFIG_HYPERV_VTL_MODE
>>   void __init hv_vtl_init_platform(void);
>>
>> base-commit: 628cc040b3a2980df6032766e8ef0688e981ab95
>> --
>> 2.43.0
>>
> 

-- 
Thank you,
Roman


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ