Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v21 08/13] mfd: core: Add firmware-node support to MFD cells
From: Bartosz Golaszewski @ 2026-04-29  9:50 UTC (permalink / raw)
  To: Shivendra Pratap
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Florian Fainelli, Krzysztof Kozlowski,
	Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
	Kathiravan Thirumoorthy, Srinivas Kandagatla, Sebastian Reichel,
	Mark Rutland, Lorenzo Pieralisi, Rafael J. Wysocki,
	Daniel Lezcano, Christian Loehle, Ulf Hansson, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty, Andy Yan,
	Matthias Brugger, John Stultz, Moritz Fischer, Sudeep Holla
In-Reply-To: <ce92a493-5016-41a2-8337-d2227cdf760a@oss.qualcomm.com>

On Wed, Apr 29, 2026 at 11:46 AM Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> wrote:
>
> >>
> >> If set, it would be called from MFD core and would do the name lookup
> >> and return the fwnode which would then be assigned to the cell device?
> >
>
> Or should we just drop this patch for fwnode in mfd-core?
>
> Export a new call in reboot-mode framework as:
> reboot_mode_register_node(struct reboot_mode_driver *reboot,
>                    struct device_node *np / fwnode)
>
> Use the parent node in psci-reboot-mode and find reboot-mode and call
> reboot_mode_register_node?
>

No, I think the reboot-mode device (as pointed to by the dev field of
struct reboot_mode_driver) should be associated with the right fwnode
from the start so it should be handled in MFD core.

I'm of course open to other ideas on how to do it.

Bart


^ permalink raw reply

* Re: [PATCH v2 02/15] Drivers: hv: Move hv_vp_assist_page to common files
From: Naman Jain @ 2026-04-29  9:55 UTC (permalink / raw)
  To: Michael Kelley, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
	mrigendrachaubey, linux-hyperv@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
	linux-riscv@lists.infradead.org, vdso@mailbox.org,
	ssengar@linux.microsoft.com
In-Reply-To: <SN6PR02MB4157BEAF5480D931C3756B7DD4362@SN6PR02MB4157.namprd02.prod.outlook.com>



On 4/27/2026 11:07 AM, Michael Kelley wrote:
> From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>>
>> Move the logic to initialize and export hv_vp_assist_page from x86
>> architecture code to Hyper-V common code to allow it to be used for
>> upcoming arm64 support in MSHV_VTL driver.
>> Note: This change also improves error handling - if VP assist page
>> allocation fails, hyperv_init() now returns early instead of
>> continuing with partial initialization.
>>
>> Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
>> Reviewed-by: Roman Kisel <vdso@mailbox.org>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> ---
>>   arch/x86/hyperv/hv_init.c       | 88 +-----------------------------
>>   arch/x86/include/asm/mshyperv.h | 14 -----
>>   drivers/hv/hv_common.c          | 94 ++++++++++++++++++++++++++++++++-
>>   include/asm-generic/mshyperv.h  | 16 ++++++
>>   include/hyperv/hvgdk_mini.h     |  6 ++-
>>   5 files changed, 115 insertions(+), 103 deletions(-)
>>
>> diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
>> index 323adc93f2dc..75a98b5e451b 100644
>> --- a/arch/x86/hyperv/hv_init.c
>> +++ b/arch/x86/hyperv/hv_init.c
>> @@ -81,9 +81,6 @@ union hv_ghcb * __percpu *hv_ghcb_pg;
>>   /* Storage to save the hypercall page temporarily for hibernation */
>>   static void *hv_hypercall_pg_saved;
>>
>> -struct hv_vp_assist_page **hv_vp_assist_page;
>> -EXPORT_SYMBOL_GPL(hv_vp_assist_page);
>> -
>>   static int hyperv_init_ghcb(void)
>>   {
>>   	u64 ghcb_gpa;
>> @@ -117,59 +114,12 @@ 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;
>>   	int ret;
>>
>>   	ret = hv_common_cpu_init(cpu);
>>   	if (ret)
>>   		return ret;
>>
>> -	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
>> -		 * page, instead of allocating a new page.
>> -		 */
>> -		rdmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
>> -		*hvp = memremap(msr.pfn << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT,
>> -				PAGE_SIZE, MEMREMAP_WB);
>> -	} else {
>> -		/*
>> -		 * The VP assist page is an "overlay" page (see Hyper-V TLFS's
>> -		 * Section 5.2.1 "GPA Overlay Pages"). Here it must be zeroed
>> -		 * out to make sure we always write the EOI MSR in
>> -		 * hv_apic_eoi_write() *after* the EOI optimization is disabled
>> -		 * in hv_cpu_die(), otherwise a CPU may not be stopped in the
>> -		 * case of CPU offlining and the VM will hang.
>> -		 */
>> -		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);
>> -
>> -	}
>> -	if (!WARN_ON(!(*hvp))) {
>> -		msr.enable = 1;
>> -		wrmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
>> -	}
>> -
>>   	/* Allow Hyper-V stimer vector to be injected from Hypervisor. */
>>   	if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE)
>>   		apic_update_vector(cpu, HYPERV_STIMER0_VECTOR, true);
>> @@ -286,23 +236,6 @@ static int hv_cpu_die(unsigned int cpu)
>>
>>   	hv_common_cpu_die(cpu);
>>
>> -	if (hv_vp_assist_page && hv_vp_assist_page[cpu]) {
>> -		union hv_vp_assist_msr_contents msr = { 0 };
>> -		if (hv_root_partition()) {
>> -			/*
>> -			 * For root partition the VP assist page is mapped to
>> -			 * hypervisor provided page, and thus we unmap the
>> -			 * page here and nullify it, so that in future we have
>> -			 * correct page address mapped in hv_cpu_init.
>> -			 */
>> -			memunmap(hv_vp_assist_page[cpu]);
>> -			hv_vp_assist_page[cpu] = NULL;
>> -			rdmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
>> -			msr.enable = 0;
>> -		}
>> -		wrmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
>> -	}
>> -
>>   	if (hv_reenlightenment_cb == NULL)
>>   		return 0;
>>
>> @@ -460,21 +393,6 @@ void __init hyperv_init(void)
>>   	if (hv_common_init())
>>   		return;
>>
>> -	/*
>> -	 * 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 = kzalloc_objs(*hv_vp_assist_page, nr_cpu_ids);
>> -	if (!hv_vp_assist_page) {
>> -		ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
>> -
>> -		if (!hv_isolation_type_tdx())
>> -			goto common_free;
>> -	}
>> -
>>   	if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
>>   		/* Negotiate GHCB Version. */
>>   		if (!hv_ghcb_negotiate_protocol())
>> @@ -483,7 +401,7 @@ void __init hyperv_init(void)
>>
>>   		hv_ghcb_pg = alloc_percpu(union hv_ghcb *);
>>   		if (!hv_ghcb_pg)
>> -			goto free_vp_assist_page;
>> +			goto free_ghcb_page;
> 
> Seems like this should be "goto common_free". The allocation of
> hv_ghcb_pg has failed, so going to a label where hv_ghcb_pg is
> freed seems redundant. It works since free_percpu() checks for
> a NULL argument, but it's a bit unexpected since the common_free
> label is already there.

Thanks for catching this, I'll fix it.


> 
>>   	}
>>
>>   	cpuhp = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "x86/hyperv_init:online",
>> @@ -613,10 +531,6 @@ void __init hyperv_init(void)
>>   	cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
>>   free_ghcb_page:
>>   	free_percpu(hv_ghcb_pg);
>> -free_vp_assist_page:
>> -	kfree(hv_vp_assist_page);
>> -	hv_vp_assist_page = NULL;
>> -common_free:
>>   	hv_common_free();
>>   }
>>
>> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
>> index f64393e853ee..95b452387969 100644
>> --- a/arch/x86/include/asm/mshyperv.h
>> +++ b/arch/x86/include/asm/mshyperv.h
>> @@ -155,16 +155,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
>>   	return _hv_do_fast_hypercall16(control, input1, input2);
>>   }
>>
>> -extern struct hv_vp_assist_page **hv_vp_assist_page;
>> -
>> -static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
>> -{
>> -	if (!hv_vp_assist_page)
>> -		return NULL;
>> -
>> -	return hv_vp_assist_page[cpu];
>> -}
>> -
>>   void __init hyperv_init(void);
>>   void hyperv_setup_mmu_ops(void);
>>   void set_hv_tscchange_cb(void (*cb)(void));
>> @@ -254,10 +244,6 @@ static inline void hyperv_setup_mmu_ops(void) {}
>>   static inline void set_hv_tscchange_cb(void (*cb)(void)) {}
>>   static inline void clear_hv_tscchange_cb(void) {}
>>   static inline void hyperv_stop_tsc_emulation(void) {};
>> -static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
>> -{
>> -	return NULL;
>> -}
>>   static inline int hyperv_flush_guest_mapping(u64 as) { return -1; }
>>   static inline int hyperv_flush_guest_mapping_range(u64 as,
>>   		hyperv_fill_flush_list_func fill_func, void *data)
>> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
>> index 6b67ac616789..e8633bc51d56 100644
>> --- a/drivers/hv/hv_common.c
>> +++ b/drivers/hv/hv_common.c
>> @@ -28,7 +28,11 @@
>>   #include <linux/slab.h>
>>   #include <linux/dma-map-ops.h>
>>   #include <linux/set_memory.h>
>> +#include <linux/vmalloc.h>
>> +#include <linux/io.h>
>> +#include <linux/hyperv.h>
>>   #include <hyperv/hvhdk.h>
>> +#include <hyperv/hvgdk.h>
>>   #include <asm/mshyperv.h>
>>
>>   u64 hv_current_partition_id = HV_PARTITION_ID_SELF;
>> @@ -78,6 +82,8 @@ static struct ctl_table_header *hv_ctl_table_hdr;
>>   u8 * __percpu *hv_synic_eventring_tail;
>>   EXPORT_SYMBOL_GPL(hv_synic_eventring_tail);
>>
>> +struct hv_vp_assist_page **hv_vp_assist_page;
>> +EXPORT_SYMBOL_GPL(hv_vp_assist_page);
>>   /*
>>    * Hyper-V specific initialization and shutdown code that is
>>    * common across all architectures.  Called from architecture
>> @@ -92,6 +98,9 @@ void __init hv_common_free(void)
>>   	if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE)
>>   		hv_kmsg_dump_unregister();
>>
>> +	kfree(hv_vp_assist_page);
>> +	hv_vp_assist_page = NULL;
>> +
>>   	kfree(hv_vp_index);
>>   	hv_vp_index = NULL;
>>
>> @@ -394,6 +403,23 @@ int __init hv_common_init(void)
>>   	for (i = 0; i < nr_cpu_ids; i++)
>>   		hv_vp_index[i] = VP_INVAL;
>>
>> +	/*
>> +	 * 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;
>> +#ifdef CONFIG_X86_64
>> +		ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
>> +#endif
> 
> I realize that this #ifdef went away for the reason I flagged in v1 of
> this patch set, but it's back again for a different reason.
> 
> Let me suggest another approach. hv_common_init() is called from
> both the x86/64 and arm64 hyperv_init() functions. Immediately after
> the call to hv_common_init() in the x86/64 hyperv_init(), test
> hv_vp_assist_page for NULL and clear
> HV_X64_ENLIGHTENED_VMCS_RECOMMENDED if it is. No #ifdef is
> needed, and x86/64 specific hackery stays under arch/x86 instead of
> being in common code.

Acked. Thanks.

> 
>> +	} else {
>> +		hv_vp_assist_page = kzalloc_objs(*hv_vp_assist_page, nr_cpu_ids);
>> +		if (!hv_vp_assist_page) {
>> +			hv_common_free();
>> +			return -ENOMEM;
>> +		}
>> +	}
>> +
>>   	return 0;
>>   }
>>
>> @@ -471,6 +497,8 @@ void __init ms_hyperv_late_init(void)
>>
>>   int hv_common_cpu_init(unsigned int cpu)
>>   {
>> +	union hv_vp_assist_msr_contents msr = { 0 };
>> +	struct hv_vp_assist_page **hvp;
>>   	void **inputarg, **outputarg;
>>   	u8 **synic_eventring_tail;
>>   	u64 msr_vp_index;
>> @@ -539,7 +567,53 @@ int hv_common_cpu_init(unsigned int cpu)
>>   						sizeof(u8), flags);
>>   		/* No need to unwind any of the above on failure here */
>>   		if (unlikely(!*synic_eventring_tail))
>> -			ret = -ENOMEM;
>> +			return -ENOMEM;
>> +	}
>> +
>> +	if (!hv_vp_assist_page)
>> +		return ret;
>> +
>> +	hvp = &hv_vp_assist_page[cpu];
>> +	if (hv_root_partition()) {
>> +		/*
>> +		 * For root partition we get the hypervisor provided VP assist
>> +		 * page, instead of allocating a new page.
>> +		 */
>> +		msr.as_uint64 = hv_get_msr(HV_MSR_VP_ASSIST_PAGE);
>> +		*hvp = memremap(msr.pfn << HV_VP_ASSIST_PAGE_ADDRESS_SHIFT,
>> +				HV_HYP_PAGE_SIZE, MEMREMAP_WB);
>> +	} else {
>> +		/*
>> +		 * The VP assist page is an "overlay" page (see Hyper-V TLFS's
>> +		 * Section 5.2.1 "GPA Overlay Pages"). Here it must be zeroed
>> +		 * out to make sure that on x86/x64, we always write the EOI MSR in
>> +		 * hv_apic_eoi_write() *after* the EOI optimization is disabled
>> +		 * in hv_cpu_die(), otherwise a CPU may not be stopped in the
>> +		 * case of CPU offlining and the VM will hang.
>> +		 */
>> +		if (!*hvp) {
>> +			*hvp = __vmalloc(HV_HYP_PAGE_SIZE, flags | __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, HV_HYP_PAGE_SIZE);
>> +			}
>> +		}
>> +
>> +		if (*hvp)
>> +			msr.pfn = page_to_hvpfn(vmalloc_to_page(*hvp));
> 
> Your Patch 0 changelog mentions adding a comment about vmalloc_to_pfn(), which
> I didn't see anywhere. I'm not sure what that comment would say, so maybe it
> became unnecessary.

I think I mixed up two things. Changelog was about your suggestion to 
add "x86/x64" in above comment about GPA Overlay Pages.
I also changed this function to page_to_hvpfn(vmalloc_to_page(*hvp)) as 
per your suggestion.
Apologies for the confusion.

> 
>> +	}
>> +	if (!WARN_ON(!(*hvp))) {
>> +		msr.enable = 1;
>> +		hv_set_msr(HV_MSR_VP_ASSIST_PAGE, msr.as_uint64);
>>   	}
>>
>>   	return ret;
>> @@ -566,6 +640,24 @@ int hv_common_cpu_die(unsigned int cpu)
>>   		*synic_eventring_tail = NULL;
>>   	}
>>
>> +	if (hv_vp_assist_page && hv_vp_assist_page[cpu]) {
>> +		union hv_vp_assist_msr_contents msr = { 0 };
>> +
>> +		if (hv_root_partition()) {
>> +			/*
>> +			 * For root partition the VP assist page is mapped to
>> +			 * hypervisor provided page, and thus we unmap the
>> +			 * page here and nullify it, so that in future we have
>> +			 * correct page address mapped in hv_cpu_init.
>> +			 */
>> +			memunmap(hv_vp_assist_page[cpu]);
>> +			hv_vp_assist_page[cpu] = NULL;
>> +			msr.as_uint64 = hv_get_msr(HV_MSR_VP_ASSIST_PAGE);
>> +			msr.enable = 0;
>> +		}
>> +		hv_set_msr(HV_MSR_VP_ASSIST_PAGE, msr.as_uint64);
>> +	}
>> +
>>   	return 0;
>>   }
>>
>> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
>> index d37b68238c97..2810aa05dc73 100644
>> --- a/include/asm-generic/mshyperv.h
>> +++ b/include/asm-generic/mshyperv.h
>> @@ -25,6 +25,7 @@
>>   #include <linux/nmi.h>
>>   #include <asm/ptrace.h>
>>   #include <hyperv/hvhdk.h>
>> +#include <hyperv/hvgdk.h>
>>
>>   #define VTPM_BASE_ADDRESS 0xfed40000
>>
>> @@ -299,6 +300,16 @@ do { \
>>   #define hv_status_debug(status, fmt, ...) \
>>   	hv_status_printk(debug, status, fmt, ##__VA_ARGS__)
>>
>> +extern struct hv_vp_assist_page **hv_vp_assist_page;
>> +
>> +static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
>> +{
>> +	if (!hv_vp_assist_page)
>> +		return NULL;
>> +
>> +	return hv_vp_assist_page[cpu];
>> +}
>> +
>>   const char *hv_result_to_string(u64 hv_status);
>>   int hv_result_to_errno(u64 status);
>>   void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
>> @@ -327,6 +338,11 @@ static inline enum hv_isolation_type hv_get_isolation_type(void)
>>   {
>>   	return HV_ISOLATION_TYPE_NONE;
>>   }
>> +
>> +static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
>> +{
>> +	return NULL;
>> +}
>>   #endif /* CONFIG_HYPERV */
>>
>>   #if IS_ENABLED(CONFIG_MSHV_ROOT)
>> diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
>> index 056ef7b6b360..c72d04cd5ae4 100644
>> --- a/include/hyperv/hvgdk_mini.h
>> +++ b/include/hyperv/hvgdk_mini.h
>> @@ -149,6 +149,7 @@ struct hv_u128 {
>>   #define HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT	12
> 
> Can this X64 specific definition of the shift be eliminated entirely,
> and a single common definition for x86/64 and arm64 be used?
> As I understand it, the MSR layout is the same on both architectures.
> The one gotcha is that kvm_hv_set_msr() would need to be updated.
> 
> HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK defined below isn't
> used anywhere, so it could go away too.  (The KVM selftest usage has
> its own definition.)
> 
> I realize these are changes to a source code file that is derived from
> Windows, and I'm not sure of the guidelines for such changes. So maybe
> these suggestions have to be ignored ....

The VP assist page definition is common to both x86 and arm64, so the 
address mask and shift can be shared. Also, I don't see the shift and 
mask definitions in the Hyper-V header so seem to be specific to in 
kernel usage.

> 
>>   #define HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK	\
>>   		(~((1ull << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT) - 1))
>> +#define HV_MSR_VP_ASSIST_PAGE              (HV_X64_MSR_VP_ASSIST_PAGE)
> 
> This is the correct file for this #define, but it should be placed down around
> line 1148 or so with the other HV_MSR_* definitions in terms of HV_X64_MSR_*
> 

Acked.

>>
>>   /* Hyper-V Enlightened VMCS version mask in nested features CPUID */
>>   #define HV_X64_ENLIGHTENED_VMCS_VERSION		0xff
>> @@ -410,6 +411,7 @@ union hv_x64_msr_hypercall_contents {
>>   #if defined(CONFIG_ARM64)
>>   #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE	BIT(8)
>>   #define HV_STIMER_DIRECT_MODE_AVAILABLE		BIT(13)
>> +#define HV_VP_ASSIST_PAGE_ADDRESS_SHIFT 12
>>   #endif /* CONFIG_ARM64 */
>>
>>   #if defined(CONFIG_X86)
>> @@ -1163,6 +1165,8 @@ enum hv_register_name {
>>   #define HV_MSR_STIMER0_CONFIG	(HV_X64_MSR_STIMER0_CONFIG)
>>   #define HV_MSR_STIMER0_COUNT	(HV_X64_MSR_STIMER0_COUNT)
>>
>> +#define HV_VP_ASSIST_PAGE_ADDRESS_SHIFT HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT
>> +
>>   #elif defined(CONFIG_ARM64) /* CONFIG_X86 */
>>
>>   #define HV_MSR_CRASH_P0		(HV_REGISTER_GUEST_CRASH_P0)
>> @@ -1185,7 +1189,7 @@ enum hv_register_name {
>>
>>   #define HV_MSR_STIMER0_CONFIG	(HV_REGISTER_STIMER0_CONFIG)
>>   #define HV_MSR_STIMER0_COUNT	(HV_REGISTER_STIMER0_COUNT)
>> -
>> +#define HV_MSR_VP_ASSIST_PAGE    (HV_REGISTER_VP_ASSIST_PAGE)
> 
> Nit: This definition is slightly mis-aligned. It has spaces where there
> should be a tab to match the similar definitions above it.
> 

Acked.

>>   #endif /* CONFIG_ARM64 */
>>
>>   union hv_explicit_suspend_register {
>> --
>> 2.43.0
>>

Regards,
Naman



^ permalink raw reply

* Re: [PATCH v2 03/15] Drivers: hv: Move vmbus_handler to common code
From: Naman Jain @ 2026-04-29  9:55 UTC (permalink / raw)
  To: Michael Kelley, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
	mrigendrachaubey, linux-hyperv@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
	linux-riscv@lists.infradead.org, vdso@mailbox.org,
	ssengar@linux.microsoft.com
In-Reply-To: <SN6PR02MB4157E3B0A6F76E4686D8C3E4D4362@SN6PR02MB4157.namprd02.prod.outlook.com>



On 4/27/2026 11:08 AM, Michael Kelley wrote:
> From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>>
>> Move the vmbus_handler global variable and hv_setup_vmbus_handler()/
>> hv_remove_vmbus_handler() from arch/x86 to drivers/hv/hv_common.c.
>>
>> hv_setup_vmbus_handler() is called unconditionally in vmbus_bus_init()
>> and works for both x86 (sysvec handler) and arm64 (vmbus_percpu_isr).
>>
>> This eliminates the need for separate percpu vmbus handler setup
>> functions and __weak stubs, that are needed for adding ARM64 support
>> in MSHV_VTL driver where we need to set a custom per-cpu vmbus handler.
>>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> ---
>>   arch/x86/kernel/cpu/mshyperv.c | 12 ------------
>>   drivers/hv/hv_common.c         |  9 +++++++--
>>   drivers/hv/vmbus_drv.c         | 17 +++++++++--------
>>   include/asm-generic/mshyperv.h |  1 +
>>   4 files changed, 17 insertions(+), 22 deletions(-)
>>
>> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
>> index 89a2eb8a0722..68706ff5880e 100644
>> --- a/arch/x86/kernel/cpu/mshyperv.c
>> +++ b/arch/x86/kernel/cpu/mshyperv.c
>> @@ -145,7 +145,6 @@ void hv_set_msr(unsigned int reg, u64 value)
>>   EXPORT_SYMBOL_GPL(hv_set_msr);
>>
>>   static void (*mshv_handler)(void);
>> -static void (*vmbus_handler)(void);
>>   static void (*hv_stimer0_handler)(void);
>>   static void (*hv_kexec_handler)(void);
>>   static void (*hv_crash_handler)(struct pt_regs *regs);
>> @@ -172,17 +171,6 @@ void hv_setup_mshv_handler(void (*handler)(void))
>>   	mshv_handler = handler;
>>   }
>>
>> -void hv_setup_vmbus_handler(void (*handler)(void))
>> -{
>> -	vmbus_handler = handler;
>> -}
>> -
>> -void hv_remove_vmbus_handler(void)
>> -{
>> -	/* We have no way to deallocate the interrupt gate */
>> -	vmbus_handler = NULL;
>> -}
>> -
>>   /*
>>    * Routines to do per-architecture handling of stimer0
>>    * interrupts when in Direct Mode
>> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
>> index e8633bc51d56..eb7b0028b45d 100644
>> --- a/drivers/hv/hv_common.c
>> +++ b/drivers/hv/hv_common.c
>> @@ -758,13 +758,18 @@ bool __weak hv_isolation_type_tdx(void)
>>   }
>>   EXPORT_SYMBOL_GPL(hv_isolation_type_tdx);
>>
>> -void __weak hv_setup_vmbus_handler(void (*handler)(void))
>> +void (*vmbus_handler)(void);
>> +EXPORT_SYMBOL_GPL(vmbus_handler);
>> +
>> +void hv_setup_vmbus_handler(void (*handler)(void))
>>   {
>> +	vmbus_handler = handler;
>>   }
>>   EXPORT_SYMBOL_GPL(hv_setup_vmbus_handler);
>>
>> -void __weak hv_remove_vmbus_handler(void)
>> +void hv_remove_vmbus_handler(void)
>>   {
>> +	vmbus_handler = NULL;
>>   }
>>   EXPORT_SYMBOL_GPL(hv_remove_vmbus_handler);
> 
> I'd suggest moving hv_setup_vmbus_handler() and
> hv_remove_vmbus_handler() above or below the group
> of __weak stubs in this source code file. There's a comment
> describing the purpose of these __weak functions, and
> intermixing these two functions that are no longer __weak
> produces something of a jumble.
> 

Acked.

>>
>> diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
>> index bc4fc1951ae1..052ca8b11cee 100644
>> --- a/drivers/hv/vmbus_drv.c
>> +++ b/drivers/hv/vmbus_drv.c
>> @@ -1415,7 +1415,8 @@ EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl");
>>
>>   static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
>>   {
>> -	vmbus_isr();
>> +	if (vmbus_handler)
>> +		vmbus_handler();
> 
> Is it necessary to test vmbus_handler first? From what I can
> see, it is always set before the per-cpu interrupt is setup.

After the shuffle of hv_remove_vmbus_handler() and freeing the irq, it 
can be safely removed. When I was setting the vmbus_handler to NULL 
first, before freeing the IRQ, this was required.

> 
>>   	return IRQ_HANDLED;
>>   }
>>
>> @@ -1517,8 +1518,10 @@ static int vmbus_bus_init(void)
>>   		vmbus_irq_initialized = true;
>>   	}
>>
>> +	hv_setup_vmbus_handler(vmbus_isr);
>> +
>>   	if (vmbus_irq == -1) {
>> -		hv_setup_vmbus_handler(vmbus_isr);
>> +		/* x86: sysvec handler uses vmbus_handler directly */
>>   	} else {
>>   		ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr,
>>   				"Hyper-V VMbus", &vmbus_evt);
>> @@ -1553,9 +1556,8 @@ static int vmbus_bus_init(void)
>>   	return 0;
>>
>>   err_connect:
>> -	if (vmbus_irq == -1)
>> -		hv_remove_vmbus_handler();
>> -	else
>> +	hv_remove_vmbus_handler();
>> +	if (vmbus_irq != -1)
>>   		free_percpu_irq(vmbus_irq, &vmbus_evt);
> 
> These operations should be reordered so they are the inverse
> of how they are setup.  I.e., free_percpu_irq() first, then remove
> the VMBus handler. That's just good standard practice unless
> there's a specific reason to do the cleanup ordering differently. In
> fact, hv_remove_vmbus_handler() needs to be moved down
> to the err_setup label so it's done if request_percpu_irq()
> fails.


Acked. I will do the same for other hv_remove_vmbus_handler() as well.

> 
>>   err_setup:
>>   	if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
>> @@ -3026,9 +3028,8 @@ static void __exit vmbus_exit(void)
>>   	vmbus_connection.conn_state = DISCONNECTED;
>>   	hv_stimer_global_cleanup();
>>   	vmbus_disconnect();
>> -	if (vmbus_irq == -1)
>> -		hv_remove_vmbus_handler();
>> -	else
>> +	hv_remove_vmbus_handler();
>> +	if (vmbus_irq != -1)
>>   		free_percpu_irq(vmbus_irq, &vmbus_evt);
> 
> Ordering should be changed here as well so it is the inverse
> of how things are set up.
> 
>>   	if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
>>   		smpboot_unregister_percpu_thread(&vmbus_irq_threads);
>> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
>> index 2810aa05dc73..db183c8cfb95 100644
>> --- a/include/asm-generic/mshyperv.h
>> +++ b/include/asm-generic/mshyperv.h
>> @@ -179,6 +179,7 @@ static inline u64 hv_generate_guest_id(u64 kernel_version)
>>
>>   int hv_get_hypervisor_version(union hv_hypervisor_version_info *info);
>>
>> +extern void (*vmbus_handler)(void);
>>   void hv_setup_vmbus_handler(void (*handler)(void));
>>   void hv_remove_vmbus_handler(void);
>>   void hv_setup_stimer0_handler(void (*handler)(void));
>> --
>> 2.43.0
>>


Regards,
Naman



^ permalink raw reply

* Re: [PATCH v2 07/15] arm64: hyperv: Add support for mshv_vtl_return_call
From: Naman Jain @ 2026-04-29  9:56 UTC (permalink / raw)
  To: Mark Rutland, Marc Zyngier
  Cc: K . Y . Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
	Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H . Peter Anvin, Arnd Bergmann,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Michael Kelley, Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi,
	Sascha Bischoff, mrigendrachaubey, linux-hyperv, linux-arm-kernel,
	linux-kernel, linux-arch, linux-riscv, vdso, ssengar
In-Reply-To: <aeolHwXHFH4AnX_n@J2N7QTR9R3.cambridge.arm.com>



On 4/23/2026 7:26 PM, Mark Rutland wrote:
> On Thu, Apr 23, 2026 at 12:41:57PM +0000, Naman Jain wrote:
>> Add the arm64 variant of mshv_vtl_return_call() to support the MSHV_VTL
>> driver on arm64. This function enables the transition between Virtual
>> Trust Levels (VTLs) in MSHV_VTL when the kernel acts as a paravisor.
>>
>> Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
>> Reviewed-by: Roman Kisel <vdso@mailbox.org>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> ---
>>   arch/arm64/hyperv/Makefile        |   1 +
>>   arch/arm64/hyperv/hv_vtl.c        | 158 ++++++++++++++++++++++++++++++
>>   arch/arm64/include/asm/mshyperv.h |  13 +++
>>   arch/x86/include/asm/mshyperv.h   |   2 -
>>   drivers/hv/mshv_vtl.h             |   3 +
>>   include/asm-generic/mshyperv.h    |   2 +
>>   6 files changed, 177 insertions(+), 2 deletions(-)
>>   create mode 100644 arch/arm64/hyperv/hv_vtl.c
>>
>> diff --git a/arch/arm64/hyperv/Makefile b/arch/arm64/hyperv/Makefile
>> index 87c31c001da9..9701a837a6e1 100644
>> --- a/arch/arm64/hyperv/Makefile
>> +++ b/arch/arm64/hyperv/Makefile
>> @@ -1,2 +1,3 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   obj-y		:= hv_core.o mshyperv.o
>> +obj-$(CONFIG_HYPERV_VTL_MODE)	+= hv_vtl.o
>> diff --git a/arch/arm64/hyperv/hv_vtl.c b/arch/arm64/hyperv/hv_vtl.c
>> new file mode 100644
>> index 000000000000..59cbeb74e7b9
>> --- /dev/null
>> +++ b/arch/arm64/hyperv/hv_vtl.c
>> @@ -0,0 +1,158 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2026, Microsoft, Inc.
>> + *
>> + * Authors:
>> + *     Roman Kisel <romank@linux.microsoft.com>
>> + *     Naman Jain <namjain@linux.microsoft.com>
>> + */
>> +
>> +#include <asm/mshyperv.h>
>> +#include <asm/neon.h>
>> +#include <linux/export.h>
>> +
>> +void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0)
>> +{
>> +	struct user_fpsimd_state fpsimd_state;
>> +	u64 base_ptr = (u64)vtl0->x;
>> +
>> +	/*
>> +	 * Obtain the CPU FPSIMD registers for VTL context switch.
>> +	 * This saves the current task's FP/NEON state and allows us to
>> +	 * safely load VTL0's FP/NEON context for the hypercall.
>> +	 */
>> +	kernel_neon_begin(&fpsimd_state);
>> +
>> +	/*
>> +	 * VTL switch for ARM64 platform - managing VTL0's CPU context.
>> +	 * We explicitly use the stack to save the base pointer, and use x16
>> +	 * as our working register for accessing the context structure.
>> +	 *
>> +	 * Register Handling:
>> +	 * - X0-X17: Saved/restored (general-purpose, shared for VTL communication)
>> +	 * - X18: NOT touched - hypervisor-managed per-VTL (platform register)
>> +	 * - X19-X30: Saved/restored (part of VTL0's execution context)
>> +	 * - Q0-Q31: Saved/restored (128-bit NEON/floating-point registers, shared)
>> +	 * - SP: Not in structure, hypervisor-managed per-VTL
>> +	 *
>> +	 * X29 (FP) and X30 (LR) are in the structure and must be saved/restored
>> +	 * as part of VTL0's complete execution state.
>> +	 */
>> +	asm __volatile__ (
>> +		/* Save base pointer to stack explicitly, then load into x16 */
>> +		"str %0, [sp, #-16]!\n\t"     /* Push base pointer onto stack */
>> +		"mov x16, %0\n\t"             /* Load base pointer into x16 */
>> +		/* Volatile registers (Windows ARM64 ABI: x0-x17) */
>> +		"ldp x0, x1, [x16]\n\t"
>> +		"ldp x2, x3, [x16, #(2*8)]\n\t"
>> +		"ldp x4, x5, [x16, #(4*8)]\n\t"
>> +		"ldp x6, x7, [x16, #(6*8)]\n\t"
>> +		"ldp x8, x9, [x16, #(8*8)]\n\t"
>> +		"ldp x10, x11, [x16, #(10*8)]\n\t"
>> +		"ldp x12, x13, [x16, #(12*8)]\n\t"
>> +		"ldp x14, x15, [x16, #(14*8)]\n\t"
>> +		/* x16 will be loaded last, after saving base pointer */
>> +		"ldr x17, [x16, #(17*8)]\n\t"
>> +		/* x18 is hypervisor-managed per-VTL - DO NOT LOAD */
>> +
>> +		/* General-purpose registers: x19-x30 */
>> +		"ldp x19, x20, [x16, #(19*8)]\n\t"
>> +		"ldp x21, x22, [x16, #(21*8)]\n\t"
>> +		"ldp x23, x24, [x16, #(23*8)]\n\t"
>> +		"ldp x25, x26, [x16, #(25*8)]\n\t"
>> +		"ldp x27, x28, [x16, #(27*8)]\n\t"
>> +
>> +		/* Frame pointer and link register */
>> +		"ldp x29, x30, [x16, #(29*8)]\n\t"
>> +
>> +		/* Shared NEON/FP registers: Q0-Q31 (128-bit) */
>> +		"ldp q0, q1, [x16, #(32*8)]\n\t"
>> +		"ldp q2, q3, [x16, #(32*8 + 2*16)]\n\t"
>> +		"ldp q4, q5, [x16, #(32*8 + 4*16)]\n\t"
>> +		"ldp q6, q7, [x16, #(32*8 + 6*16)]\n\t"
>> +		"ldp q8, q9, [x16, #(32*8 + 8*16)]\n\t"
>> +		"ldp q10, q11, [x16, #(32*8 + 10*16)]\n\t"
>> +		"ldp q12, q13, [x16, #(32*8 + 12*16)]\n\t"
>> +		"ldp q14, q15, [x16, #(32*8 + 14*16)]\n\t"
>> +		"ldp q16, q17, [x16, #(32*8 + 16*16)]\n\t"
>> +		"ldp q18, q19, [x16, #(32*8 + 18*16)]\n\t"
>> +		"ldp q20, q21, [x16, #(32*8 + 20*16)]\n\t"
>> +		"ldp q22, q23, [x16, #(32*8 + 22*16)]\n\t"
>> +		"ldp q24, q25, [x16, #(32*8 + 24*16)]\n\t"
>> +		"ldp q26, q27, [x16, #(32*8 + 26*16)]\n\t"
>> +		"ldp q28, q29, [x16, #(32*8 + 28*16)]\n\t"
>> +		"ldp q30, q31, [x16, #(32*8 + 30*16)]\n\t"
>> +
>> +		/* Now load x16 itself */
>> +		"ldr x16, [x16, #(16*8)]\n\t"
>> +
>> +		/* Return to the lower VTL */
>> +		"hvc #3\n\t"
> 
> NAK to this.
> 
> * This is a non-SMCCC hypercall, which we have NAK'd in general in the
>    past for various reasons that I am not going to rehash here.
> 
> * It's not clear how this is going to be extended with necessary
>    architecture state in future (e.g. SVE, SME). This is not
>    future-proof, and I don't believe this is maintainable.
> 
> * This breaks general requirements for reliable stacktracing by
>    clobbering state (e.g. x29) that we depend upon being valid AT ALL
>    TIMES outside of entry code.
> 
> * IMO, if this needs to be saved/restored, that should happen in
>    whatever you are calling.
> 
> Mark.


Merging threads for addressing comments from Mark Rutland and Marc 
Zyngier on this patch.

Thanks for reviewing the changes. Please allow me to briefly explain the 
use case here and then address your comments.

Hyper-V's Virtual Trust Levels (VTLs) provide hardware-enforced 
isolation within a single VM, analogous to ARM TrustZone. The kernel 
runs in VTL2 (higher privilege) as a "paravisor", a security monitor 
that handles intercepts for the primary OS in VTL0 (lower privilege). 
The VTL switch (mshv_vtl_return_call) is functionally equivalent to 
KVM's guest enter/exit. It saves VTL2 state, loads VTL0's GPRs other 
registers from a shared context structure, issues hvc #3 to let VTL0 
run, and on return saves VTL0's updated state back.

Coming to the problems with the code, I have identified a few ways to 
address them.

I can put the assembly code in a separate .S file with 
SYM_FUNC_START/SYM_FUNC_END and marked as noinstr, to prevent 
ftrace/kprobes from instrumenting between the GPR load and the hvc, 
which could have corrupted VTL0 register state. This should solve x29 
clobbering, stack tracing problems.

I should use kernel_neon_begin()/kernel_neon_end() to save/restore the 
full extended FP state of the current task in VTL2. VTL0's Q0-Q31 can be 
loaded/saved separately via fpsimd_load_state()/fpsimd_save_state(). 
This way, the assembly touches none of the SIMD registers. This is 
SVE/SME-safe for VTL2's task state. VTL0 still only carries Q0-Q31 in 
the context struct, and extending to SVE, SME is a future context struct 
change, which will need Hyper-V arm64 ABI support.
This way, VTL2's callee-saved regs (x19-x28, x29, x30) are explicitly 
saved to the stack frame at the top and restored at the bottom of 
assembly code. The C caller (in hv_vtl.c) is a clean function call.

Regarding Non-SMCCC "hvc #3" call, I have a limitation here owing to the 
ABI that is defined by the Hyper-V hypervisor. Fixing this requires a 
hypervisor-side change to support SMCCC-style dispatch for VTL return. 
Until then, hvc #3 is the only working interface. Moreover there would 
be backward compatibility issues with this new ABI interface, if at all 
it is added.

Link to TLFS: 
https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/vsm#on-arm64-platforms-3

Please correct me if any of the above is incorrect or if I should be 
looking at some other existing examples to solve these problems.

Regards,
Naman


^ permalink raw reply

* Re: [PATCH v2 07/15] arm64: hyperv: Add support for mshv_vtl_return_call
From: Naman Jain @ 2026-04-29  9:56 UTC (permalink / raw)
  To: Michael Kelley, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
	mrigendrachaubey, linux-hyperv@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
	linux-riscv@lists.infradead.org, vdso@mailbox.org,
	ssengar@linux.microsoft.com
In-Reply-To: <SN6PR02MB4157C147A1B915F9B45D3B74D4362@SN6PR02MB4157.namprd02.prod.outlook.com>



On 4/27/2026 11:08 AM, Michael Kelley wrote:
> From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>>
>> Add the arm64 variant of mshv_vtl_return_call() to support the MSHV_VTL
>> driver on arm64. This function enables the transition between Virtual
>> Trust Levels (VTLs) in MSHV_VTL when the kernel acts as a paravisor.
>>
>> Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
>> Reviewed-by: Roman Kisel <vdso@mailbox.org>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> ---
>>   arch/arm64/hyperv/Makefile        |   1 +
>>   arch/arm64/hyperv/hv_vtl.c        | 158 ++++++++++++++++++++++++++++++
>>   arch/arm64/include/asm/mshyperv.h |  13 +++
>>   arch/x86/include/asm/mshyperv.h   |   2 -
>>   drivers/hv/mshv_vtl.h             |   3 +
>>   include/asm-generic/mshyperv.h    |   2 +
>>   6 files changed, 177 insertions(+), 2 deletions(-)
>>   create mode 100644 arch/arm64/hyperv/hv_vtl.c
>>
> 
> [snip]
> 
>> diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
>> index 585b23a26f1b..9eb0e5999f29 100644
>> --- a/arch/arm64/include/asm/mshyperv.h
>> +++ b/arch/arm64/include/asm/mshyperv.h
>> @@ -60,6 +60,18 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg)
>>   				ARM_SMCCC_SMC_64,		\
>>   				ARM_SMCCC_OWNER_VENDOR_HYP,	\
>>   				HV_SMCCC_FUNC_NUMBER)
>> +
>> +struct mshv_vtl_cpu_context {
>> +/*
>> + * x18 is managed by the hypervisor. It won't be reloaded from this array.
>> + * It is included here for convenience in array indexing.
>> + * 'rsvd' field serves as alignment padding so q[] starts at offset 32*8=256.
>> + */
>> +	__u64 x[31];
>> +	__u64 rsvd;
>> +	__uint128_t q[32];
>> +};
>> +
>>   #ifdef CONFIG_HYPERV_VTL_MODE
>>   /*
>>    * Get/Set the register. If the function returns `1`, that must be done via
>> @@ -69,6 +81,7 @@ static inline int hv_vtl_get_set_reg(struct hv_register_assoc *regs,
>> bool set, b
>>   {
>>   	return 1;
>>   }
>> +
> 
> This appears to be a spurious blank line being added since there
> are no other changes in the vicinity.

Acked.

> 
>>   #endif
>>
>>   #include <asm-generic/mshyperv.h>
>> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
>> index 08278547b84c..b4d80c9a673a 100644
>> --- a/arch/x86/include/asm/mshyperv.h
>> +++ b/arch/x86/include/asm/mshyperv.h
>> @@ -286,7 +286,6 @@ struct mshv_vtl_cpu_context {
>>   #ifdef CONFIG_HYPERV_VTL_MODE
>>   void __init hv_vtl_init_platform(void);
>>   int __init hv_vtl_early_init(void);
>> -void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
>>   void mshv_vtl_return_call_init(u64 vtl_return_offset);
>>   void mshv_vtl_return_hypercall(void);
>>   void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
>> @@ -294,7 +293,6 @@ int hv_vtl_get_set_reg(struct hv_register_assoc *regs, bool set,
>> bool shared);
>>   #else
>>   static inline void __init hv_vtl_init_platform(void) {}
>>   static inline int __init hv_vtl_early_init(void) { return 0; }
>> -static inline void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
>>   static inline void mshv_vtl_return_call_init(u64 vtl_return_offset) {}
>>   static inline void mshv_vtl_return_hypercall(void) {}
>>   static inline void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
>> diff --git a/drivers/hv/mshv_vtl.h b/drivers/hv/mshv_vtl.h
>> index a6eea52f7aa2..103f07371f3f 100644
>> --- a/drivers/hv/mshv_vtl.h
>> +++ b/drivers/hv/mshv_vtl.h
>> @@ -22,4 +22,7 @@ struct mshv_vtl_run {
>>   	char vtl_ret_actions[MSHV_MAX_RUN_MSG_SIZE];
>>   };
>>
>> +static_assert(sizeof(struct mshv_vtl_cpu_context) <= 1024,
>> +	      "struct mshv_vtl_cpu_context exceeds reserved space in struct
>> mshv_vtl_run");
>> +
>>   #endif /* _MSHV_VTL_H */
>> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
>> index db183c8cfb95..8cdf2a9fbdfb 100644
>> --- a/include/asm-generic/mshyperv.h
>> +++ b/include/asm-generic/mshyperv.h
>> @@ -396,8 +396,10 @@ static inline int hv_deposit_memory(u64 partition_id, u64 status)
>>
>>   #if IS_ENABLED(CONFIG_HYPERV_VTL_MODE)
>>   u8 __init get_vtl(void);
>> +void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
>>   #else
>>   static inline u8 get_vtl(void) { return 0; }
>> +static inline void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
> 
> Is this stub needed? Maybe I missed something, but it looks to me like none
> of the code that calls this gets built unless CONFIG_HYPERV_VTL_MODE is set.
> See further comments about stubs in Patch 8 of this series.
> 

Config dependencies would handle such cases, and this is not required. I 
saw similar stubs added in the code, so I thought this is a norm that 
should be followed, and not rely on config dependencies.
I can remove it.

Regards,
Naman



^ permalink raw reply

* Re: [PATCH v2 08/15] Drivers: hv: Move hv_call_(get|set)_vp_registers() declarations
From: Naman Jain @ 2026-04-29  9:57 UTC (permalink / raw)
  To: Michael Kelley, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
	mrigendrachaubey, linux-hyperv@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
	linux-riscv@lists.infradead.org, vdso@mailbox.org,
	ssengar@linux.microsoft.com
In-Reply-To: <SN6PR02MB4157852404B5258EF13A5450D4362@SN6PR02MB4157.namprd02.prod.outlook.com>



On 4/27/2026 11:09 AM, Michael Kelley wrote:
> From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>>
>> Move hv_call_get_vp_registers() and hv_call_set_vp_registers()
>> declarations from drivers/hv/mshv.h to include/asm-generic/mshyperv.h.
>>
>> These functions are defined in mshv_common.c and are going to be called
>> from both drivers/hv/ and arch/x86/hyperv/hv_vtl.c. The latter never
>> included mshv.h, relying on implicit declaration visibility. Moving the
>> declarations to the arch-generic Hyper-V header makes them properly
>> visible to all architecture-specific callers.
>>
>> Provide static inline stubs returning -EOPNOTSUPP when neither
>> CONFIG_MSHV_ROOT nor CONFIG_MSHV_VTL is enabled.
> 
> Looking at the drivers/hv/Kconfig, it's possible to build with
> CONFIG_HYPERV_VTL_MODE=y, but not CONFIG_MSHV_VTL. In such a
> case, mshv_common.o doesn't get built, which is why the stubs are
> needed. Is such a configuration desirable for some scenarios?
> 
> I wonder if having CONFIG_HYPERV_VTL_MODE force the building of
> mshv_common.o would be a better approach. Then the stubs wouldn't
> be needed. The "ifneq" statement in drivers/hv/Makefile could use
> CONFIG_HYPERV_VTL_MODE instead of CONFIG_MSHV_VTL, and
> everything would be good since CONFIG_MSHV_VTL depends on
> CONFIG_HYPERV_VTL_MODE.
> 

This looks good. I'll try this and make the changes. In case there are 
some challenges with that, I'll revert back.


Regards,
Naman


^ permalink raw reply

* Re: [PATCH 1/7] dt-bindings: display: renesas,du: Document Renesas R-Car R8A779MD M3Le
From: Geert Uytterhoeven @ 2026-04-29  9:57 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Conor Dooley, David Airlie, Kieran Bingham,
	Krzysztof Kozlowski, Kuninori Morimoto, Laurent Pinchart,
	Magnus Damm, Maxime Ripard, Michael Turquette, Rob Herring,
	Simona Vetter, Stephen Boyd, Thomas Zimmermann, Tomi Valkeinen,
	devicetree, dri-devel, linux-clk, linux-kernel, linux-renesas-soc
In-Reply-To: <20260419193718.133174-2-marek.vasut+renesas@mailbox.org>

Hi Marek,

Thanks for your patch!

On Sun, 19 Apr 2026 at 21:37, Marek Vasut
<marek.vasut+renesas@mailbox.org> wrote:
> Extend the Renesas DU display bindings to support the Renesas R-Car
> R8A779MD M3Le SoC. This SoC is similar to R-Car R8A77965 M3-N SoC,
> except the HDMI port@1 is not present.

"and DU1 is unused." (whatever that may mean...)

>
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

> --- a/Documentation/devicetree/bindings/display/renesas,du.yaml
> +++ b/Documentation/devicetree/bindings/display/renesas,du.yaml
> @@ -42,6 +42,7 @@ properties:
>        - renesas,du-r8a779a0 # for R-Car V3U compatible DU
>        - renesas,du-r8a779g0 # for R-Car V4H compatible DU
>        - renesas,du-r8a779h0 # for R-Car V4M compatible DU
> +      - renesas,du-r8a779md # for R-Car M3Le compatible DU

I am not sure you need a new compatible value: is the DU really
different than on R-Car M3-N, or does it just lack some wiring? ...

>
>    reg:
>      maxItems: 1
> @@ -890,6 +891,75 @@ allOf:
>          - reset-names
>          - renesas,vsps
>
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            enum:
> +              - renesas,du-r8a779md
> +    then:
> +      properties:
> +        clocks:
> +          minItems: 3
> +          items:
> +            - description: Functional clock for DU0
> +            - description: Functional clock for DU1

... E.g. you still have a module clock for DU1 (the documentation
for R-Car M3Le does not mention any changes in clocks).

> +            - description: Functional clock for DU3
> +            - description: DU_DOTCLKIN0 input clock
> +            - description: DU_DOTCLKIN1 input clock

... E.g. the package is documented to have pin DU_DOTCLKIN1.

> +            - description: DU_DOTCLKIN3 input clock
> +
> +        clock-names:
> +          minItems: 3
> +          items:
> +            - const: du.0
> +            - const: du.1
> +            - const: du.3
> +            - pattern: '^dclkin\.[013]$'
> +            - pattern: '^dclkin\.[013]$'
> +            - pattern: '^dclkin\.[013]$'
> +
> +        interrupts:
> +          minItems: 3
> +          maxItems: 3

... E.g. you still have an interrupt for DU1 (the documentation
for R-Car M3Le does not mention any changes in interrupts).

> +
> +        resets:
> +          minItems: 2
> +          maxItems: 2
> +
> +        reset-names:
> +          items:
> +            - const: du.0
> +            - const: du.3
> +
> +        ports:
> +          properties:
> +            port@0:
> +              description: DPAD 0
> +            port@1: false

Perhaps port@1 can be present (and thus required), but left unconnected
instead?

> +            port@2:
> +              description: LVDS 0
> +            port@3: false
> +
> +          required:
> +            - port@0
> +            - port@2
> +
> +        renesas,cmms:
> +          minItems: 3
> +          maxItems: 3
> +
> +        renesas,vsps:
> +          minItems: 3
> +          maxItems: 3

Your arch/arm64/boot/dts/renesas/r8a779md.dtsi only has two entries
in renesas,cmms and renesas,vsps?

> +
> +      required:
> +        - clock-names
> +        - interrupts
> +        - resets
> +        - reset-names
> +        - renesas,vsps
> +
>  additionalProperties: false
>
>  examples:

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


^ permalink raw reply

* Re: [PATCH v2 09/15] Drivers: hv: mshv_vtl: Move hv_vtl_configure_reg_page() to x86
From: Naman Jain @ 2026-04-29  9:57 UTC (permalink / raw)
  To: Michael Kelley, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
	mrigendrachaubey, linux-hyperv@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
	linux-riscv@lists.infradead.org, vdso@mailbox.org,
	ssengar@linux.microsoft.com
In-Reply-To: <SN6PR02MB4157467FDBC0203C67A67042D4362@SN6PR02MB4157.namprd02.prod.outlook.com>



On 4/27/2026 11:10 AM, Michael Kelley wrote:
> From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>>
>> Move hv_vtl_configure_reg_page() from drivers/hv/mshv_vtl_main.c to
>> arch/x86/hyperv/hv_vtl.c. The register page overlay is an x86-specific
>> feature that uses HV_X64_REGISTER_REG_PAGE, so its configuration belongs
>> in architecture-specific code.
>>
>> Move struct mshv_vtl_per_cpu and union hv_synic_overlay_page_msr to
>> include/asm-generic/mshyperv.h so they are visible to both arch and
>> driver code.
>>
>> Change the return type from void to bool so the caller can determine
>> whether the register page was successfully configured and set
>> mshv_has_reg_page accordingly.
>>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> ---
>>   arch/x86/hyperv/hv_vtl.c       | 32 ++++++++++++++++++++++
>>   drivers/hv/mshv_vtl_main.c     | 49 +++-------------------------------
>>   include/asm-generic/mshyperv.h | 17 ++++++++++++
>>   3 files changed, 53 insertions(+), 45 deletions(-)
>>

<snip>

>>   #if IS_ENABLED(CONFIG_HYPERV_VTL_MODE)
>> +/* SYNIC_OVERLAY_PAGE_MSR - internal, identical to hv_synic_simp */
> 
> This comment pre-dates your patch, but I don't understand the point
> it is trying to make. The comment is factually true, but I don't know
> why calling that out is relevant. The REG_PAGE MSR seems to be
> conceptually separate and distinct from the SIMP MSR, so the fact
> that the layouts are the same is just a coincidence. Or is there some
> relationship between the two MSRs that I'm not aware of, and the
> comment is trying (and failing?) to point out?

This was added as per suggestion from Nuno in my initial series for 
MSHV_VTL. If the reference in "identical to" is misleading, I should 
remove it.

https://lore.kernel.org/all/68143eb0-e6a7-4579-bedb-4c2ec5aaef6b@linux.microsoft.com/

Quoting:
"""
it is a generic structure that
appears to be used for several overlay page MSRs (SIMP, SIEF, etc).

But, the type doesn't appear in the hv*dk headers explicitly; it's just
used internally by the hypervisor.

I think it should be renamed with a hv_ prefix to indicate it's part of
the hypervisor ABI, and a brief comment with the provenance:

/* SYNIC_OVERLAY_PAGE_MSR - internal, identical to hv_synic_simp */
union hv_synic_overlay_page_msr {
	/* <snip> */
};
"""

> 
>> +union hv_synic_overlay_page_msr {
>> +	u64 as_uint64;
>> +	struct {
>> +		u64 enabled: 1;
>> +		u64 reserved: 11;
>> +		u64 pfn: 52;
>> +	} __packed;
>> +};
>> +
>>   u8 __init get_vtl(void);
>>   void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
>> +bool hv_vtl_configure_reg_page(struct mshv_vtl_per_cpu *per_cpu);
>>   #else
>>   static inline u8 get_vtl(void) { return 0; }
>>   static inline void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
>> +static inline bool hv_vtl_configure_reg_page(struct mshv_vtl_per_cpu *per_cpu) { return false; }
> 
> As with Patch 8, if CONFIG_HYPERV_VTL_MODE caused mshv_common.o
> to be built, this stub wouldn't be needed.
> 

Acked.


>>   #endif
>>
>>   #endif
>> --
>> 2.43.0
>>

Regards,
Naman


^ permalink raw reply

* [PATCH 0/4] drm/mediatek: fix sparse warnings
From: Louis-Alexis Eyraud @ 2026-04-29  9:58 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Simona Vetter,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu,
	Alexandre Mergnat
  Cc: kernel, dri-devel, linux-mediatek, linux-kernel, linux-arm-kernel,
	Louis-Alexis Eyraud, kernel test robot

This series fixes in the mediatek-drm driver the sparse warnings
reported by [1] and two others introduced during v6.16 development
cycle.
    
The series has compile-tested and is based on linux-next (tag:
next-2040428).
    
[1]: https://lore.kernel.org/all/202604132044.fcYjEcU8-lkp@intel.com/

Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
Louis-Alexis Eyraud (4):
      drm/mediatek: mtk_hdmi_ddc_v2: Fix non-static global variable
      drm/mediatek: mtk_hdmi_v2: Fix non-static global variable
      drm/mediatek: mtk_cec: Fix non-static global variable
      drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable

 drivers/gpu/drm/mediatek/mtk_cec.c         | 2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c    | 2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c | 2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi_v2.c     | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)
---
base-commit: 0f3ef35b0747832b58b97171bad99011d084c6b1
change-id: 20260424-mediatek-drm-fix-sparse-warnings-7404de18204c

Best regards,
-- 
Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>



^ permalink raw reply

* [PATCH 1/4] drm/mediatek: mtk_hdmi_ddc_v2: Fix non-static global variable
From: Louis-Alexis Eyraud @ 2026-04-29  9:58 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Simona Vetter,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu,
	Alexandre Mergnat
  Cc: kernel, dri-devel, linux-mediatek, linux-kernel, linux-arm-kernel,
	Louis-Alexis Eyraud, kernel test robot
In-Reply-To: <20260429-mediatek-drm-fix-sparse-warnings-v1-0-d95c4d118b83@collabora.com>

The struct 'mtk_hdmi_ddc_v2_driver' is not used outside of the
mtk_hdmi_ddc_v2.c file, so make it static to silence sparse warning:
```
drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c:392:24: sparse: warning:
  symbol 'mtk_hdmi_ddc_v2_driver' was not declared. Should it be
  static?
```

Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202604132044.fcYjEcU8-lkp@intel.com/
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
index d937219fdb7e..31e81a6de6d8 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
@@ -389,7 +389,7 @@ static const struct of_device_id mtk_hdmi_ddc_v2_match[] = {
 };
 MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_v2_match);
 
-struct platform_driver mtk_hdmi_ddc_v2_driver = {
+static struct platform_driver mtk_hdmi_ddc_v2_driver = {
 	.probe = mtk_hdmi_ddc_v2_probe,
 	.driver = {
 		.name = "mediatek-hdmi-ddc-v2",

-- 
2.54.0



^ permalink raw reply related

* [PATCH 4/4] drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable
From: Louis-Alexis Eyraud @ 2026-04-29  9:59 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Simona Vetter,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu,
	Alexandre Mergnat
  Cc: kernel, dri-devel, linux-mediatek, linux-kernel, linux-arm-kernel,
	Louis-Alexis Eyraud
In-Reply-To: <20260429-mediatek-drm-fix-sparse-warnings-v1-0-d95c4d118b83@collabora.com>

The struct 'mtk_hdmi_ddc_driver' is not used outside of the
mtk_hdmi_ddc.c file, so make it static to silence sparse warning:
```
drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:331:24: sparse: warning: symbol
  'mtk_hdmi_ddc_driver' was not declared. Should it be static?
```

Fixes: c241118b6216 ("drm/mediatek: mtk_hdmi_ddc: Switch to register as module_platform_driver")
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
index 6358e1af69b4..2acbdb025d89 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
@@ -328,7 +328,7 @@ static const struct of_device_id mtk_hdmi_ddc_match[] = {
 };
 MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match);
 
-struct platform_driver mtk_hdmi_ddc_driver = {
+static struct platform_driver mtk_hdmi_ddc_driver = {
 	.probe = mtk_hdmi_ddc_probe,
 	.remove = mtk_hdmi_ddc_remove,
 	.driver = {

-- 
2.54.0



^ permalink raw reply related

* [PATCH 3/4] drm/mediatek: mtk_cec: Fix non-static global variable
From: Louis-Alexis Eyraud @ 2026-04-29  9:59 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Simona Vetter,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu,
	Alexandre Mergnat
  Cc: kernel, dri-devel, linux-mediatek, linux-kernel, linux-arm-kernel,
	Louis-Alexis Eyraud
In-Reply-To: <20260429-mediatek-drm-fix-sparse-warnings-v1-0-d95c4d118b83@collabora.com>

The struct 'mtk_cec_driver' is not used outside of the
mtk_cec.c file, so make it static to silence sparse warning:
```
drivers/gpu/drm/mediatek/mtk_cec.c:243:24: sparse: warning: symbol
'mtk_cec_driver' was not declared. Should it be static?
```

Fixes: 1e914a89ab7e ("drm/mediatek: mtk_cec: Switch to register as module_platform_driver")
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_cec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c
index c7be530ca041..b8ccd6e55bed 100644
--- a/drivers/gpu/drm/mediatek/mtk_cec.c
+++ b/drivers/gpu/drm/mediatek/mtk_cec.c
@@ -240,7 +240,7 @@ static const struct of_device_id mtk_cec_of_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, mtk_cec_of_ids);
 
-struct platform_driver mtk_cec_driver = {
+static struct platform_driver mtk_cec_driver = {
 	.probe = mtk_cec_probe,
 	.remove = mtk_cec_remove,
 	.driver = {

-- 
2.54.0



^ permalink raw reply related

* [PATCH 2/4] drm/mediatek: mtk_hdmi_v2: Fix non-static global variable
From: Louis-Alexis Eyraud @ 2026-04-29  9:59 UTC (permalink / raw)
  To: Chun-Kuang Hu, Philipp Zabel, David Airlie, Simona Vetter,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu,
	Alexandre Mergnat
  Cc: kernel, dri-devel, linux-mediatek, linux-kernel, linux-arm-kernel,
	Louis-Alexis Eyraud, kernel test robot
In-Reply-To: <20260429-mediatek-drm-fix-sparse-warnings-v1-0-d95c4d118b83@collabora.com>

The struct 'mtk_hdmi_v2_clk_names' is not used outside of the
mtk_hdmi_v2.c file, so make it static to silence sparse warning:
```
drivers/gpu/drm/mediatek/mtk_hdmi_v2.c:53:12: sparse: warning: symbol
'mtk_hdmi_v2_clk_names' was not declared. Should it be static?
```

Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202604132044.fcYjEcU8-lkp@intel.com/
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_hdmi_v2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
index b5c738380dc2..a8eb6fd0908b 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
@@ -50,7 +50,7 @@ enum mtk_hdmi_v2_clk_id {
 	MTK_HDMI_V2_CLK_COUNT,
 };
 
-const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = {
+static const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = {
 	[MTK_HDMI_V2_CLK_HDMI_APB_SEL] = "bus",
 	[MTK_HDMI_V2_CLK_HDCP_SEL] = "hdcp",
 	[MTK_HDMI_V2_CLK_HDCP_24M_SEL] = "hdcp24m",

-- 
2.54.0



^ permalink raw reply related

* Re: [PATCH v2 12/15] mshv_vtl: Move VSM code page offset logic to x86 files
From: Naman Jain @ 2026-04-29 10:00 UTC (permalink / raw)
  To: Michael Kelley, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
	mrigendrachaubey, linux-hyperv@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
	linux-riscv@lists.infradead.org, vdso@mailbox.org,
	ssengar@linux.microsoft.com
In-Reply-To: <SN6PR02MB4157E0525DDDD153888F5AFBD4362@SN6PR02MB4157.namprd02.prod.outlook.com>



On 4/27/2026 11:10 AM, Michael Kelley wrote:
> From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>>
>> The VSM code page offset register (HV_REGISTER_VSM_CODE_PAGE_OFFSETS)
>> is x86 specific, its value configures the static call used to return
>> to VTL0 via the hypercall page. Move the register read from the common
>> mshv_vtl_get_vsm_regs() into the x86 mshv_vtl_return_call_init(),
>> which is the sole consumer of the offset.
>>
>> Change mshv_vtl_return_call_init() from taking a u64 parameter
>> to taking no arguments, and rename mshv_vtl_get_vsm_regs() to
>> mshv_vtl_get_vsm_cap_reg() since it now only fetches
>> HV_REGISTER_VSM_CAPABILITIES.
>>
>> No functional change on x86. This prepares the common driver code for
>> ARM64 where VSM code page offsets do not apply.
>>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> ---
>>   arch/x86/hyperv/hv_vtl.c        | 19 +++++++++++++++++--
>>   arch/x86/include/asm/mshyperv.h |  4 ++--
>>   drivers/hv/mshv_vtl_main.c      | 24 +++++++++++++-----------
>>   3 files changed, 32 insertions(+), 15 deletions(-)
>>
>> diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c
>> index f3ffb6a7cb2d..7c10b34cf8a4 100644
>> --- a/arch/x86/hyperv/hv_vtl.c
>> +++ b/arch/x86/hyperv/hv_vtl.c
>> @@ -293,10 +293,25 @@ EXPORT_SYMBOL_GPL(hv_vtl_configure_reg_page);
>>
>>   DEFINE_STATIC_CALL_NULL(__mshv_vtl_return_hypercall, void (*)(void));
>>
>> -void mshv_vtl_return_call_init(u64 vtl_return_offset)
>> +int mshv_vtl_return_call_init(void)
>>   {
>> +	struct hv_register_assoc vsm_pg_offset_reg;
>> +	union hv_register_vsm_page_offsets offsets;
>> +	int ret;
>> +
>> +	vsm_pg_offset_reg.name = HV_REGISTER_VSM_CODE_PAGE_OFFSETS;
>> +
>> +	ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF,
>> +				       1, input_vtl_zero, &vsm_pg_offset_reg);
>> +	if (ret)
>> +		return ret;
>> +
>> +	offsets.as_uint64 = vsm_pg_offset_reg.value.reg64;
>> +
>>   	static_call_update(__mshv_vtl_return_hypercall,
>> -			   (void *)((u8 *)hv_hypercall_pg + vtl_return_offset));
>> +			   (void *)((u8 *)hv_hypercall_pg + offsets.vtl_return_offset));
>> +
>> +	return 0;
>>   }
>>   EXPORT_SYMBOL(mshv_vtl_return_call_init);
>>
>> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
>> index b4d80c9a673a..b48f115c1292 100644
>> --- a/arch/x86/include/asm/mshyperv.h
>> +++ b/arch/x86/include/asm/mshyperv.h
>> @@ -286,14 +286,14 @@ struct mshv_vtl_cpu_context {
>>   #ifdef CONFIG_HYPERV_VTL_MODE
>>   void __init hv_vtl_init_platform(void);
>>   int __init hv_vtl_early_init(void);
>> -void mshv_vtl_return_call_init(u64 vtl_return_offset);
>> +int mshv_vtl_return_call_init(void);
>>   void mshv_vtl_return_hypercall(void);
>>   void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
>>   int hv_vtl_get_set_reg(struct hv_register_assoc *regs, bool set, bool shared);
>>   #else
>>   static inline void __init hv_vtl_init_platform(void) {}
>>   static inline int __init hv_vtl_early_init(void) { return 0; }
>> -static inline void mshv_vtl_return_call_init(u64 vtl_return_offset) {}
>> +static inline int mshv_vtl_return_call_init(void) { return 0; }
>>   static inline void mshv_vtl_return_hypercall(void) {}
>>   static inline void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
>>   #endif
>> diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c
>> index 4c9ae65ad3e8..be498c9234fd 100644
>> --- a/drivers/hv/mshv_vtl_main.c
>> +++ b/drivers/hv/mshv_vtl_main.c
>> @@ -79,7 +79,6 @@ struct mshv_vtl {
>>   };
>>
>>   static struct mutex mshv_vtl_poll_file_lock;
>> -static union hv_register_vsm_page_offsets mshv_vsm_page_offsets;
>>   static union hv_register_vsm_capabilities mshv_vsm_capabilities;
>>
>>   static DEFINE_PER_CPU(struct mshv_vtl_poll_file, mshv_vtl_poll_file);
>> @@ -203,21 +202,19 @@ static void mshv_vtl_synic_enable_regs(unsigned int cpu)
>>   	/* VTL2 Host VSP SINT is (un)masked when the user mode requests that */
>>   }
>>
>> -static int mshv_vtl_get_vsm_regs(void)
>> +static int mshv_vtl_get_vsm_cap_reg(void)
>>   {
>> -	struct hv_register_assoc registers[2];
>> -	int ret, count = 2;
>> +	struct hv_register_assoc vsm_capability_reg;
>> +	int ret;
>>
>> -	registers[0].name = HV_REGISTER_VSM_CODE_PAGE_OFFSETS;
>> -	registers[1].name = HV_REGISTER_VSM_CAPABILITIES;
>> +	vsm_capability_reg.name = HV_REGISTER_VSM_CAPABILITIES;
>>
>>   	ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF,
>> -				       count, input_vtl_zero, registers);
>> +				       1, input_vtl_zero, &vsm_capability_reg);
>>   	if (ret)
>>   		return ret;
>>
>> -	mshv_vsm_page_offsets.as_uint64 = registers[0].value.reg64;
>> -	mshv_vsm_capabilities.as_uint64 = registers[1].value.reg64;
>> +	mshv_vsm_capabilities.as_uint64 = vsm_capability_reg.value.reg64;
>>
>>   	return ret;
> 
> Nit: This could be just "return 0".

Acked.

> 
>>   }
>> @@ -1139,13 +1136,18 @@ static int __init mshv_vtl_init(void)
>>   	tasklet_init(&msg_dpc, mshv_vtl_sint_on_msg_dpc, 0);
>>   	init_waitqueue_head(&fd_wait_queue);
>>
>> -	if (mshv_vtl_get_vsm_regs()) {
>> +	if (mshv_vtl_get_vsm_cap_reg()) {
>>   		dev_emerg(dev, "Unable to get VSM capabilities !!\n");
> 
> Why is this failure an emergency message, while the other failures
> here in mshv_vtl_init() are just error messages? When there's lack
> of consistency, I always wonder if there is a reason ..... :-)

It might be because I didn’t pay enough attention to the old code :)
dev_err() should work just fine, I'll change it.


> 
>>   		ret = -ENODEV;
>>   		goto free_dev;
>>   	}
>>
>> -	mshv_vtl_return_call_init(mshv_vsm_page_offsets.vtl_return_offset);
>> +	ret = mshv_vtl_return_call_init();
>> +	if (ret) {
>> +		dev_err(dev, "mshv_vtl_return_call_init failed: %d\n", ret);
>> +		goto free_dev;
>> +	}
>> +
>>   	ret = hv_vtl_setup_synic();
>>   	if (ret)
>>   		goto free_dev;
>> --
>> 2.43.0
>>

Regards,
Naman


^ permalink raw reply

* Re: [PATCH 2/7] drm/rcar-du: Add support for Renesas R-Car R8A779MD M3Le
From: Geert Uytterhoeven @ 2026-04-29 10:10 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Conor Dooley, David Airlie, Kieran Bingham,
	Krzysztof Kozlowski, Kuninori Morimoto, Laurent Pinchart,
	Magnus Damm, Maxime Ripard, Michael Turquette, Rob Herring,
	Simona Vetter, Stephen Boyd, Thomas Zimmermann, Tomi Valkeinen,
	devicetree, dri-devel, linux-clk, linux-kernel, linux-renesas-soc
In-Reply-To: <20260419193718.133174-3-marek.vasut+renesas@mailbox.org>

Hi Marek,

Thanks for your patch!

On Sun, 19 Apr 2026 at 21:37, Marek Vasut
<marek.vasut+renesas@mailbox.org> wrote:
> Add support the Renesas R-Car R8A779MD M3Le SoC. This SoC is
> similar to R-Car R8A77965 M3-N SoC, except the HDMI port@1 is
> not present.

"and DU1 is unused." (whatever that may mean...)

>
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

> --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
> +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
> @@ -564,6 +564,30 @@ static const struct rcar_du_device_info rcar_du_r8a779h0_info = {
>         .dsi_clk_mask = BIT(0),
>  };
>
> +static const struct rcar_du_device_info rcar_du_r8a779md_info = {

Assuming for this review we do need a new compatible value...

> +       .gen = 3,
> +       .features = RCAR_DU_FEATURE_CRTC_IRQ
> +                 | RCAR_DU_FEATURE_CRTC_CLOCK
> +                 | RCAR_DU_FEATURE_VSP1_SOURCE
> +                 | RCAR_DU_FEATURE_INTERLACED
> +                 | RCAR_DU_FEATURE_TVM_SYNC,
> +       .channels_mask = BIT(1) | BIT(0),

 "BIT(3) | BIT(0)", given R-Car M3Le dropped DU1, not DU3?

> +       .routes = {
> +               /* R8A779MD has one RGB output and one LVDS output. */
> +               [RCAR_DU_OUTPUT_DPAD0] = {
> +                       .possible_crtcs = BIT(1),
> +                       .port = 0,
> +               },
> +               [RCAR_DU_OUTPUT_LVDS0] = {
> +                       .possible_crtcs = BIT(0),
> +                       .port = 2,
> +               },
> +       },
> +       .num_lvds = 1,
> +       .num_rpf = 5,
> +       .dpll_mask = BIT(1),
> +};
> +
>  static const struct of_device_id rcar_du_of_table[] = {
>         { .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info },
>         { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },

I also don't know where "M3Le does not support 4K output." (which is
a VSP2 limitation) is to be handled.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


^ permalink raw reply

* Re: [PATCH 07/43] KVM: arm64: gic-v5: Create & manage VM and VPE tables
From: Marc Zyngier @ 2026-04-29 10:25 UTC (permalink / raw)
  To: Sascha Bischoff
  Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	kvm@vger.kernel.org, nd, oliver.upton@linux.dev, Joey Gouly,
	Suzuki Poulose, yuzenghui@huawei.com, peter.maydell@linaro.org,
	lpieralisi@kernel.org, Timothy Hayes
In-Reply-To: <20260427160547.3129448-8-sascha.bischoff@arm.com>

On Mon, 27 Apr 2026 17:08:25 +0100,
Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> 
> GICv5 uses a set of in-memory tables to track and manage VM
> state. These must be allocated by the hypervisor, and provided to the
> IRS to use.
> 
> The VMT (Virtual Machine Table) is a linear or two level table
> comprising VMT Entries (VMTE). Each VMTE describes the state for a
> single VM. This state includes things such as the SPI and LPI IST
> configuration (coming in a future commit), an implementation-defined
> VM Descriptor, and a VPE Table (VPET).
> 
> The VPET contains one entry per VPE belonging to a VM, and is used to
> mark a VPE as valid, as well as providing the address of an
> implementation-defined VPE Descriptor, which is used by the hardware
> to track and manage VPE state.
> 
> This commit adds support for allocating the VMT, and managing the
> VMTEs. The VMTEs can be initialised or released for re-use. Allocation
> and tracking of unused VMTEs is handled with an IDA.
> 
> Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> ---
>  arch/arm64/kvm/vgic/vgic-v5-tables.c | 628 +++++++++++++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-v5-tables.h | 108 +++++
>  include/kvm/arm_vgic.h               |   2 +
>  include/linux/irqchip/arm-gic-v5.h   |  13 +
>  4 files changed, 751 insertions(+)
> 
> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.c b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> index 30e2b108b1aa3..502d05d46cccf 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.c
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> @@ -3,6 +3,634 @@
>   * Copyright (C) 2025, 2026 Arm Ltd.
>   */
>  
> +#include <kvm/arm_vgic.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/sizes.h>
> +#include <linux/slab.h>
> +#include <linux/xarray.h>
> +#include <asm/kvm_mmu.h>
> +
> +#include "vgic.h"
>  #include "vgic-v5-tables.h"
>  
>  struct vgic_v5_host_ist_caps gicv5_host_ist_caps;
> +
> +static struct vgic_v5_vmt *vmt_info;
> +DEFINE_XARRAY(vm_info);

Can this be made static?

> +
> +static bool vgic_v5_vmt_allocated(void)
> +{
> +	return vmt_info != NULL;
> +}
> +
> +static int vgic_v5_check_vm_id(u16 vm_id)
> +{
> +	if (vm_id >= vmt_info->num_entries)
> +		return -EINVAL;
> +
> +	return 0;
> +}

Under what circumstance do we have to issue these checks? This looks
like debug code to me.

> +
> +/*
> + * Our IRS might be coherent or non-coherent. If coherent, we can just emit a
> + * DSB to ensure that we're in sync. However, when non-coherent, we need to
> + * manage our cached data explicitly.
> + *
> + * This helper is used to handle both coherent and non-coherent IRSes, and
> + * handles all combinations of cleaning and invalidating to the PoC.
> + */
> +static void vgic_v5_clean_inval(void *va, size_t size, bool clean, bool inval)
> +{
> +	unsigned long base = (unsigned long)va;
> +
> +	/* Catch any accidental NOPs */
> +	BUILD_BUG_ON(!(clean || inval));
> +
> +	/* Coherent; emit DSB. */
> +	if (!gicv5_host_ist_caps.irs_non_coherent) {
> +		dsb(ishst);
> +		return;
> +	}

You need a DSB in all situations. Otherwise, the subsequent CMOs are
not ordered.

> +
> +	if (clean && inval)
> +		dcache_clean_inval_poc(base, base + size);
> +	else if (clean)
> +		dcache_clean_poc(base, base + size);
> +	else if (inval)
> +		dcache_inval_poc(base, base + size);

You could also do yourself a favour and just use clean+invalidate,
which conveniently works everywhere. There is very little point in
spreading that complexity all over the map.

> +}
> +
> +/*
> + * Create a linear VM table, rounding up the number of entries to at least one
> + * whole page to give us nicer alignment.

Define nicer. What are the constraints? Does GICv5 have a different
notion of "page size" from the CPU like on v3?

> + *
> + * Note: We don't update the number of entries tracked in our tracking structure
> + * as this might be higher than the number of bits supported by the HW.
> + */
> +static int vgic_v5_alloc_vmt_linear(unsigned int num_entries)
> +{
> +	unsigned int l2_entries_per_page;
> +	size_t alloc_size;
> +
> +	/* Potentially throw away a bit of memory for the sake of alignment! */
> +	l2_entries_per_page = PAGE_SIZE / GICV5_VMTEL2E_SIZE;
> +	if (num_entries < l2_entries_per_page)
> +		num_entries = l2_entries_per_page;

Don't you want to allocate full pages, irrespective of the number of
entries? I don't see the reason why you'd treat the first page
differently from the rest.

> +
> +	alloc_size = num_entries * sizeof(struct vmtl2_entry);
> +
> +	vmt_info->linear.vmt_base = kzalloc(alloc_size, GFP_KERNEL);

Consider using the new allocation helpers:

	vmt_info->vmt_base = kzalloc_objs(struct vmtl2_entry, num_entries, GFP_KERNEL)

But if you are supposed to use full pages anyway, you might as well
switch to the page allocator.

> +	if (vmt_info->linear.vmt_base == NULL)
> +		return -ENOMEM;
> +
> +	vgic_v5_clean_inval(vmt_info->linear.vmt_base, alloc_size, true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Allocate the first level of a two-level VM table. The second-level VM tables
> + * are allocated on demand (by vgic_v5_alloc_l2_vmt()).
> + *
> + * Note: If there are too few entries, these are rounded up to the size of an L2
> + * table (4k) to ensure sane alignment. As with the linear table, the tracked
> + * number of entries is not increased to avoid the case of going above what the
> + * hardware supports.
> + */
> +static int vgic_v5_alloc_vmt_two_level(unsigned int num_entries)
> +{
> +	size_t alloc_size;
> +
> +	/* Potentially throw away a bit of memory for the sake of alignment! */
> +	if (num_entries < GICV5_VMT_L2_TABLE_ENTRIES)
> +		num_entries = GICV5_VMT_L2_TABLE_ENTRIES;
> +
> +	/*
> +	 * Let's make sure that we always allocate a whole power of 2
> +	 * of entries. Note that we need to subtract 1 from the fls()
> +	 * result in order to give the correct number of bits as we
> +	 * are operating on a whole power of 2.
> +	 */
> +	num_entries = roundup_pow_of_two(num_entries);
> +
> +	vmt_info->l2.num_l1_ents = (num_entries / GICV5_VMT_L2_TABLE_ENTRIES);
> +	alloc_size = vmt_info->l2.num_l1_ents * sizeof(vmtl1_entry);
> +
> +	vmt_info->l2.vmt_base = kzalloc(alloc_size, GFP_KERNEL);
> +	if (vmt_info->l2.vmt_base == NULL)
> +		return -ENOMEM;
> +
> +	vgic_v5_clean_inval(vmt_info->l2.vmt_base, alloc_size, true, true);
> +
> +	vmt_info->l2.l2ptrs = kzalloc_objs(*vmt_info->l2.l2ptrs,
> +					   vmt_info->l2.num_l1_ents,
> +					   GFP_KERNEL);
> +	if (vmt_info->l2.l2ptrs == NULL) {
> +		kfree(vmt_info->l2.vmt_base);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +/*
> + * Allocate a second level VMT, if required. This can be called eagerly, and
> + * will only perform the allocation if required.
> + */
> +static int vgic_v5_alloc_l2_vmt(struct kvm *kvm)
> +{
> +	unsigned int l1_index;
> +	struct vmtl2_entry *l2_table;
> +	vmtl1_entry tmp;
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
> +	struct gicv5_cmd_info cmd_info;
> +	int ret;
> +
> +	if (!vgic_v5_vmt_allocated())
> +		return -ENXIO;

Shouldn't that be checked in the caller rather than being buried in a
helper? It is also odd to check for "vmt being allocated" in a
function that "allocates a vmt". My guts feeling is that vmt_allocated
is not exactly the correct name.

> +
> +	/* Nothing to do if we have linear tables! */
> +	if (!vmt_info->two_level)
> +		return 0;
> +
> +	ret = vgic_v5_check_vm_id(vm_id);
> +	if (ret)
> +		return ret;

Why should we check the vm id at this stage? Surely we should be able
to trust the value passed around.

> +
> +	/*
> +	 * We have 4k-sized L2 tables - this is mandated by the spec for
> +	 * two-level VMTs. This means that we have 128 entries per L1 VMTE.
> +	 */
> +	l1_index = vm_id / GICV5_VMT_L2_TABLE_ENTRIES;
> +
> +	if (l1_index > vmt_info->l2.num_l1_ents)
> +		return -E2BIG;

Same for these checks. If this is something we got form the vm_id
allocator, we should be able to trust it.

> +
> +	/* Already valid? Great! */
> +	if (vmt_info->l2.l2ptrs[l1_index] != NULL)
> +		return 0;
> +
> +	l2_table = kzalloc(GICV5_VMT_L2_TABLE_SIZE, GFP_KERNEL);
> +	if (l2_table == NULL)

nit:

	if (!l2_table)

is the more idiomatic way of writing this.

> +		return -ENOMEM;
> +
> +	if (virt_to_phys(l2_table) & ~GICV5_VMTEL1E_L2_ADDR) {

Are you second-guessing the memory allocator? We have a guarantee that
the result will be aligned to the size of the next power of 2.

> +		kfree(l2_table);
> +		return -EINVAL;
> +	}
> +
> +	vmt_info->l2.l2ptrs[l1_index] = l2_table;
> +
> +	/* Alignment issue! */
> +	if (virt_to_phys(l2_table) & ~GICV5_VMTEL1E_L2_ADDR) {

Doing it twice just to be sure?

> +		kfree(l2_table);
> +		return -EFAULT;
> +	}
> +
> +	tmp = virt_to_phys(l2_table) & GICV5_VMTEL1E_L2_ADDR;

You have just verified twice that the bottom bits were 0. I sense a
certain level of paranoia! ;-)

> +	WRITE_ONCE(vmt_info->l2.vmt_base[l1_index], cpu_to_le64(tmp));
> +
> +	vgic_v5_clean_inval(l2_table, GICV5_VMT_L2_TABLE_SIZE, true, true);
> +	/* Skip inval for now - wait until table is made valid by HW */
> +	vgic_v5_clean_inval(vmt_info->l2.vmt_base + l1_index,
> +			    sizeof(vmtl1_entry), true, false);
> +
> +	/* VMAP in the L2 VMT via the IRS */
> +	cmd_info.cmd_type = VMT_L2_MAP;

cmd_info.data is left uninitialised.

> +	ret = irq_set_vcpu_affinity(vgic_v5_vpe_db(vcpu0), &cmd_info);

Who implements the irq_set_vcpu_affinity() callback? I don't see it in
any of the previous patches, and I'd like to be sure that this
callback doesn't snapshot the pointer...

It is also slightly odd to do the VM init using a vcpu-specific
handle. Not necessarily something to change, but at least document it.

> +
> +	/* We've failed to make the L2 VMT valid - things are very broken! */
> +	if (ret) {
> +		/* Remove the pointer from L1 table */
> +		WRITE_ONCE(vmt_info->l2.vmt_base[l1_index], 0);
> +
> +		kfree(l2_table);
> +		vmt_info->l2.l2ptrs[l1_index] = NULL;
> +
> +		return ret;
> +	}
> +
> +	/* Table updated; inval our copy */
> +	vgic_v5_clean_inval(vmt_info->l2.vmt_base + l1_index,
> +			    sizeof(vmtl1_entry), false, true);
> +
> +	return ret;
> +}
> +
> +/*
> + * Allocate the top-level VMT. This can either be linear or two-level.
> + */
> +int vgic_v5_vmt_allocate(bool two_level, unsigned int num_entries,
> +			 size_t vmd_size, size_t vped_size,
> +			 unsigned int max_vpes)
> +{
> +	int ret = 0;
> +
> +	if (vgic_v5_vmt_allocated())
> +		return -EBUSY;
> +
> +	/* VMD is optional; using 0 to signal that it not needed. */
> +	if (vmd_size != 0 &&
> +	    (vmd_size < VMD_MIN_SIZE || vmd_size > VMD_MAX_SIZE))
> +		return -EINVAL;
> +
> +	if (vped_size < VPED_MIN_SIZE || vped_size > VPED_MAX_SIZE)
> +		return -EINVAL;
> +
> +	/* Allocate the tracking structure */
> +	vmt_info = kzalloc_obj(*vmt_info, GFP_KERNEL);
> +	if (vmt_info == NULL)
> +		return -ENOMEM;
> +
> +	ida_init(&vmt_info->vm_id_ida);
> +	vmt_info->max_vpes = max_vpes;
> +	vmt_info->vmd_size = vmd_size;
> +	vmt_info->vped_size = vped_size;
> +	vmt_info->two_level = two_level;
> +	vmt_info->num_entries = num_entries;
> +
> +	if (!two_level)
> +		ret = vgic_v5_alloc_vmt_linear(num_entries);
> +	else
> +		ret = vgic_v5_alloc_vmt_two_level(num_entries);
> +
> +	/* If anything failed, free our tracking structure before returning */
> +	if (ret) {
> +		kfree(vmt_info);
> +		vmt_info = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * Free the VMT and associated tracking structures. This isn't strictly expected
> + * to be called in general operation, but instead exists for completeness.
> + */
> +int vgic_v5_vmt_free(void)
> +{
> +	if (!vgic_v5_vmt_allocated())
> +		return -EINVAL;
> +
> +	if (!vmt_info->two_level) {
> +		kfree(vmt_info->linear.vmt_base);
> +	} else {
> +		/* Free the L2 tables; kfree(NULL) is safe */
> +		for (int i = 0; i < vmt_info->l2.num_l1_ents; ++i)
> +			kfree(vmt_info->l2.l2ptrs[i]);
> +		kfree(vmt_info->l2.l2ptrs);
> +
> +		/* And now free the L1 table */
> +		kfree(vmt_info->l2.vmt_base);
> +	}
> +
> +	ida_destroy(&vmt_info->vm_id_ida);
> +	kfree(vmt_info);
> +	vmt_info = NULL;
> +
> +	return 0;
> +}
> +
> +/*
> + * Look up a VMT Entry by VM ID.
> + */
> +static int vgic_v5_get_l2_vmte(u16 vm_id, struct vmtl2_entry **vmte)
> +{

A much more idiomatic way to do that would be to just return a
pointer, and to encode the potential error with ERR_PTR(), checking
for IS_ERR()/PTR_ERR(). Or just return NULL on error, which is a
pretty standard behaviour.

> +	unsigned int l1_index, l2_index;
> +	struct vmtl2_entry *l2_table;
> +	int ret;
> +
> +	ret = vgic_v5_check_vm_id(vm_id);
> +	if (ret)
> +		return ret;
> +
> +	if (!vmt_info->two_level) {
> +		/* All entries always valid for Linear table */
> +		*vmte = &vmt_info->linear.vmt_base[vm_id];
> +	} else {
> +		l1_index = vm_id / GICV5_VMT_L2_TABLE_ENTRIES;
> +		l2_index = vm_id % GICV5_VMT_L2_TABLE_ENTRIES;
> +
> +		if (l1_index > vmt_info->l2.num_l1_ents)
> +			return -E2BIG;
> +
> +		if (vmt_info->l2.l2ptrs[l1_index] == NULL)
> +			return -EINVAL;
> +
> +		l2_table = vmt_info->l2.l2ptrs[l1_index];
> +		*vmte = &l2_table[l2_index];
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Zero a VMT Entry, and flush & invalidate to the PoC, if required.
> + */
> +static int vgic_v5_reset_vmte(struct kvm *kvm)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vmtl2_entry *vmte;
> +	int ret;
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	WRITE_ONCE(vmte->val[0], 0ULL);
> +	WRITE_ONCE(vmte->val[1], 0ULL);
> +	WRITE_ONCE(vmte->val[2], 0ULL);
> +	WRITE_ONCE(vmte->val[3], 0ULL);

Why the WRITE_ONCE()? Is there anything observing these writes
concurrently? Also, missing endianness conversions.

> +
> +	vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Use the IDA to allocate a new VM ID, and track it in the gicv5_vm data
> + * structure. If we're out of VM IDs, the IDA catches that, and we return the
> + * error (-ENOSPC).
> + */
> +int vgic_v5_allocate_vm_id(struct kvm *kvm)
> +{
> +	int id;
> +
> +	id = ida_alloc_max(&vmt_info->vm_id_ida, vmt_info->num_entries - 1u,
> +			   GFP_KERNEL);
> +	if (id < 0)
> +		return id;
> +
> +	kvm->arch.vgic.gicv5_vm.vm_id = id;
> +	kvm->arch.vgic.gicv5_vm.vm_id_valid = true;

Do we need this extra flag? Why can't that be a specific id value?

> +
> +	return 0;
> +}
> +
> +/*
> + * Release the VM ID to allow it to be reallocated in the future.
> + */
> +void vgic_v5_release_vm_id(struct kvm *kvm)
> +{
> +	ida_free(&vmt_info->vm_id_ida, kvm->arch.vgic.gicv5_vm.vm_id);
> +	kvm->arch.vgic.gicv5_vm.vm_id_valid = false;
> +}
> +
> +/*
> + * Initialise an entry in the VMT based on the index of the VM.
> + *
> + * Note: We don't mark the VMTE as valid as this needs to be done by
> + * the hardware.
> + */
> +int vgic_v5_vmte_init(struct kvm *kvm)
> +{
> +	int nr_cpus = atomic_read(&kvm->online_vcpus);
> +	struct vgic_v5_vm_info *vmi = NULL;
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	void *vmd = NULL, *vpet = NULL;
> +	struct vmtl2_entry *vmte;
> +	void **vped_ptrs = NULL;
> +	size_t vpet_alloc_size;
> +	int ret;
> +	u64 tmp;
> +
> +	if (nr_cpus > vmt_info->max_vpes)
> +		return -E2BIG;
> +
> +	/*
> +	 * If we're using two-level VMTs, L2 is allocated on demand. For linear
> +	 * VMTs, this is a NOP.
> +	 */
> +	if (vgic_v5_alloc_l2_vmt(kvm))
> +		return -EIO;
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	/* If the entry is already valid, something went wrong */
> +	if (FIELD_GET(GICV5_VMTEL2E_VALID, le64_to_cpu(READ_ONCE(vmte->val[0])))) {
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);
> +		return -EINVAL;
> +	}
> +
> +	ret = vgic_v5_reset_vmte(kvm);
> +	if (ret)
> +		return ret;
> +
> +	vmi = kzalloc_objs(*vmi, GFP_KERNEL);

Errr. No. For a single object, this is *kzalloc_obj*, no trailing 's'.
Here, you are allocating GFP_KERNEL objects, which may or may not be
sensible...

Also, GFP_KERNEL is the default, so you can write this as:

	vmi = kzalloc_obj(*vmi);

> +	if (vmi == NULL) {
> +		ret = -ENOMEM;
> +		goto out_fail;
> +	}
> +
> +	ret = xa_insert(&vm_info, vm_id, vmi, GFP_KERNEL);
> +	if (ret)
> +		goto out_fail;
> +
> +	/* Allocate and assign the VM Descriptor, if required. */
> +	if (vmt_info->vmd_size != 0) {
> +		vmd = kzalloc(vmt_info->vmd_size, GFP_KERNEL);

kzalloc_obj()

> +		if (!vmd) {
> +			ret = -ENOMEM;
> +			goto out_fail;
> +		}
> +
> +		/* Stash the VA so we can free it later */
> +		vmi->vmd_base = vmd;
> +
> +		tmp = FIELD_PREP(GICV5_VMTEL2E_VMD_ADDR,
> +				virt_to_phys(vmd) >>
> +				GICV5_VMTEL2E_VMD_ADDR_SHIFT);
> +		WRITE_ONCE(vmte->val[0], cpu_to_le64(tmp));
> +	}
> +
> +	/*
> +	 * Allocate and assign the VPE Table. We can only describe the number of
> +	 * VPE ID Bits in the VMTE, and therefore we round up the number of CPUs
> +	 * to a whole power of two.
> +	 */
> +	nr_cpus = roundup_pow_of_two(nr_cpus);
> +	vmi->vpe_id_bits = fls(nr_cpus) - 1;
> +
> +	vpet_alloc_size = sizeof(vpe_entry) * nr_cpus;
> +	vpet = kzalloc(vpet_alloc_size, GFP_KERNEL);
> +	if (!vpet) {
> +		ret = -ENOMEM;
> +		goto out_fail;
> +	}
> +
> +	/* Stash the VA so we can free it later */
> +	vmi->vpet_base = vpet;
> +
> +	tmp = FIELD_PREP(GICV5_VMTEL2E_VPET_ADDR,
> +			virt_to_phys(vpet) >> GICV5_VMTEL2E_VPET_ADDR_SHIFT);
> +	tmp |= FIELD_PREP(GICV5_VMTEL2E_VPE_ID_BITS, vmi->vpe_id_bits);
> +	WRITE_ONCE(vmte->val[1], cpu_to_le64(tmp));
> +
> +	vped_ptrs = kzalloc_objs(*vped_ptrs, nr_cpus, GFP_KERNEL);
> +	if (vped_ptrs == NULL) {
> +		ret = -ENOMEM;
> +		goto out_fail;
> +	}
> +	vmi->vped_ptrs = vped_ptrs;
> +
> +	if (vmd)
> +		vgic_v5_clean_inval(vmd, vmt_info->vmd_size, true, true);
> +	vgic_v5_clean_inval(vpet, vpet_alloc_size, true, true);
> +	vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);
> +
> +	kvm->arch.vgic.gicv5_vm.vmte_allocated = true;
> +
> +	return 0;
> +
> +out_fail:
> +	/* kfree(NULL) is safe so we can just kfree() at leisure */
> +	kfree(vmd);
> +	kfree(vpet);
> +	kfree(vped_ptrs);
> +	if (vmi)
> +		xa_erase(&vm_info, vm_id);
> +	kfree(vmi);
> +
> +	vgic_v5_reset_vmte(kvm);
> +
> +	return ret;
> +}
> +
> +/*
> + * Release the VMT Entry, freeing up any allocated data structures before
> + * zeroing the VMTE.
> + *
> + * The VMTE must be marked as invalid before it is released.
> + */
> +int vgic_v5_vmte_release(struct kvm *kvm)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	struct vmtl2_entry *vmte;
> +	int ret;
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	/* Reject if the VMTE has not been marked as invalid! */
> +	if (FIELD_GET(GICV5_VMTEL2E_VALID, le64_to_cpu(READ_ONCE(vmte->val[0])))) {
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);

What is this invalidation achieving?

> +		return -EINVAL;
> +	}
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		goto no_vmi;
> +
> +	kfree(vmi->vmd_base);
> +	kfree(vmi->vpet_base);
> +
> +	xa_erase(&vm_info, vm_id);
> +	kfree(vmi);
> +
> +no_vmi:
> +	/*
> +	 * If we didn't get far enough into allocating a VMTE to create the VM
> +	 * info structure, then we just zero the VMTE and move on. There's
> +	 * nothing else we can realistically do here.
> +	 */
> +	ret = vgic_v5_reset_vmte(kvm);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/*
> + * Allocate a VPE descriptor and provide it to the hardware via the VPE Table.
> + */
> +int vgic_v5_vmte_alloc_vpe(struct kvm_vcpu *vcpu)
> +{
> +	u16 vm_id = vgic_v5_vm_id(vcpu->kvm);
> +	u16 vpe_id = vgic_v5_vpe_id(vcpu);
> +	struct vgic_v5_vm_info *vmi;
> +	vpe_entry tmp, *vpet_base;
> +	void *vped;
> +
> +	/* Make sure we're not over what the hardware supports */
> +	if (vpe_id >= vmt_info->max_vpes)
> +		return -E2BIG;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		return -EINVAL;
> +
> +	if (vpe_id >= 1 << vmi->vpe_id_bits)
> +		return -E2BIG;
> +
> +	vpet_base = vmi->vpet_base;
> +
> +	/* If the VPETE for this CPU is already valid we've gone wrong */
> +	if (FIELD_GET(GICV5_VPE_VALID, le64_to_cpu(READ_ONCE(vpet_base[vpe_id])))) {
> +		vgic_v5_clean_inval(&vpet_base[vpe_id], sizeof(*vpet_base), true, true);

Same question as above.

> +		return -EBUSY;
> +	}
> +
> +	/* Alloc VPE Descriptor. Only used by IRS. */
> +	vped = kzalloc(vmt_info->vped_size, GFP_KERNEL);
> +	if (vped == NULL)
> +		return -ENOMEM;
> +
> +	vmi->vped_ptrs[vpe_id] = vped;
> +
> +	tmp = FIELD_PREP(GICV5_VPED_ADDR, virt_to_phys(vped) >> GICV5_VPED_ADDR_SHIFT);
> +	WRITE_ONCE(vpet_base[vpe_id], cpu_to_le64(tmp));
> +
> +	vgic_v5_clean_inval(vped, vmt_info->vped_size, true, true);
> +	vgic_v5_clean_inval(vpet_base + vpe_id, sizeof(vpe_entry), true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Free the memory allocated for the VPE descriptor.
> + */
> +int vgic_v5_vmte_free_vpe(struct kvm_vcpu *vcpu)
> +{
> +	u16 vm_id = vgic_v5_vm_id(vcpu->kvm);
> +	u16 vpe_id = vgic_v5_vpe_id(vcpu);
> +	struct vgic_v5_vm_info *vmi;
> +	struct vmtl2_entry *vmte;
> +	vpe_entry *vpet_base;
> +	void *vped;
> +	int ret;
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	if (FIELD_GET(GICV5_VMTEL2E_VALID, le64_to_cpu(READ_ONCE(vmte->val[0])))) {
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);

Again.

> +		return -EBUSY;
> +	}
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (!vmi)
> +		return -EINVAL;
> +
> +	if (vpe_id >= 1 << vmi->vpe_id_bits)
> +		return -E2BIG;
> +
> +	vpet_base = vmi->vpet_base;
> +	WRITE_ONCE(vpet_base[vpe_id], 0ULL);
> +
> +	vgic_v5_clean_inval(vpet_base + vpe_id, sizeof(vpe_entry), true, true);
> +
> +	/* Free VPE Descriptor. Only used by IRS. */
> +	vped = vmi->vped_ptrs[vpe_id];
> +	vmi->vped_ptrs[vpe_id] = NULL;
> +	kfree(vped);
> +
> +	return 0;
> +}

I've only very lightly went through the rest of the allocations, but
what strikes me is the distinct lack of locking and interaction with
the HW. I guess this is taken care of somewhere else?

> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.h b/arch/arm64/kvm/vgic/vgic-v5-tables.h
> index cf00a248eabd5..5501a44308362 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.h
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.h
> @@ -8,6 +8,86 @@
>  
>  #include <linux/irqchip/arm-gic-v5.h>
>  
> +#define VM_ID_BITS_MIN	8
> +#define VM_ID_BITS_MAX	16
> +#define VMD_MIN_SIZE	8
> +#define VMD_MAX_SIZE	4096
> +#define VPED_MIN_SIZE	8
> +#define VPED_MAX_SIZE	4096
> +#define VPE_ID_BITS_MIN	8
> +#define VPE_ID_BITS_MAX	16
> +
> +/* Level 1 Virtual Machine Table Entry */
> +typedef __le64 vmtl1_entry;
> +#define GICV5_VMTEL1E_VALID		BIT_ULL(0)
> +/* Note that there is no shift for the address by design */
> +#define GICV5_VMTEL1E_L2_ADDR		GENMASK(51, 12)
> +
> +#define GICV5_VMTEL2E_SIZE		32ULL
> +/* An L2 table (two-level VMT) is ALWAYS 4kB! */
> +#define GICV5_VMT_L2_TABLE_SIZE		4096ULL
> +#define GICV5_VMT_L2_TABLE_ENTRIES	(GICV5_VMT_L2_TABLE_SIZE / GICV5_VMTEL2E_SIZE)
> +
> +/* Level 2 Virtual Machine Table Entry */
> +struct vmtl2_entry {
> +	__le64 val[4];
> +};
> +
> +/*
> + * As the L2 VMTE is a large data structure, we are splitting it into 4 parts.
> + * We only mask and shift WITHIN each part for simplicity.
> + */
> +/* First 64-bit chunk */
> +#define GICV5_VMTEL2E_VALID		BIT_ULL(0)
> +#define GICV5_VMTEL2E_VMD_ADDR_SHIFT	3ULL
> +#define GICV5_VMTEL2E_VMD_ADDR		GENMASK_ULL(55, 3)
> +/* Second 64-bit chunk */
> +#define GICV5_VMTEL2E_VPET_ADDR_SHIFT	3ULL
> +#define GICV5_VMTEL2E_VPET_ADDR		GENMASK_ULL(55, 3)
> +#define GICV5_VMTEL2E_VPE_ID_BITS	GENMASK_ULL(63, 59)
> +/* Third & fourth 64-bit chunks (the encodings are the same for each) */
> +#define GICV5_VMTEL2E_IST_VALID		BIT_ULL(0)
> +#define GICV5_VMTEL2E_IST_L2SZ		GENMASK_ULL(2, 1)
> +#define GICV5_VMTEL2E_IST_ADDR_SHIFT	6ULL
> +#define GICV5_VMTEL2E_IST_ADDR		GENMASK_ULL(55, 6)
> +#define GICV5_VMTEL2E_IST_ISTSZ		GENMASK_ULL(57, 56)
> +#define GICV5_VMTEL2E_IST_STRUCTURE	BIT_ULL(58)
> +#define GICV5_VMTEL2E_IST_ID_BITS	GENMASK_ULL(63, 59)
> +
> +/* Virtual PE Table Entry */
> +typedef __le64 vpe_entry;
> +#define GICV5_VPE_VALID			BIT_ULL(0)
> +/* Note that there is no shift for the address by design. */
> +#define GICV5_VPED_ADDR_SHIFT		3ULL
> +#define GICV5_VPED_ADDR			GENMASK_ULL(55, 3)

Are these definitions used outside of the .c file? If not, consider
moving them into it.

> +
> +struct vgic_v5_vm_info {
> +	void __iomem		*vmd_base;
> +	vpe_entry __iomem	*vpet_base;
> +	void __iomem		**vped_ptrs;
> +	u8			vpe_id_bits;
> +};
> +
> +struct vgic_v5_vmt {
> +	union {
> +		struct {
> +			struct vmtl2_entry *vmt_base;
> +			unsigned int num_ents;
> +		} linear;
> +		struct {
> +			vmtl1_entry *vmt_base;
> +			struct vmtl2_entry **l2ptrs;
> +			unsigned int num_l1_ents;
> +		} l2;
> +	};
> +	bool		two_level;
> +	unsigned int	num_entries;
> +	unsigned int	max_vpes;
> +	size_t		vmd_size;
> +	size_t		vped_size;
> +	struct ida	vm_id_ida;
> +};
> +
>  struct vgic_v5_host_ist_caps {
>  	/* IST Capabilities */
>  
> @@ -38,4 +118,32 @@ static inline struct vgic_v5_host_ist_caps *vgic_v5_host_caps(void)
>  	return &gicv5_host_ist_caps;
>  }
>  
> +static inline u16 vgic_v5_vm_id(struct kvm *kvm)
> +{
> +	return kvm->arch.vgic.gicv5_vm.vm_id;
> +}
> +
> +static inline u16 vgic_v5_vpe_id(struct kvm_vcpu *vcpu)
> +{
> +	return vcpu->vcpu_id;
> +}
> +
> +static inline int vgic_v5_vpe_db(struct kvm_vcpu *vcpu)
> +{
> +	return vcpu->arch.vgic_cpu.vgic_v5.gicv5_vpe.db;
> +}
> +
> +int vgic_v5_vmt_allocate(bool two_level, unsigned int num_entries,
> +			 size_t vmd_size, size_t vped_size,
> +			 unsigned int vpe_id_bits);
> +int vgic_v5_vmt_free(void);
> +
> +int vgic_v5_allocate_vm_id(struct kvm *kvm);
> +void vgic_v5_release_vm_id(struct kvm *kvm);
> +
> +int vgic_v5_vmte_init(struct kvm *kvm);
> +int vgic_v5_vmte_release(struct kvm *kvm);
> +int vgic_v5_vmte_alloc_vpe(struct kvm_vcpu *vcpu);
> +int vgic_v5_vmte_free_vpe(struct kvm_vcpu *vcpu);
> +
>  #endif
> diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
> index 05dbd01f6fd21..0bcbc751593cc 100644
> --- a/include/kvm/arm_vgic.h
> +++ b/include/kvm/arm_vgic.h
> @@ -372,6 +372,8 @@ struct vgic_v5_vm {
>  	int			vpe_db_base;
>  	int			nr_vpes;
>  	u16			vm_id;
> +	bool			vm_id_valid;
> +	bool			vmte_allocated;
>  };
>  
>  struct vgic_dist {
> diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
> index 087d94f739672..89579ee04f5d1 100644
> --- a/include/linux/irqchip/arm-gic-v5.h
> +++ b/include/linux/irqchip/arm-gic-v5.h
> @@ -182,6 +182,7 @@
>  #define GICV5_IRS_MAP_L2_ISTR_ID	GENMASK(23, 0)
>  
>  #define GICV5_ISTL1E_VALID		BIT_ULL(0)
> +#define GICV5_IRS_ISTL1E_SIZE		8UL
>  
>  #define GICV5_ISTL1E_L2_ADDR_MASK	GENMASK_ULL(55, 12)
>  
> @@ -444,4 +445,16 @@ void gicv5_free_lpi(u32 lpi);
>  
>  void __init gicv5_its_of_probe(struct device_node *parent);
>  void __init gicv5_its_acpi_probe(void);
> +
> +enum gicv5_vcpu_info_cmd_type {
> +	VMT_L2_MAP,		/* Map in a L2 VMT - *may* happen on VM init */
> +	VMTE_MAKE_VALID,	/* Make the VMTE valid */
> +	VMTE_MAKE_INVALID,	/* Make the VMTE (et al.) invalid */
> +};
> +
> +struct gicv5_cmd_info {
> +	enum gicv5_vcpu_info_cmd_type	cmd_type;
> +	u64				data;
> +};
> +
>  #endif

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.


^ permalink raw reply

* [RFC PATCH 1/4] arm64: acpi: Refactor FADT table verification
From: Suzuki K Poulose @ 2026-04-29 10:35 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-kernel, catalin.marinas, will, ardb, lpieralisi,
	mark.rutland, steven.price, aneesh.kumar, sudeep.holla, robh, maz,
	Suzuki K Poulose
In-Reply-To: <20260429103535.266728-1-suzuki.poulose@arm.com>

Refactor the FADT sanity check, such that this could be reused. We are soon
going to consume the FADT early at boot for probing the PSCI conduit, and this
will be of use.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kernel/acpi.c | 55 +++++++++++++++++++++-------------------
 1 file changed, 29 insertions(+), 26 deletions(-)

diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index af90128cfed5..30c7b11a8a9d 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -118,6 +118,34 @@ bool acpi_psci_use_hvc(void)
 	return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_USE_HVC;
 }
 
+static int __init __acpi_fadt_sanity_check(struct acpi_table_fadt *fadt)
+{
+	struct acpi_table_header *table = (struct acpi_table_header *)fadt;
+
+	/*
+	 * Revision in table header is the FADT Major revision, and there
+	 * is a minor revision of FADT which was introduced by ACPI 5.1,
+	 * we only deal with ACPI 5.1 or newer revision to get GIC and SMP
+	 * boot protocol configuration data.
+	 */
+	if (table->revision < 5 ||
+	   (table->revision == 5 && fadt->minor_revision < 1)) {
+		pr_err(FW_BUG "Unsupported FADT revision %d.%d, should be 5.1+\n",
+		       table->revision, fadt->minor_revision);
+
+		if (!fadt->arm_boot_flags) {
+			return -EINVAL;
+		}
+		pr_err("FADT has ARM boot flags set, assuming 5.1\n");
+	}
+
+	if (!(fadt->flags & ACPI_FADT_HW_REDUCED)) {
+		pr_err("FADT not ACPI hardware reduced compliant\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 /*
  * acpi_fadt_sanity_check() - Check FADT presence and carry out sanity
  *			      checks on it
@@ -127,7 +155,6 @@ bool acpi_psci_use_hvc(void)
 static int __init acpi_fadt_sanity_check(void)
 {
 	struct acpi_table_header *table;
-	struct acpi_table_fadt *fadt;
 	acpi_status status;
 	int ret = 0;
 
@@ -143,32 +170,8 @@ static int __init acpi_fadt_sanity_check(void)
 		return -ENODEV;
 	}
 
-	fadt = (struct acpi_table_fadt *)table;
+	ret = __acpi_fadt_sanity_check((struct acpi_table_fadt *)table);
 
-	/*
-	 * Revision in table header is the FADT Major revision, and there
-	 * is a minor revision of FADT which was introduced by ACPI 5.1,
-	 * we only deal with ACPI 5.1 or newer revision to get GIC and SMP
-	 * boot protocol configuration data.
-	 */
-	if (table->revision < 5 ||
-	   (table->revision == 5 && fadt->minor_revision < 1)) {
-		pr_err(FW_BUG "Unsupported FADT revision %d.%d, should be 5.1+\n",
-		       table->revision, fadt->minor_revision);
-
-		if (!fadt->arm_boot_flags) {
-			ret = -EINVAL;
-			goto out;
-		}
-		pr_err("FADT has ARM boot flags set, assuming 5.1\n");
-	}
-
-	if (!(fadt->flags & ACPI_FADT_HW_REDUCED)) {
-		pr_err("FADT not ACPI hardware reduced compliant\n");
-		ret = -EINVAL;
-	}
-
-out:
 	/*
 	 * acpi_get_table() creates FADT table mapping that
 	 * should be released after parsing and before resuming boot
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 2/4] psci: Add support for Early detection and init
From: Suzuki K Poulose @ 2026-04-29 10:35 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-kernel, catalin.marinas, will, ardb, lpieralisi,
	mark.rutland, steven.price, aneesh.kumar, sudeep.holla, robh, maz,
	Suzuki K Poulose
In-Reply-To: <20260429103535.266728-1-suzuki.poulose@arm.com>

Allow early detection of the PSCI conduit and presence of SMCCC. Primary motive
is to detect if we are a Realm as early as possible to make better decisions

 1. For mapping the memory without BBML2_NOABORT
 2. Making use of the earlycon, to be able to debug problems at early boot.

Add a new psci_early_init_conduit() which can probe the PSCI and SMCCC. This is
optional, and could really fallback to the normal if the architecture cannot
reliably detect the conduit. Later patches will make use of this.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 drivers/firmware/psci/psci.c | 49 ++++++++++++++++++++++++++++++++++--
 include/linux/psci.h         |  2 ++
 2 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 38ca190d4a22..fa0839513d64 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -80,6 +80,11 @@ static u32 psci_cpu_suspend_feature;
 static bool psci_system_reset2_supported;
 static bool psci_system_off2_hibernate_supported;
 
+static inline bool psci_probed(void)
+{
+	return psci_ops.get_version != NULL;
+}
+
 static inline bool psci_has_ext_power_state(void)
 {
 	return psci_cpu_suspend_feature &
@@ -270,6 +275,15 @@ static unsigned long psci_migrate_info_up_cpu(void)
 
 static void set_conduit(enum arm_smccc_conduit conduit)
 {
+	/*
+	 * If we have set the conduit already, make sure
+	 * it is the same as we have found.
+	 */
+	if (psci_conduit != SMCCC_CONDUIT_NONE) {
+		WARN_ON(conduit != psci_conduit);
+		return;
+	}
+
 	switch (conduit) {
 	case SMCCC_CONDUIT_HVC:
 		invoke_psci_fn = __invoke_psci_fn_hvc;
@@ -679,7 +693,16 @@ static void __init psci_0_2_set_functions(void)
  */
 static int __init psci_probe(void)
 {
-	u32 ver = psci_0_2_get_version();
+	u32 ver;
+
+	/* Make sure we have a set CONDUIT */
+	if (WARN_ON(psci_conduit == SMCCC_CONDUIT_NONE))
+		return -ENODEV;
+
+	if (WARN_ON(psci_probed()))
+		return 0;
+
+	ver = psci_0_2_get_version();
 
 	pr_info("PSCIv%d.%d detected in firmware.\n",
 			PSCI_VERSION_MAJOR(ver),
@@ -717,10 +740,12 @@ static int __init psci_0_2_init(const struct device_node *np)
 {
 	int err;
 
+	if (psci_probed())
+		return 0;
+
 	err = get_set_conduit_method(np);
 	if (err)
 		return err;
-
 	/*
 	 * Starting with v0.2, the PSCI specification introduced a call
 	 * (PSCI_VERSION) that allows probing the firmware version, so
@@ -739,6 +764,9 @@ static int __init psci_0_1_init(const struct device_node *np)
 	u32 id;
 	int err;
 
+	if (psci_probed())
+		return 0;
+
 	err = get_set_conduit_method(np);
 	if (err)
 		return err;
@@ -816,6 +844,20 @@ int __init psci_dt_init(void)
 	return ret;
 }
 
+int __init psci_early_init_conduit(enum arm_smccc_conduit conduit)
+{
+	switch (conduit) {
+	case SMCCC_CONDUIT_HVC:
+	case SMCCC_CONDUIT_SMC:
+		set_conduit(conduit);
+		break;
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	return psci_probe();
+}
+
 #ifdef CONFIG_ACPI
 /*
  * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
@@ -823,6 +865,9 @@ int __init psci_dt_init(void)
  */
 int __init psci_acpi_init(void)
 {
+	if (psci_probed())
+		return 0;
+
 	if (!acpi_psci_present()) {
 		pr_info("is not implemented in ACPI.\n");
 		return -EOPNOTSUPP;
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 4ca0060a3fc4..8f98d61a69e4 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -59,4 +59,6 @@ static inline bool acpi_psci_present(void) { return false; }
 static inline bool acpi_psci_use_hvc(void) {return false; }
 #endif
 
+int __init psci_early_init_conduit(enum arm_smccc_conduit);
+
 #endif /* __LINUX_PSCI_H */
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 3/4] arm64: psci: Move detection and SMCCC probe earlier
From: Suzuki K Poulose @ 2026-04-29 10:35 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-kernel, catalin.marinas, will, ardb, lpieralisi,
	mark.rutland, steven.price, aneesh.kumar, sudeep.holla, robh, maz,
	Suzuki K Poulose
In-Reply-To: <20260429103535.266728-1-suzuki.poulose@arm.com>

We parse the ACPI/DT for the PSCI conduit after the linear map is created via
paging_init(). This implies that we do not have all the information required to
decide whether block mappings are safe for the linear map.

e.g., With Realms, we cannot use Block mappings without BBML2_NOABORT support.
See [0] for more discussion.

This patch moves the detection of PSCI and SMCCC probing, before paging_init(),
after the efi_init(). We scan the ACPI and the DT (unflattened) for the conduit.
If we both have conduit set, we proceed only if they match. Otherwise, we can't
be sure what gets used eventually by the OS. e.g., if the ACPI table is corrupt,
DT may be used.

We do minimal validations on the ACPI tables (e.g. revision checks, FADT
checks etc.). TODO: Verify the checksum ? (acpi_table_checksum())

[0] https://lore.kernel.org/all/20260330161705.3349825-2-ryan.roberts@arm.com/

Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Steven Price <steven.price@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/include/asm/acpi.h |  1 +
 arch/arm64/kernel/acpi.c      | 81 +++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/setup.c     | 68 +++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+)

diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index c07a58b96329..a5198fa46088 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -188,4 +188,5 @@ static inline void acpi_map_cpus_to_nodes(void) { }
 
 #define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE
 
+enum arm_smccc_conduit __init acpi_early_psci_conduit(void);
 #endif /*_ASM_ACPI_H*/
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 30c7b11a8a9d..88d79def4c90 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -180,6 +180,87 @@ static int __init acpi_fadt_sanity_check(void)
 	return ret;
 }
 
+/*
+ * Find the PSCI conduit from ACPI FADT table. We only proceed with the parsing
+ * if acpi *may be* used. i.e, acpi=force or acpi=on or DT is stub.
+ *
+ * FADT table is located as below:
+ *
+ *  RSDP -> XSDT ptr -> array of pointers to different Tables
+ *
+ * Note: Arm64 requires ACPI v5.1+, thus we always use XSDT (not RSDT, for < 2.0)
+ *	 from RSDP.
+ *
+ * Returns : Conduit if system is PSCI compliant.
+ *	     Otherwise returns SMCCC_CONDUIT_NONE.
+ */
+enum arm_smccc_conduit __init acpi_early_psci_conduit(void)
+{
+	enum arm_smccc_conduit c = SMCCC_CONDUIT_NONE;
+	struct acpi_table_rsdp *rsdp;
+	struct acpi_table_xsdt *xsdt;
+	u64 *ptr, *end;
+	u64 xsdt_pa, xsdt_len;
+	bool found = false;
+
+	if (param_acpi_off ||
+	    (!param_acpi_on && !param_acpi_force && !dt_is_stub()))
+		return SMCCC_CONDUIT_NONE;
+
+	if (efi.acpi20 == EFI_INVALID_TABLE_ADDR)
+		return SMCCC_CONDUIT_NONE;
+
+	rsdp = early_memremap(efi.acpi20, sizeof(*rsdp));
+	if (!rsdp)
+		return SMCCC_CONDUIT_NONE;
+
+	if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature) || rsdp->revision < 2 ||
+	    rsdp->xsdt_physical_address == 0) {
+		early_memunmap(rsdp, sizeof(*rsdp));
+		return SMCCC_CONDUIT_NONE;
+	}
+
+	xsdt_pa = rsdp->xsdt_physical_address;
+
+	/* Now that XSDT is found, unmap the RSDP */
+	early_memunmap(rsdp, sizeof(*rsdp));
+
+	/*
+	 * XSDT is mainly an array of table pointers, with standard header.
+	 * So we map upto a PAGE_SIZE (and more with alignment) and only
+	 * look there, with a WARNING.
+	 */
+	xsdt = early_memremap(xsdt_pa, PAGE_SIZE);
+	if (!xsdt)
+		return SMCCC_CONDUIT_NONE;
+
+	xsdt_len = ((struct acpi_table_header*)xsdt)->length;
+	if (xsdt_len > (ALIGN(xsdt_pa + PAGE_SIZE, PAGE_SIZE) - xsdt_pa)) {
+		pr_warn("XSDT table is larger than a page\n");
+	}
+
+	/* Find FADT table from the XSDT */
+	ptr = &xsdt->table_offset_entry[0];
+	end = (u64*)((void *)xsdt + xsdt_len);
+	for (; ptr < end && !found; ptr++) {
+		struct acpi_table_fadt *fadt = early_memremap(*ptr, sizeof(*fadt));
+
+		if (ACPI_COMPARE_NAMESEG(&fadt->header.signature, ACPI_SIG_FADT) &&
+		    __acpi_fadt_sanity_check(fadt) == 0) {
+			u16 arm_boot_flags = fadt->arm_boot_flags;
+
+			if (arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT) {
+				c = arm_boot_flags & ACPI_FADT_PSCI_USE_HVC ?
+					SMCCC_CONDUIT_HVC : SMCCC_CONDUIT_SMC;
+			}
+			found = true;
+		}
+		early_memunmap(fadt, sizeof(*fadt));
+	}
+	early_memunmap(xsdt, PAGE_SIZE);
+	return c;
+}
+
 /*
  * acpi_boot_table_init() called from setup_arch(), always.
  *	1. find RSDP and get its address, and then find XSDT
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 23c05dc7a8f2..595aff6f17d5 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -278,6 +278,71 @@ u64 cpu_logical_map(unsigned int cpu)
 	return __cpu_logical_map[cpu];
 }
 
+static enum arm_smccc_conduit early_dt_probe_psci_conduit(void)
+{
+	int len;
+	int psci_node;
+	const char *method;
+	enum arm_smccc_conduit conduit = SMCCC_CONDUIT_NONE;
+	unsigned long dt_root;
+
+	/* DT hasn't been unflattened yet, we have to work with the flat blob */
+	dt_root = of_get_flat_dt_root();
+	psci_node = of_get_flat_dt_subnode_by_name(dt_root, "psci");
+	if (psci_node <= 0)
+		return conduit;
+
+	method = of_get_flat_dt_prop(psci_node, "method", &len);
+	if (!method)
+		return conduit;
+
+	if (strncmp(method, "smc", len) == 0) {
+		conduit = SMCCC_CONDUIT_SMC;
+	} else if (strncmp(method, "hvc", len) == 0) {
+		conduit = SMCCC_CONDUIT_HVC;
+	}
+	return conduit;
+}
+
+/*
+ * Detect the PSCI conduit from both ACPI and DT, and probe the PSCI/SMCCC
+ * early if we can.
+ *
+ * Given both ACPI and DT could have valid configurations, we go forward with
+ * the early detection only if there is a valid conduit and both of them match.
+ */
+static void __init early_psci_init(void)
+{
+	enum arm_smccc_conduit dt_conduit = SMCCC_CONDUIT_NONE;
+	enum arm_smccc_conduit acpi_conduit = SMCCC_CONDUIT_NONE;
+	enum arm_smccc_conduit conduit = SMCCC_CONDUIT_NONE;
+
+	dt_conduit = early_dt_probe_psci_conduit();
+
+#ifdef CONFIG_ACPI
+	if (efi_enabled(EFI_BOOT))
+		acpi_conduit = acpi_early_psci_conduit();
+#endif
+	if (dt_conduit == SMCCC_CONDUIT_NONE &&
+	    acpi_conduit == SMCCC_CONDUIT_NONE) {
+		pr_crit("PSCI: Early probe: no conduit found\n");
+		return;
+	}
+
+	if (acpi_conduit == SMCCC_CONDUIT_NONE) {
+		conduit = dt_conduit;
+	} else if (dt_conduit == SMCCC_CONDUIT_NONE) {
+		conduit = acpi_conduit;
+	} else if (dt_conduit == acpi_conduit) {
+		conduit = acpi_conduit;
+	} else {
+		WARN(1, "PSCI: Early probe: Mismatched PSCI conduit, skipping\n");
+		return;
+	}
+
+	psci_early_init_conduit(conduit);
+}
+
 void __init __no_sanitize_address setup_arch(char **cmdline_p)
 {
 	setup_initial_init_mm(_text, _etext, _edata, _end);
@@ -322,6 +387,9 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
 	xen_early_init();
 	efi_init();
 
+	/* Probe the PSCI early after the efi_init() */
+	early_psci_init();
+
 	if (!efi_enabled(EFI_BOOT)) {
 		if ((u64)_text % MIN_KIMG_ALIGN)
 			pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 4/4] arm64: realm: Move RSI detection earlier
From: Suzuki K Poulose @ 2026-04-29 10:35 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-kernel, catalin.marinas, will, ardb, lpieralisi,
	mark.rutland, steven.price, aneesh.kumar, sudeep.holla, robh, maz,
	Suzuki K Poulose
In-Reply-To: <20260429103535.266728-1-suzuki.poulose@arm.com>

As discussed in [0], Realm world detection happens too late for making the
decision for the Linear map granularity for Arm64. If the boot CPU doesn't support
BBML2_NOABORT, it is difficult to break the mapping to PTE level. To solve this
try probing the Realm world earlier,  now that we probe the PSCI/SMCCC earlier.
Like the PSCI probe, if we fall back to the later point if the SMCCC probe
wasn't successful. We should probably add a WARNING/panic the system if the
BBML2_NOABORT is not supported on the boot CPU and linear map was created
with block mappings. This may case avoided by using rodata=full.

[0] https://lore.kernel.org/all/20260330161705.3349825-2-ryan.roberts@arm.com/

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/include/asm/rsi.h |  1 +
 arch/arm64/kernel/rsi.c      | 23 ++++++++++++++++++++++-
 arch/arm64/kernel/setup.c    |  3 ++-
 3 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/rsi.h b/arch/arm64/include/asm/rsi.h
index 88b50d660e85..4456bf0a1f23 100644
--- a/arch/arm64/include/asm/rsi.h
+++ b/arch/arm64/include/asm/rsi.h
@@ -14,6 +14,7 @@
 
 DECLARE_STATIC_KEY_FALSE(rsi_present);
 
+void __init arm64_early_rsi_init(void);
 void __init arm64_rsi_init(void);
 
 bool arm64_rsi_is_protected(phys_addr_t base, size_t size);
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index c64a06f58c0b..2d2befae2c75 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -138,10 +138,14 @@ static int realm_ioremap_hook(phys_addr_t phys, size_t size, pgprot_t *prot)
 	return 0;
 }
 
-void __init arm64_rsi_init(void)
+static bool rsi_probed = 0;
+
+static void __init rsi_probe(void)
 {
 	if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_SMC)
 		return;
+
+	rsi_probed = true;
 	if (!rsi_version_matches())
 		return;
 	if (WARN_ON(rsi_get_realm_config(&config)))
@@ -159,6 +163,23 @@ void __init arm64_rsi_init(void)
 	static_branch_enable(&rsi_present);
 }
 
+void __init arm64_early_rsi_init(void)
+{
+	rsi_probe();
+}
+
+void __init arm64_rsi_init(void)
+{
+	if (rsi_probed)
+		return;
+
+	rsi_probe();
+	/*
+	 * TODO: Warn if we don't have BBML2_NOABORT and page mapping is
+	 * not used.
+	 */
+}
+
 static struct platform_device rsi_dev = {
 	.name = RSI_PDEV_NAME,
 	.id = PLATFORM_DEVID_NONE
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 595aff6f17d5..0664130dd791 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -340,7 +340,8 @@ static void __init early_psci_init(void)
 		return;
 	}
 
-	psci_early_init_conduit(conduit);
+	if (!psci_early_init_conduit(conduit))
+		arm64_early_rsi_init();
 }
 
 void __init __no_sanitize_address setup_arch(char **cmdline_p)
-- 
2.43.0



^ permalink raw reply related

* [RFC PATCH 0/4] arm64: realm: Support for probing RSI earlier
From: Suzuki K Poulose @ 2026-04-29 10:35 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: linux-kernel, catalin.marinas, will, ardb, lpieralisi,
	mark.rutland, steven.price, aneesh.kumar, sudeep.holla, robh, maz,
	Suzuki K Poulose

The Realm Guest linux support is broken without rodata=full (fortunately default
for arm64), as we detect the RSI support after we have created the Linear map
with Block/Contiguous mappings. If the boot CPU doesn't support BBML2_NOABORT
(there are CPUs out there with FEAT_RME and no - useable - BBML2_NOABORT)
we are then not able to split the page tables down to PTE level if the system
as such doesn't support BBML2.

See the following link for the discussion.

https://lore.kernel.org/all/20260330161705.3349825-2-ryan.roberts@arm.com/

The available options are :
 1. Start with PTE level mappings at paging_init() and then "FOLD" the page tables
    to Block/Cont mappings after we have the full picture available. Looking at the
    future (with BBML3), this might mean "additional work" for most of the systems
    at boot. But not bad as splitting them ?
 2. Hold the secondary CPUs in busy loop with MMU disabled and split the mappings
    by the boot CPU with MMU off (if Boot CPU can't support BBML2). This is tricky
    with the page allocations required to add the page-tables.
 3. Move the detection of Realm support earlier to make a better decision for
    paging_init(), with an added bonus of earlycon support for Realms without
    the user having to work out the "top bit" for the Realm.

This series is an attempt to implement (3) (without the earlycon support). We try
to probe the PSCI conduit early from the DT/ACPI. DT is not flattened at this time.

ACPI table is not mapped in full, so we have to map one table at a time and walk
from the Root of the table (RSDP) through to XSDT and find the FADT table from
the array of table pointers there. Minimal verification is performed on the 
tables (e.g., revision checks, standard FADT sanity checks). Checksum is not
verified, but should be possible to do for the parts we consume.

With arm64, during the normal boot, we could fallback to using DT if the ACPI
tables are not useable. So, during the early probe, we try to follow the similar
logic and probe the conduit from both DT and ACPI where available. If both of
them contain a conduit, we only proceed if they match. Otherwise, we skip the
early probe and do things the normal way. (Any sane system shouldn't have such
a mismatch, but..)

Once we probe the PSCI conduit, PSCI is probed, along with the presence of SMCCC.
With that in place, we try to probe the RSI support after the early probe and
advertise the Realm World. If the early probe wasn't successful, we fall back
to the late mode, where we could end up with (on a possibly rare broken firmware).

NOTE: This is an early RFC attempt to moving the PSCI detection earlier. The other
option(s) that may be worth exploring are:

1. On systems with EFI, parse this from EFI Stub and pass the data back in the
   DT Stub, under chosen node. e.g., "linux,uefi-arm-psci-conduit".
   Challenge: EFI stub doesn't seem to be ACPI aware. We could make that change,
   we only need a few table walks.
2. Have EFI firmware provide this information (with my limited knowledge on the
   area, this looks like too much work, and bending the standards)
3. Append arm64 boot protocol to have this information passed to the kernel.
   (Firmware provided) - (Steven's idea)
4. Any other options ?


This series is also available here :

git@git.gitlab.arm.com:linux-arm/linux-cca.git	cca-guest/early-rsi-detection/rfc-v1

Thoughts ?

Suzuki


Suzuki K Poulose (4):
  arm64: acpi: Refactor FADT table verification
  psci: Add support for Early detection and init
  arm64: psci: Move detection and SMCCC probe earlier
  arm64: realm: Move RSI detection earlier

 arch/arm64/include/asm/acpi.h |   1 +
 arch/arm64/include/asm/rsi.h  |   1 +
 arch/arm64/kernel/acpi.c      | 136 +++++++++++++++++++++++++++-------
 arch/arm64/kernel/rsi.c       |  23 +++++-
 arch/arm64/kernel/setup.c     |  69 +++++++++++++++++
 drivers/firmware/psci/psci.c  |  49 +++++++++++-
 include/linux/psci.h          |   2 +
 7 files changed, 252 insertions(+), 29 deletions(-)

-- 
2.43.0



^ permalink raw reply

* Re: [PATCH net-next] net: airoha: Introduce airoha_fe_get()/airoha_qdma_get() register read helpers
From: Lorenzo Bianconi @ 2026-04-29 10:51 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <20260428-airoha_fe_get-airoha_qdma_get-v1-1-6cfbdeb42743@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 4709 bytes --]

> Add airoha_fe_get() and airoha_qdma_get() as utility routines for reading
> a masked field from a specified register.
> This is a non-functional refactor, no logical changes are introduced to
> the existing codebase.
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  drivers/net/ethernet/airoha/airoha_eth.c | 13 ++++---------
>  drivers/net/ethernet/airoha/airoha_eth.h |  4 ++++
>  drivers/net/ethernet/airoha/airoha_ppe.c |  5 ++---
>  3 files changed, 10 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 2bb0a3ff9810..40b7a00c7d95 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -201,15 +201,13 @@ static void airoha_fe_vip_setup(struct airoha_eth *eth)
>  static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth,
>  					     u32 port, u32 queue)
>  {
> -	u32 val;
> -
>  	airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
>  		      PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK,
>  		      FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
>  		      FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue));
> -	val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL);
>  
> -	return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val);
> +	return airoha_fe_get(eth, REG_FE_PSE_QUEUE_CFG_VAL,
> +			     PSE_CFG_OQ_RSV_MASK);
>  }
>  
>  static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
> @@ -227,9 +225,7 @@ static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
>  
>  static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth)
>  {
> -	u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET);
> -
> -	return FIELD_GET(PSE_ALLRSV_MASK, val);
> +	return airoha_fe_get(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK);
>  }
>  
>  static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
> @@ -247,8 +243,7 @@ static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
>  		      FIELD_PREP(PSE_ALLRSV_MASK, all_rsv));
>  
>  	/* modify hthd */
> -	tmp = airoha_fe_rr(eth, PSE_FQ_CFG);
> -	fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp);
> +	fq_limit = airoha_fe_get(eth, PSE_FQ_CFG, PSE_FQ_LIMIT_MASK);
>  	tmp = fq_limit - all_rsv - 0x20;
>  	airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
>  		      PSE_SHARE_USED_HTHD_MASK,
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index e389d2fe3b86..c81433d44e3e 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -619,6 +619,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
>  	airoha_rmw((eth)->fe_regs, (offset), 0, (val))
>  #define airoha_fe_clear(eth, offset, val)			\
>  	airoha_rmw((eth)->fe_regs, (offset), (val), 0)
> +#define airoha_fe_get(eth, offset, mask)			\
> +	FIELD_GET((mask), airoha_fe_rr((eth), (offset)))
>  
>  #define airoha_qdma_rr(qdma, offset)				\
>  	airoha_rr((qdma)->regs, (offset))
> @@ -630,6 +632,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
>  	airoha_rmw((qdma)->regs, (offset), 0, (val))
>  #define airoha_qdma_clear(qdma, offset, val)			\
>  	airoha_rmw((qdma)->regs, (offset), (val), 0)
> +#define airoha_qdma_get(eth, offset, mask)			\
> +	FIELD_GET((mask), airoha_qdma_rr((eth), (offset)))

commenting on Sashiko's report:
https://sashiko.dev/#/patchset/20260428-airoha_fe_get-airoha_qdma_get-v1-1-6cfbdeb42743%40kernel.org

- This isn't a bug, but is the first parameter of airoha_qdma_get() supposed
  to be named qdma instead of eth?
  This is correct, I will fix it in v2.

Regards,
Lorenzo


>  
>  static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
>  {
> diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
> index 5c9dff6bccd1..697af6fdd4c3 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -80,9 +80,8 @@ bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index)
>  
>  static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
>  {
> -	u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS);
> -
> -	return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp);
> +	return airoha_fe_get(ppe->eth, REG_FE_FOE_TS,
> +			     AIROHA_FOE_IB1_BIND_TIMESTAMP);
>  }
>  
>  void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport)
> 
> ---
> base-commit: 790ead9394860e7d70c5e0e50a35b243e909a618
> change-id: 20260428-airoha_fe_get-airoha_qdma_get-7a087a23aef4
> 
> Best regards,
> -- 
> Lorenzo Bianconi <lorenzo@kernel.org>
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH net-next v2] net: airoha: Introduce airoha_fe_get()/airoha_qdma_get() register read helpers
From: Lorenzo Bianconi @ 2026-04-29 10:54 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Lorenzo Bianconi
  Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev

Add airoha_fe_get() and airoha_qdma_get() as utility routines for reading
a masked field from a specified register.
This is a non-functional refactor, no logical changes are introduced to
the existing codebase.

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Changes in v2:
- Fix typo in airoha_qdma_get() definition.
- Link to v1: https://lore.kernel.org/r/20260428-airoha_fe_get-airoha_qdma_get-v1-1-6cfbdeb42743@kernel.org
---
 drivers/net/ethernet/airoha/airoha_eth.c | 13 ++++---------
 drivers/net/ethernet/airoha/airoha_eth.h |  4 ++++
 drivers/net/ethernet/airoha/airoha_ppe.c |  5 ++---
 3 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 2bb0a3ff9810..40b7a00c7d95 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -201,15 +201,13 @@ static void airoha_fe_vip_setup(struct airoha_eth *eth)
 static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth,
 					     u32 port, u32 queue)
 {
-	u32 val;
-
 	airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
 		      PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK,
 		      FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
 		      FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue));
-	val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL);
 
-	return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val);
+	return airoha_fe_get(eth, REG_FE_PSE_QUEUE_CFG_VAL,
+			     PSE_CFG_OQ_RSV_MASK);
 }
 
 static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
@@ -227,9 +225,7 @@ static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
 
 static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth)
 {
-	u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET);
-
-	return FIELD_GET(PSE_ALLRSV_MASK, val);
+	return airoha_fe_get(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK);
 }
 
 static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
@@ -247,8 +243,7 @@ static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
 		      FIELD_PREP(PSE_ALLRSV_MASK, all_rsv));
 
 	/* modify hthd */
-	tmp = airoha_fe_rr(eth, PSE_FQ_CFG);
-	fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp);
+	fq_limit = airoha_fe_get(eth, PSE_FQ_CFG, PSE_FQ_LIMIT_MASK);
 	tmp = fq_limit - all_rsv - 0x20;
 	airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
 		      PSE_SHARE_USED_HTHD_MASK,
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index e389d2fe3b86..eda888593f94 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -619,6 +619,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
 	airoha_rmw((eth)->fe_regs, (offset), 0, (val))
 #define airoha_fe_clear(eth, offset, val)			\
 	airoha_rmw((eth)->fe_regs, (offset), (val), 0)
+#define airoha_fe_get(eth, offset, mask)			\
+	FIELD_GET((mask), airoha_fe_rr((eth), (offset)))
 
 #define airoha_qdma_rr(qdma, offset)				\
 	airoha_rr((qdma)->regs, (offset))
@@ -630,6 +632,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
 	airoha_rmw((qdma)->regs, (offset), 0, (val))
 #define airoha_qdma_clear(qdma, offset, val)			\
 	airoha_rmw((qdma)->regs, (offset), (val), 0)
+#define airoha_qdma_get(qdma, offset, mask)			\
+	FIELD_GET((mask), airoha_qdma_rr((qdma), (offset)))
 
 static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
 {
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 5c9dff6bccd1..697af6fdd4c3 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -80,9 +80,8 @@ bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index)
 
 static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
 {
-	u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS);
-
-	return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp);
+	return airoha_fe_get(ppe->eth, REG_FE_FOE_TS,
+			     AIROHA_FOE_IB1_BIND_TIMESTAMP);
 }
 
 void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport)

---
base-commit: 09942ddedcb960f9e78fd817ec33f501d1040c5b
change-id: 20260428-airoha_fe_get-airoha_qdma_get-7a087a23aef4

Best regards,
-- 
Lorenzo Bianconi <lorenzo@kernel.org>



^ permalink raw reply related

* Re: [DONOTAPPLY RFC PATCH v2 0/4] WiFi support for samsung,coreprimevelte
From: Karel Balej @ 2026-04-29 10:55 UTC (permalink / raw)
  To: Brian Norris, Francesco Dolcini
  Cc: Johannes Berg, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Duje Mihanović, Andrew Lunn, Gregory Clement,
	Sebastian Hesselbarth, Ulf Hansson, Frank Li, linux-wireless,
	devicetree, linux-kernel, linux-arm-kernel, linux-mmc,
	~postmarketos/upstreaming, phone-devel, Jeff Chen, Peng Fan,
	david
In-Reply-To: <aSnWYS2g5slVFaSk@gaggiata.pivistrello.it>

Hello,

Francesco Dolcini, 2025-11-28T18:05:37+01:00:
> Hello Karel,
>
> On Thu, Nov 27, 2025 at 04:29:12PM +0100, Karel Balej wrote:
>> To reiterate, the firmware is generally available but is not part of
>> linux-firmware and the entire process of upstreaming the chipset support is
>> stuck on that.
>
> I'll try to see if any of my contact in NXP Wi-Fi group is able to help. Give
> me a few days.

so I was in a long conversation over the past months with Jeff from NXP
who was very helpful and tried to arrange for the upstreaming of the
firmware. Unfortunately however, his efforts were ultimately rejected by
the internal management.

We were directed to try to reach out to NXP via the customer support
page which yielded nothing. The current situation is thus the same as a
few months ago, summarized in my quote above.

Brian, what are the options here now? Would it be possible to make an
exception and accept the patches without the firmware being in
linux-firmware? This is an old device with no mainstream audience so I
expect everyone who will want to use it will be able to supply the
firmware themselves and it would be great to not have to keep the
patches in a fork, especially when trying to build on top of them
further (such as to fix the driver-firmware incompatibilities discussed
in one of the patches of this series).

Francesco, would you perhaps still be able to help in any way?

Thank you, kind regards,
K. B.


^ permalink raw reply

* Re: [PATCH v8 next 00/10] arm_mpam: Introduce Narrow-PARTID feature
From: Zeng Heng @ 2026-04-29 10:59 UTC (permalink / raw)
  To: ben.horgan, Dave.Martin, james.morse, tan.shaopeng,
	reinette.chatre, fenghuay, tglx, will, hpa, bp, babu.moger,
	dave.hansen, mingo, tony.luck, gshan, catalin.marinas
  Cc: linux-arm-kernel, x86, linux-kernel, wangkefeng.wang
In-Reply-To: <20260413085405.1166412-1-zengheng4@huawei.com>



On 2026/4/13 16:53, Zeng Heng wrote:
> Background
> ==========
> 
> On x86, the resctrl allows creating up to num_rmids monitoring groups
> under parent control group. However, ARM64 MPAM is currently limited by
> the PMG (Performance Monitoring Group) count, which is typically much
> smaller than the theoretical RMID limit. This creates a significant
> scalability gap: users expecting fine-grained per-process or per-thread
> monitoring quickly exhaust the PMG space, even when plenty of reqPARTIDs
> remain available.
> 
> The Narrow-PARTID feature, defined in the ARM MPAM architecture,
> addresses this by associating reqPARTIDs with intPARTIDs through a
> programmable many-to-one mapping. This allows the kernel to present more
> logical monitoring contexts.
> 
> Design Overview
> ===============
> 
> The implementation extends the RMID encoding to carry reqPARTID
> information:
> 
>    RMID = reqPARTID * NUM_PMG + PMG
> 
> In this patchset, a monitoring group is uniquely identified by the
> combination of reqPARTID and PMG. The closid is represented by intPARTID,
> which is exactly the original PARTID.
> 
> For systems with homogeneous MSCs (all supporting Narrow-PARTID), the
> driver exposes the full reqPARTID range directly. For heterogeneous
> systems where some MSCs lack Narrow-PARTID support, the driver utilizes
> PARTIDs beyond the intPARTID range as reqPARTIDs to expand monitoring
> capability. The sole exception is when any type of MSCs lack Narrow-PARTID
> support, their percentage-based control mechanism prevents the use of
> PARTIDs as reqPARTIDs.
> 
> Capability Improvements
> =======================
> 
> --------------------------------------------------------------------------
> The maximum        |  Sub-monitoring groups            | System-wide
> number of          |  under a control group            | monitoring groups
> --------------------------------------------------------------------------
> Without reqPARTID  |  PMG                              | intPARTID * PMG
> --------------------------------------------------------------------------
> reqPARTID          |                                   |
> static allocation  | (reqPARTID // intPARTID) * PMG    | reqPARTID * PMG
> --------------------------------------------------------------------------
> reqPARTID          |                                   |
> dynamic allocation | (reqPARTID - intPARTID + 1) * PMG | reqPARTID * PMG
> --------------------------------------------------------------------------
> 
> Note: The number of intPARTIDs can be capped via the boot parameter
> mpam.intpartid_max. Under MPAM, reqPARTID count is always greater than
> or equal to intPARTID count.
> 

Thank you for your review. Please find the test report below for this
patch set.

Test Environment:
- Kernel: linux/master (commit: 6596a02b207886e9e00bb0161c7fd59fea53c081)
- Architecture: arm64
- Platform: Kunpeng 950
- MPAM Features: 256 reqPARTIDs, 32 intPARTIDs, 4 PMGs

Functional Test Example:

~~~
   # dmesg | grep -i pmg
   [   11.528919] MPAM enabled with 256 reqPARTIDs, 16 intPARTIDs and 4 PMGs
   # cat /proc/cmdline
   <...> mpam.intpartid_max=15
   # cat /sys/module/mpam/parameters/intpartid_max
   15

   # mount -t resctrl resctrl /sys/fs/resctrl/ -o debug
   # cd /sys/fs/resctrl/
   # mkdir p1
   # cd p1/mon_groups/
   # mkdir m{1..963}
   # ls m963
   cpus  cpus_list  mon_data  mon_hw_id  tasks
~~~
Able to create 964 resource groups ((256 - 15) * 4), which matches the
expected result.


MPAM KUnit Test Log:

~~~
[   11.515338] KTAP version 1
[   11.515340] 1..2
[   11.515398]     KTAP version 1
[   11.515400]     # Subtest: mpam_devices_test_suite
[   11.515402]     # module: mpam
[   11.515405]     1..3
[   11.515497]     ok 1 test_mpam_reset_msc_bitmap
[   11.515568] mpam:__props_mismatch: __props_mismatch took the min cmax_wd
[   11.515579] mpam:__props_mismatch: cleared cpor_part
[   11.515581] mpam:__props_mismatch: cleared mbw_part
[   11.515592] mpam:__props_mismatch: took the min bwa_wd
[   11.515594] mpam:__props_mismatch: __props_mismatch took the min cmax_wd
[   11.515613] mpam:__props_mismatch: __props_mismatch took the min cmax_wd
[   11.515658]     ok 2 test_mpam_enable_merge_features
[   11.515722]     ok 3 test__props_mismatch
[   11.515725] # mpam_devices_test_suite: pass:3 fail:0 skip:0 total:3
[   11.515726] # Totals: pass:3 fail:0 skip:0 total:3
[   11.515728] ok 1 mpam_devices_test_suite
[   11.515730]     KTAP version 1
[   11.515732]     # Subtest: mpam_resctrl_test_suite
[   11.515734]     # module: mpam
[   11.515735]     1..6
[   11.515792]     ok 1 test_get_mba_granularity
[   11.515795]         KTAP version 1
[   11.515797]         # Subtest: test_mbw_max_to_percent
[   11.515850]         ok 1 pc=1, width=8, value=0x01
[   11.515905]         ok 2 pc=1, width=12, value=0x027
[   11.515959]         ok 3 pc=1, width=16, value=0x028e
[   11.516011]         ok 4 pc=25, width=8, value=0x3f
[   11.516069]         ok 5 pc=25, width=12, value=0x3ff
[   11.516127]         ok 6 pc=25, width=16, value=0x3fff
[   11.516182]         ok 7 pc=33, width=8, value=0x53
[   11.516236]         ok 8 pc=33, width=12, value=0x546
[   11.516290]         ok 9 pc=33, width=16, value=0x5479
[   11.516345]         ok 10 pc=35, width=8, value=0x58
[   11.516401]         ok 11 pc=35, width=12, value=0x598
[   11.516456]         ok 12 pc=35, width=16, value=0x5998
[   11.516511]         ok 13 pc=45, width=8, value=0x72
[   11.516565]         ok 14 pc=45, width=12, value=0x732
[   11.516617]         ok 15 pc=45, width=16, value=0x7332
[   11.516673]         ok 16 pc=50, width=8, value=0x7f
[   11.516726]         ok 17 pc=50, width=12, value=0x7ff
[   11.516781]         ok 18 pc=50, width=16, value=0x7fff
[   11.516835]         ok 19 pc=52, width=8, value=0x84
[   11.516889]         ok 20 pc=52, width=12, value=0x850
[   11.516943]         ok 21 pc=52, width=16, value=0x851d
[   11.516997]         ok 22 pc=55, width=8, value=0x8b
[   11.517073]         ok 23 pc=55, width=12, value=0x8cb
[   11.517139]         ok 24 pc=55, width=16, value=0x8ccb
[   11.517196]         ok 25 pc=58, width=8, value=0x93
[   11.517254]         ok 26 pc=58, width=12, value=0x946
[   11.517309]         ok 27 pc=58, width=16, value=0x9479
[   11.517364]         ok 28 pc=75, width=8, value=0xbf
[   11.517418]         ok 29 pc=75, width=12, value=0xbff
[   11.517474]         ok 30 pc=75, width=16, value=0xbfff
[   11.517529]         ok 31 pc=80, width=8, value=0xcb
[   11.517583]         ok 32 pc=80, width=12, value=0xccb
[   11.517638]         ok 33 pc=80, width=16, value=0xcccb
[   11.517693]         ok 34 pc=88, width=8, value=0xe0
[   11.517750]         ok 35 pc=88, width=12, value=0xe13
[   11.517806]         ok 36 pc=88, width=16, value=0xe146
[   11.517861]         ok 37 pc=95, width=8, value=0xf2
[   11.517918]         ok 38 pc=95, width=12, value=0xf32
[   11.517972]         ok 39 pc=95, width=16, value=0xf332
[   11.518027]         ok 40 pc=100, width=8, value=0xff
[   11.518084]         ok 41 pc=100, width=12, value=0xfff
[   11.518141]         ok 42 pc=100, width=16, value=0xffff
[   11.518144]     # test_mbw_max_to_percent: pass:42 fail:0 skip:0 total:42
[   11.518146]     ok 2 test_mbw_max_to_percent
[   11.518149]         KTAP version 1
[   11.518151]         # Subtest: test_percent_to_mbw_max
[   11.518204]         ok 1 pc=1, width=8, value=0x01
[   11.518259]         ok 2 pc=1, width=12, value=0x027
[   11.518321]         ok 3 pc=1, width=16, value=0x028e
[   11.518380]         ok 4 pc=25, width=8, value=0x3f
[   11.518434]         ok 5 pc=25, width=12, value=0x3ff
[   11.518492]         ok 6 pc=25, width=16, value=0x3fff
[   11.518546]         ok 7 pc=33, width=8, value=0x53
[   11.518603]         ok 8 pc=33, width=12, value=0x546
[   11.518662]         ok 9 pc=33, width=16, value=0x5479
[   11.518718]         ok 10 pc=35, width=8, value=0x58
[   11.518776]         ok 11 pc=35, width=12, value=0x598
[   11.518832]         ok 12 pc=35, width=16, value=0x5998
[   11.518887]         ok 13 pc=45, width=8, value=0x72
[   11.518943]         ok 14 pc=45, width=12, value=0x732
[   11.519000]         ok 15 pc=45, width=16, value=0x7332
[   11.519055]         ok 16 pc=50, width=8, value=0x7f
[   11.519110]         ok 17 pc=50, width=12, value=0x7ff
[   11.519166]         ok 18 pc=50, width=16, value=0x7fff
[   11.519219]         ok 19 pc=52, width=8, value=0x84
[   11.519276]         ok 20 pc=52, width=12, value=0x850
[   11.519331]         ok 21 pc=52, width=16, value=0x851d
[   11.519387]         ok 22 pc=55, width=8, value=0x8b
[   11.519444]         ok 23 pc=55, width=12, value=0x8cb
[   11.519504]         ok 24 pc=55, width=16, value=0x8ccb
[   11.519561]         ok 25 pc=58, width=8, value=0x93
[   11.519616]         ok 26 pc=58, width=12, value=0x946
[   11.519676]         ok 27 pc=58, width=16, value=0x9479
[   11.519730]         ok 28 pc=75, width=8, value=0xbf
[   11.519785]         ok 29 pc=75, width=12, value=0xbff
[   11.519840]         ok 30 pc=75, width=16, value=0xbfff
[   11.519893]         ok 31 pc=80, width=8, value=0xcb
[   11.519946]         ok 32 pc=80, width=12, value=0xccb
[   11.519999]         ok 33 pc=80, width=16, value=0xcccb
[   11.520054]         ok 34 pc=88, width=8, value=0xe0
[   11.520106]         ok 35 pc=88, width=12, value=0xe13
[   11.520161]         ok 36 pc=88, width=16, value=0xe146
[   11.520217]         ok 37 pc=95, width=8, value=0xf2
[   11.520270]         ok 38 pc=95, width=12, value=0xf32
[   11.520323]         ok 39 pc=95, width=16, value=0xf332
[   11.520376]         ok 40 pc=100, width=8, value=0xff
[   11.520428]         ok 41 pc=100, width=12, value=0xfff
[   11.520481]         ok 42 pc=100, width=16, value=0xffff
[   11.520483]     # test_percent_to_mbw_max: pass:42 fail:0 skip:0 total:42
[   11.520485]     ok 3 test_percent_to_mbw_max
[   11.520487]         KTAP version 1
[   11.520488]         # Subtest: test_mbw_max_to_percent_limits
[   11.520540]         ok 1 wd=1
[   11.520594]         ok 2 wd=2
[   11.520647]         ok 3 wd=3
[   11.520700]         ok 4 wd=4
[   11.520752]         ok 5 wd=5
[   11.520807]         ok 6 wd=6
[   11.520858]         ok 7 wd=7
[   11.520909]         ok 8 wd=8
[   11.520962]         ok 9 wd=9
[   11.521017]         ok 10 wd=10
[   11.521078]         ok 11 wd=11
[   11.521138]         ok 12 wd=12
[   11.521193]         ok 13 wd=13
[   11.521245]         ok 14 wd=14
[   11.521296]         ok 15 wd=15
[   11.521346]         ok 16 wd=16
[   11.521348]     # test_mbw_max_to_percent_limits: pass:16 fail:0 
skip:0 total:16
[   11.521350]     ok 4 test_mbw_max_to_percent_limits
[   11.521375]     # test_percent_to_max_rounding: Round-up rate: 43% 
(18/42)
[   11.521409]     ok 5 test_percent_to_max_rounding
[   11.521411]         KTAP version 1
[   11.521413]         # Subtest: test_percent_max_roundtrip_stability
[   11.521465]         ok 1 wd=1
[   11.521515]         ok 2 wd=2
[   11.521565]         ok 3 wd=3
[   11.521614]         ok 4 wd=4
[   11.521667]         ok 5 wd=5
[   11.521719]         ok 6 wd=6
[   11.521770]         ok 7 wd=7
[   11.521820]         ok 8 wd=8
[   11.521871]         ok 9 wd=9
[   11.521922]         ok 10 wd=10
[   11.521973]         ok 11 wd=11
[   11.522022]         ok 12 wd=12
[   11.522075]         ok 13 wd=13
[   11.522126]         ok 14 wd=14
[   11.522178]         ok 15 wd=15
[   11.522231]         ok 16 wd=16
[   11.522233]     # test_percent_max_roundtrip_stability: pass:16 
fail:0 skip:0 total:16
[   11.522235]     ok 6 test_percent_max_roundtrip_stability
[   11.522237] # mpam_resctrl_test_suite: pass:6 fail:0 skip:0 total:6
[   11.522238] # Totals: pass:118 fail:0 skip:0 total:118
[   11.522240] ok 2 mpam_resctrl_test_suite
~~~
All 118 MPAM KUnit tests passed.

Tested-by: Zeng Heng <zengheng4@huawei.com>


Best regards,
Zeng Heng


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox