Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND v3 1/3] KVM: arm64: Reset page order in pKVM hyp_pool
From: Vincent Donnefort @ 2026-05-21 14:36 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, kernel-team, qperret, tabba,
	Vincent Donnefort, Sashiko
In-Reply-To: <20260521143626.1005660-1-vdonnefort@google.com>

When a VM fails to initialise after its stage-2 hyp_pool has been
initialised, that stage-2 must be torn down entirely. This requires
resetting both the refcount and the order of its pages back to 0.

Currently, reclaim_pgtable_pages() implicitly resets the page order by
allocating the entire pool with order-0 granularity. However, in the VM
initialisation error path, the addresses of the donated memory (the PGD)
are already known, making it unnecessary to iterate over all pages in
the pool.

Since the vmemmap page order is a hyp_pool-specific field, leaving a
non-zero order on hyp_pool destruction is harmless until another pool
attempts to admit the page. Instead of resetting this field during
destruction, reset it during pool initialization in hyp_pool_init().

For 'external' pages, we can't trust the order either as they bypass
hyp_pool_init(). Since we never coalesce them, enforce order-0 to ensure
safe insertion into the pool.

This leaves no vmemmap order users outside of hyp_pool.

Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>

diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 25f04629014e..fa447d400b71 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -217,7 +217,6 @@ static void *guest_s2_zalloc_page(void *mc)
 	memset(addr, 0, PAGE_SIZE);
 	p = hyp_virt_to_page(addr);
 	p->refcount = 1;
-	p->order = 0;
 
 	return addr;
 }
@@ -322,7 +321,6 @@ void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
 	while (addr) {
 		page = hyp_virt_to_page(addr);
 		page->refcount = 0;
-		page->order = 0;
 		push_hyp_memcache(mc, addr, hyp_virt_to_phys);
 		WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1));
 		addr = hyp_alloc_pages(&vm->pool, 0);
diff --git a/arch/arm64/kvm/hyp/nvhe/page_alloc.c b/arch/arm64/kvm/hyp/nvhe/page_alloc.c
index a1eb27a1a747..57f86aa0f82f 100644
--- a/arch/arm64/kvm/hyp/nvhe/page_alloc.c
+++ b/arch/arm64/kvm/hyp/nvhe/page_alloc.c
@@ -94,13 +94,22 @@ static void __hyp_attach_page(struct hyp_pool *pool,
 			      struct hyp_page *p)
 {
 	phys_addr_t phys = hyp_page_to_phys(p);
-	u8 order = p->order;
 	struct hyp_page *buddy;
+	bool coalesce = true;
+	u8 order = p->order;
 
-	memset(hyp_page_to_virt(p), 0, PAGE_SIZE << p->order);
+	/*
+	 * 'external' pages are never coalesced and their ->order field
+	 * untrusted as they bypass hyp_pool_init(). Enforce order-0.
+	 */
+	if (phys < pool->range_start || phys >= pool->range_end) {
+		order = 0;
+		coalesce = false;
+	}
+
+	memset(hyp_page_to_virt(p), 0, PAGE_SIZE << order);
 
-	/* Skip coalescing for 'external' pages being freed into the pool. */
-	if (phys < pool->range_start || phys >= pool->range_end)
+	if (!coalesce)
 		goto insert;
 
 	/*
@@ -237,8 +246,10 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages,
 
 	/* Init the vmemmap portion */
 	p = hyp_phys_to_page(phys);
-	for (i = 0; i < nr_pages; i++)
+	for (i = 0; i < nr_pages; i++) {
 		hyp_set_page_refcounted(&p[i]);
+		p[i].order = 0;
+	}
 
 	/* Attach the unused pages to the buddy tree */
 	for (i = reserved_pages; i < nr_pages; i++)
-- 
2.54.0.746.g67dd491aae-goog



^ permalink raw reply related

* [RESEND v3 2/3] KVM: arm64: Fix __pkvm_init_vm error path
From: Vincent Donnefort @ 2026-05-21 14:36 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, kernel-team, qperret, tabba,
	Vincent Donnefort, Sashiko
In-Reply-To: <20260521143626.1005660-1-vdonnefort@google.com>

In the unlikely case where insert_vm_table_entry fails, __pkvm_init_vm
release the memory donated by the host for the PGD, but as the stage-2
is still set-up the hypervisor keeps a refcount on those pages,
effectively leaking the references.

Fix the rollback with the newly added kvm_guest_destroy_stage2().

Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>

diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index 3cbfae0e3dda..4f2b871199cb 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -56,6 +56,7 @@ int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot p
 int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id);
 int kvm_host_prepare_stage2(void *pgt_pool_base);
 int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd);
+void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm);
 void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt);
 
 int hyp_pin_shared_mem(void *from, void *to);
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index fa447d400b71..986e0ac1f2d8 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -305,16 +305,21 @@ int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd)
 	return 0;
 }
 
+void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm)
+{
+	guest_lock_component(vm);
+	kvm_pgtable_stage2_destroy(&vm->pgt);
+	vm->kvm.arch.mmu.pgd_phys = 0ULL;
+	guest_unlock_component(vm);
+}
+
 void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
 {
 	struct hyp_page *page;
 	void *addr;
 
 	/* Dump all pgtable pages in the hyp_pool */
-	guest_lock_component(vm);
-	kvm_pgtable_stage2_destroy(&vm->pgt);
-	vm->kvm.arch.mmu.pgd_phys = 0ULL;
-	guest_unlock_component(vm);
+	kvm_guest_destroy_stage2(vm);
 
 	/* Drain the hyp_pool into the memcache */
 	addr = hyp_alloc_pages(&vm->pool, 0);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index eb1c10120f9f..3b2c4fbc34d8 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -853,10 +853,12 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
 	/* Must be called last since this publishes the VM. */
 	ret = insert_vm_table_entry(handle, hyp_vm);
 	if (ret)
-		goto err_remove_mappings;
+		goto err_destroy_stage2;
 
 	return 0;
 
+err_destroy_stage2:
+	kvm_guest_destroy_stage2(hyp_vm);
 err_remove_mappings:
 	unmap_donated_memory(hyp_vm, vm_size);
 	unmap_donated_memory(pgd, pgd_size);
-- 
2.54.0.746.g67dd491aae-goog



^ permalink raw reply related

* [RESEND v3 3/3] KVM: arm64: Add fail-safe for refcounted pages in __pkvm_hyp_donate_host
From: Vincent Donnefort @ 2026-05-21 14:36 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, kernel-team, qperret, tabba,
	Vincent Donnefort
In-Reply-To: <20260521143626.1005660-1-vdonnefort@google.com>

A previous bug in __pkvm_init_vm error path showed that the hypervisor
could leak refcounted pages, (i.e. losing access to a page while its
refcount is still elevated). This poses a threat to the pKVM state
machine.

Address this by introducing a fail-safe in __pkvm_hyp_donate_host.
Transitions are not a hot path so added security is worth the extra
check.

Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>

diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 986e0ac1f2d8..fee55e49c132 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -854,6 +854,16 @@ static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_pa
 	return 0;
 }
 
+static int __hyp_check_page_count_range(phys_addr_t phys, u64 size)
+{
+	for_each_hyp_page(page, phys, size) {
+		if (page->refcount)
+			return -EBUSY;
+	}
+
+	return 0;
+}
+
 static bool guest_pte_is_poisoned(kvm_pte_t pte)
 {
 	if (kvm_pte_valid(pte))
@@ -1052,7 +1062,6 @@ int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn)
 int __pkvm_host_unshare_hyp(u64 pfn)
 {
 	u64 phys = hyp_pfn_to_phys(pfn);
-	u64 virt = (u64)__hyp_va(phys);
 	u64 size = PAGE_SIZE;
 	int ret;
 
@@ -1065,10 +1074,9 @@ int __pkvm_host_unshare_hyp(u64 pfn)
 	ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED);
 	if (ret)
 		goto unlock;
-	if (hyp_page_count((void *)virt)) {
-		ret = -EBUSY;
+	ret = __hyp_check_page_count_range(phys, size);
+	if (ret)
 		goto unlock;
-	}
 
 	__hyp_set_page_state_range(phys, size, PKVM_NOPAGE);
 	WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_OWNED));
@@ -1131,6 +1139,10 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages)
 	if (ret)
 		goto unlock;
 
+	ret = __hyp_check_page_count_range(phys, size);
+	if (ret)
+		goto unlock;
+
 	__hyp_set_page_state_range(phys, size, PKVM_NOPAGE);
 	WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size);
 	WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HOST));
-- 
2.54.0.746.g67dd491aae-goog



^ permalink raw reply related

* [RESEND v3 0/3] Fix __pkvm_init_vm error path
From: Vincent Donnefort @ 2026-05-21 14:36 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, kernel-team, qperret, tabba,
	Vincent Donnefort

Sashiko reported a potential refcount leak in the unlikely case where
insert_vm_table_entry fails.

While at it, I have added a fail-safe to __pkvm_hyp_donate_host to ensure this
function doesn't allow leaking refcounted pages.

Changes since v3:
 
  * Enforce order-0 for external pages, making the vmemmap ->order field
    completely private to hyp_pool.

Changes since v2 (https://lore.kernel.org/all/20260521102149.804874-1-vdonnefort@google.com/):
  
  * Proactively init hyp_page order field in hyp_pool_init  

v1 (https://lore.kernel.org/all/20260521081250.655226-1-vdonnefort@google.com/)


Vincent Donnefort (3):
  KVM: arm64: Reset page order in pKVM hyp_pool
  KVM: arm64: Fix __pkvm_init_vm error path
  KVM: arm64: Add fail-safe for refcounted pages in
    __pkvm_hyp_donate_host

 arch/arm64/kvm/hyp/include/nvhe/mem_protect.h |  1 +
 arch/arm64/kvm/hyp/nvhe/mem_protect.c         | 35 +++++++++++++------
 arch/arm64/kvm/hyp/nvhe/page_alloc.c          | 21 ++++++++---
 arch/arm64/kvm/hyp/nvhe/pkvm.c                |  4 ++-
 4 files changed, 45 insertions(+), 16 deletions(-)


base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8
-- 
2.54.0.746.g67dd491aae-goog



^ permalink raw reply

* Re: [PATCH v14 10/44] arm64: RMI: Add support for SRO
From: Marc Zyngier @ 2026-05-21 14:35 UTC (permalink / raw)
  To: Steven Price
  Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
	Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
	linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
	Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
	Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
	Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <20260513131757.116630-11-steven.price@arm.com>

On Wed, 13 May 2026 14:17:18 +0100,
Steven Price <steven.price@arm.com> wrote:
> 
> RMM v2.0 introduces the concept of "Stateful RMI Operations" (SRO). This
> means that an SMC can return with an operation still in progress. The
> host is excepted to continue the operation until is reaches a conclusion
> (either success or failure). During this process the RMM can request
> additional memory ('donate') or hand memory back to the host
> ('reclaim'). The host can request an in progress operation is cancelled,
> but still continue the operation until it has completed (otherwise the
> incomplete operation may cause future RMM operations to fail).
> 
> The SRO is tracked using a struct rmi_sro_state object which keeps track
> of any memory which has been allocated but not yet consumed by the RMM
> or reclaimed from the RMM. This allows the memory to be reused in a
> future request within the same operation. It will also permit an
> operation to be done in a context where memory allocation may be
> difficult (e.g. atomic context) with the option to abort the operation
> and retry the memory allocation outside of the atomic context. The
> memory stored in the struct rmi_sro_state object can then be reused on
> the subsequent attempt.
> 
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> v14:
>  * SRO support has improved although is still not fully complete. The
>    infrastructure has been moved out of KVM.
> ---
>  arch/arm64/include/asm/rmi_cmds.h |   1 +
>  arch/arm64/kernel/rmi.c           | 359 ++++++++++++++++++++++++++++++
>  2 files changed, 360 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
> index eb213c8e6f26..1a7b0c8f1e38 100644
> --- a/arch/arm64/include/asm/rmi_cmds.h
> +++ b/arch/arm64/include/asm/rmi_cmds.h
> @@ -35,6 +35,7 @@ struct rmi_sro_state {
>  
>  int rmi_delegate_range(phys_addr_t phys, unsigned long size);
>  int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
> +int free_delegated_page(phys_addr_t phys);
>  
>  static inline int rmi_delegate_page(phys_addr_t phys)
>  {
> diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
> index 08cef54acadb..a8107ca9bb6d 100644
> --- a/arch/arm64/kernel/rmi.c
> +++ b/arch/arm64/kernel/rmi.c
> @@ -48,6 +48,365 @@ int rmi_undelegate_range(phys_addr_t phys, unsigned long size)
>  	return ret;
>  }
>  
> +static unsigned long donate_req_to_size(unsigned long donatereq)
> +{
> +	unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
> +
> +	switch (unit_size) {
> +	case 0:
> +		return PAGE_SIZE;
> +	case 1:
> +		return PMD_SIZE;
> +	case 2:
> +		return PUD_SIZE;
> +	case 3:
> +		return P4D_SIZE;

How does this work when we have folded levels? If this is supposed to
be the architected size, then it should actively express that:

	return BIT(unit_size * (PAGE_SHIFT - 3) + PAGE_SHIFT);

> +	}
> +	unreachable();
> +}
> +
> +static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,
> +			     struct arm_smccc_1_2_regs *regs_out)
> +{
> +	struct arm_smccc_1_2_regs regs = *regs_in;
> +	unsigned long status;
> +
> +	do {
> +		arm_smccc_1_2_invoke(&regs, regs_out);
> +		status = RMI_RETURN_STATUS(regs_out->a0);
> +	} while (status == RMI_BUSY || status == RMI_BLOCKED);
> +}
> +
> +int free_delegated_page(phys_addr_t phys)
> +{
> +	if (WARN_ON(rmi_undelegate_page(phys))) {

Please drop this WARN_ON(). Or at least make it ONCE. Everywhere.

> +		/* Undelegate failed: leak the page */
> +		return -EBUSY;
> +	}
> +
> +	free_page((unsigned long)phys_to_virt(phys));
> +
> +	return 0;
> +}
> +
> +static int rmi_sro_ensure_capacity(struct rmi_sro_state *sro,
> +				   unsigned long count)
> +{
> +	if (WARN_ON_ONCE(sro->addr_count > RMI_MAX_ADDR_LIST))
> +		return -EOVERFLOW;
> +
> +	if (count > RMI_MAX_ADDR_LIST - sro->addr_count)
> +		return -ENOSPC;
> +
> +	return 0;
> +}
> +
> +static int rmi_sro_donate_contig(struct rmi_sro_state *sro,
> +				 unsigned long sro_handle,
> +				 unsigned long donatereq,
> +				 struct arm_smccc_1_2_regs *out_regs,
> +				 gfp_t gfp)
> +{
> +	unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
> +	unsigned long unit_size_bytes = donate_req_to_size(donatereq);
> +	unsigned long count = RMI_DONATE_COUNT(donatereq);
> +	unsigned long state = RMI_DONATE_STATE(donatereq);
> +	unsigned long size = unit_size_bytes * count;
> +	unsigned long addr_range;
> +	int ret;
> +	void *virt;
> +	phys_addr_t phys;
> +	struct arm_smccc_1_2_regs regs = {
> +		SMC_RMI_OP_MEM_DONATE,
> +		sro_handle
> +	};
> +
> +	for (int i = 0; i < sro->addr_count; i++) {
> +		unsigned long entry = sro->addr_list[i];
> +
> +		if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
> +		    RMI_ADDR_RANGE_COUNT(entry) == count &&
> +		    RMI_ADDR_RANGE_STATE(entry) == state) {
> +			sro->addr_count--;
> +			swap(sro->addr_list[sro->addr_count],
> +			     sro->addr_list[i]);
> +
> +			goto out;
> +		}
> +	}
> +
> +	ret = rmi_sro_ensure_capacity(sro, 1);
> +	if (ret)
> +		return ret;
> +
> +	virt = alloc_pages_exact(size, gfp);
> +	if (!virt)
> +		return -ENOMEM;
> +	phys = virt_to_phys(virt);
> +
> +	if (state == RMI_OP_MEM_DELEGATED) {
> +		if (rmi_delegate_range(phys, size)) {
> +			free_pages_exact(virt, size);
> +			return -ENXIO;
> +		}
> +	}
> +
> +	addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
> +	FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
> +	FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, count);
> +	FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
> +
> +	sro->addr_list[sro->addr_count] = addr_range;
> +

Shouldn't this be moved to a helper that ensures capacity, and returns
an error otherwise?

> +out:
> +	regs.a2 = virt_to_phys(&sro->addr_list[sro->addr_count]);
> +	regs.a3 = 1;

This could really do with context specific helpers that populate regs
based on a set of parameters. I have no idea what this 1 here is, and
the init is spread over too much code. Think of the children!

That's valid for the whole patch.

	M.
> +	rmi_smccc_invoke(&regs, out_regs);
> +
> +	unsigned long donated_granules = out_regs->a1;
> +	unsigned long donated_size = donated_granules << PAGE_SHIFT;
> +
> +	if (donated_granules == 0) {
> +		/* No pages used by the RMM */
> +		sro->addr_count++;
> +	} else if (donated_size < size) {
> +		phys = sro->addr_list[sro->addr_count] & RMI_ADDR_RANGE_ADDR_MASK;
> +
> +		/* Not all granules used by the RMM, free the remaining pages */
> +		for (long i = donated_size; i < size; i += PAGE_SIZE) {
> +			if (state == RMI_OP_MEM_DELEGATED)
> +				free_delegated_page(phys + i);
> +			else
> +				__free_page(phys_to_page(phys + i));
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,
> +				    unsigned long sro_handle,
> +				    unsigned long donatereq,
> +				    struct arm_smccc_1_2_regs *out_regs,
> +				    gfp_t gfp)
> +{
> +	unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
> +	unsigned long unit_size_bytes = donate_req_to_size(donatereq);
> +	unsigned long count = RMI_DONATE_COUNT(donatereq);
> +	unsigned long state = RMI_DONATE_STATE(donatereq);
> +	unsigned long found = 0;
> +	unsigned long addr_list_start = sro->addr_count;
> +	int ret;
> +	struct arm_smccc_1_2_regs regs = {
> +		SMC_RMI_OP_MEM_DONATE,
> +		sro_handle
> +	};
> +
> +	for (int i = 0; i < addr_list_start && found < count; i++) {
> +		unsigned long entry = sro->addr_list[i];
> +
> +		if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
> +		    RMI_ADDR_RANGE_COUNT(entry) == 1 &&
> +		    RMI_ADDR_RANGE_STATE(entry) == state) {
> +			addr_list_start--;
> +			swap(sro->addr_list[addr_list_start],
> +			     sro->addr_list[i]);
> +			found++;
> +			i--;
> +		}
> +	}
> +
> +	ret = rmi_sro_ensure_capacity(sro, count - found);
> +	if (ret)
> +		return ret;
> +
> +	while (found < count) {
> +		unsigned long addr_range;
> +		void *virt = alloc_pages_exact(unit_size_bytes, gfp);
> +		phys_addr_t phys;
> +
> +		if (!virt)
> +			return -ENOMEM;
> +
> +		phys = virt_to_phys(virt);
> +
> +		if (state == RMI_OP_MEM_DELEGATED) {
> +			if (rmi_delegate_range(phys, unit_size_bytes)) {
> +				free_pages_exact(virt, unit_size_bytes);
> +				return -ENXIO;
> +			}
> +		}
> +
> +		addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
> +		FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
> +		FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, 1);
> +		FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
> +
> +		sro->addr_list[sro->addr_count++] = addr_range;
> +		found++;
> +	}
> +
> +	regs.a2 = virt_to_phys(&sro->addr_list[addr_list_start]);
> +	regs.a3 = found;
> +	rmi_smccc_invoke(&regs, out_regs);
> +
> +	unsigned long donated_granules = out_regs->a1;
> +
> +	if (WARN_ON(donated_granules & ((unit_size_bytes >> PAGE_SHIFT) - 1))) {
> +		/*
> +		 * FIXME: RMM has only consumed part of a huge page, this leaks
> +		 * the rest of the huge page
> +		 */
> +		donated_granules = ALIGN(donated_granules,
> +					 (unit_size_bytes >> PAGE_SHIFT));
> +	}
> +	unsigned long donated_blocks = donated_granules / (unit_size_bytes >> PAGE_SHIFT);
> +
> +	if (WARN_ON(donated_blocks > found))
> +		donated_blocks = found;
> +
> +	unsigned long undonated_blocks = found - donated_blocks;
> +
> +	while (donated_blocks && undonated_blocks) {
> +		sro->addr_count--;
> +		swap(sro->addr_list[addr_list_start],
> +		     sro->addr_list[sro->addr_count]);
> +		addr_list_start++;
> +
> +		donated_blocks--;
> +		undonated_blocks--;
> +	}
> +	sro->addr_count -= donated_blocks;
> +
> +	return 0;
> +}
> +
> +static int rmi_sro_donate(struct rmi_sro_state *sro,
> +			  unsigned long sro_handle,
> +			  unsigned long donatereq,
> +			  struct arm_smccc_1_2_regs *regs,
> +			  gfp_t gfp)
> +{
> +	unsigned long count = RMI_DONATE_COUNT(donatereq);
> +
> +	if (WARN_ON(!count))
> +		return 0;
> +
> +	if (RMI_DONATE_CONTIG(donatereq)) {
> +		return rmi_sro_donate_contig(sro, sro_handle, donatereq,
> +					     regs, gfp);
> +	} else {
> +		return rmi_sro_donate_noncontig(sro, sro_handle, donatereq,
> +						regs, gfp);
> +	}
> +}
> +
> +static int rmi_sro_reclaim(struct rmi_sro_state *sro,
> +			   unsigned long sro_handle,
> +			   struct arm_smccc_1_2_regs *out_regs)
> +{
> +	unsigned long capacity;
> +	struct arm_smccc_1_2_regs regs;
> +	int ret;
> +
> +	ret = rmi_sro_ensure_capacity(sro, 1);
> +	if (ret)
> +		rmi_sro_free(sro);
> +
> +	capacity = RMI_MAX_ADDR_LIST - sro->addr_count;
> +
> +	regs = (struct arm_smccc_1_2_regs){
> +		SMC_RMI_OP_MEM_RECLAIM,
> +		sro_handle,
> +		virt_to_phys(&sro->addr_list[sro->addr_count]),
> +		capacity
> +	};
> +	rmi_smccc_invoke(&regs, out_regs);
> +
> +	if (WARN_ON_ONCE(out_regs->a1 > capacity))
> +		out_regs->a1 = capacity;
> +
> +	sro->addr_count += out_regs->a1;
> +
> +	return 0;
> +}
> +
> +void rmi_sro_free(struct rmi_sro_state *sro)
> +{
> +	for (int i = 0; i < sro->addr_count; i++) {
> +		unsigned long entry = sro->addr_list[i];
> +		unsigned long addr = RMI_ADDR_RANGE_ADDR(entry);
> +		unsigned long unit_size = RMI_ADDR_RANGE_SIZE(entry);
> +		unsigned long count = RMI_ADDR_RANGE_COUNT(entry);
> +		unsigned long state = RMI_ADDR_RANGE_STATE(entry);
> +		unsigned long size = donate_req_to_size(unit_size) * count;
> +
> +		if (state == RMI_OP_MEM_DELEGATED) {
> +			if (WARN_ON(rmi_undelegate_range(addr, size))) {
> +				/* Leak the pages */
> +				continue;
> +			}
> +		}
> +		free_pages_exact(phys_to_virt(addr), size);
> +	}
> +
> +	sro->addr_count = 0;
> +}
> +
> +unsigned long rmi_sro_execute(struct rmi_sro_state *sro, gfp_t gfp)
> +{
> +	unsigned long sro_handle;
> +	struct arm_smccc_1_2_regs regs;
> +	struct arm_smccc_1_2_regs *regs_in = &sro->regs;
> +
> +	rmi_smccc_invoke(regs_in, &regs);
> +
> +	sro_handle = regs.a1;
> +
> +	while (RMI_RETURN_STATUS(regs.a0) == RMI_INCOMPLETE) {
> +		bool can_cancel = RMI_RETURN_CAN_CANCEL(regs.a0);
> +		int ret;
> +
> +		switch (RMI_RETURN_MEMREQ(regs.a0)) {
> +		case RMI_OP_MEM_REQ_NONE:
> +			regs = (struct arm_smccc_1_2_regs){
> +				SMC_RMI_OP_CONTINUE, sro_handle, 0
> +			};
> +			rmi_smccc_invoke(&regs, &regs);
> +			break;
> +		case RMI_OP_MEM_REQ_DONATE:
> +			ret = rmi_sro_donate(sro, sro_handle, regs.a2, &regs,
> +					     gfp);
> +			break;
> +		case RMI_OP_MEM_REQ_RECLAIM:
> +			ret = rmi_sro_reclaim(sro, sro_handle, &regs);
> +			break;
> +		default:
> +			ret = WARN_ON(1);
> +			break;
> +		}
> +
> +		if (ret) {
> +			if (can_cancel) {
> +				/*
> +				 * FIXME: Handle cancelling properly!
> +				 *
> +				 * If the operation has failed due to memory
> +				 * allocation failure then the information on
> +				 * the memory allocation should be saved, so
> +				 * that the allocation can be repeated outside
> +				 * of any context which prevented the
> +				 * allocation.

Honestly, this is the sort of stuff that I'd expect to be solved
*before* posting this code. Since this is so central to the whole
memory management, it needs to be correct from day-1.

If you can't make it work in time, then tone the supported features
down. But FIXMEs and WARN_ONs are not the way to go.

	M.

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


^ permalink raw reply

* Re: [PATCH v5 4/8] nvmem: Add the Raspberry Pi OTP driver
From: Thomas Weißschuh @ 2026-05-21 14:34 UTC (permalink / raw)
  To: Gregor Herburger
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Ray Jui, Scott Branden, Broadcom internal kernel review list,
	Eric Anholt, Stefan Wahren, Srinivas Kandagatla, Kees Cook,
	Gustavo A. R. Silva, devicetree, linux-rpi-kernel,
	linux-arm-kernel, linux-kernel, linux-hardening
In-Reply-To: <20260520-rpi-otp-driver-v5-4-b26e5908eeac@linutronix.de>

On Wed, May 20, 2026 at 04:27:56PM +0200, Gregor Herburger wrote:
> Raspberry Pis have OTP registers which can be accessed through the
> videocore firmware. Add a nvmem driver to support these OTP registers.
> 
> Reviewed-by: Stefan Wahren <wahrenst@gmx.net>
> Signed-off-by: Gregor Herburger <gregor.herburger@linutronix.de>

Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>

> ---
>  drivers/nvmem/Kconfig                      |  11 +++
>  drivers/nvmem/Makefile                     |   1 +
>  drivers/nvmem/raspberrypi-otp.c            | 130 +++++++++++++++++++++++++++++
>  include/soc/bcm2835/raspberrypi-firmware.h |  10 +++
>  4 files changed, 152 insertions(+)

(...)

> +static const struct platform_device_id raspberrypi_otp_id[] = {
> +	{ "raspberrypi-otp" },
> +	{},

The last sentinel element should not have a trailing comma,
to make sure nobody places an element after it by accident.

> +};
> +MODULE_DEVICE_TABLE(platform, raspberrypi_otp_id);

(...)


^ permalink raw reply

* [PATCH v3 1/3] KVM: arm64: Reset page order in pKVM hyp_pool
From: Vincent Donnefort @ 2026-05-21 14:33 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, kernel-team, qperret, tabba,
	Vincent Donnefort, Sashiko
In-Reply-To: <20260521143318.1002172-1-vdonnefort@google.com>

When a VM fails to initialise after its stage-2 hyp_pool has been
initialised, that stage-2 must be torn down entirely. This requires
resetting both the refcount and the order of its pages back to 0.

Currently, reclaim_pgtable_pages() implicitly resets the page order by
allocating the entire pool with order-0 granularity. However, in the VM
initialisation error path, the addresses of the donated memory (the PGD)
are already known, making it unnecessary to iterate over all pages in
the pool.

Since the vmemmap page order is a hyp_pool-specific field, leaving a
non-zero order on hyp_pool destruction is harmless until another pool
attempts to admit the page. Instead of resetting this field during
destruction, reset it during pool initialization in hyp_pool_init().

For 'external' pages, we can't trust the order either as they bypass
hyp_pool_init(). Since we never coalesce them, enforce order-0 to ensure
safe insertion into the pool.

This leaves no vmemmap order users outside of hyp_pool.

Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>

diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 25f04629014e..fa447d400b71 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -217,7 +217,6 @@ static void *guest_s2_zalloc_page(void *mc)
 	memset(addr, 0, PAGE_SIZE);
 	p = hyp_virt_to_page(addr);
 	p->refcount = 1;
-	p->order = 0;
 
 	return addr;
 }
@@ -322,7 +321,6 @@ void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
 	while (addr) {
 		page = hyp_virt_to_page(addr);
 		page->refcount = 0;
-		page->order = 0;
 		push_hyp_memcache(mc, addr, hyp_virt_to_phys);
 		WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1));
 		addr = hyp_alloc_pages(&vm->pool, 0);
diff --git a/arch/arm64/kvm/hyp/nvhe/page_alloc.c b/arch/arm64/kvm/hyp/nvhe/page_alloc.c
index a1eb27a1a747..57f86aa0f82f 100644
--- a/arch/arm64/kvm/hyp/nvhe/page_alloc.c
+++ b/arch/arm64/kvm/hyp/nvhe/page_alloc.c
@@ -94,13 +94,22 @@ static void __hyp_attach_page(struct hyp_pool *pool,
 			      struct hyp_page *p)
 {
 	phys_addr_t phys = hyp_page_to_phys(p);
-	u8 order = p->order;
 	struct hyp_page *buddy;
+	bool coalesce = true;
+	u8 order = p->order;
 
-	memset(hyp_page_to_virt(p), 0, PAGE_SIZE << p->order);
+	/*
+	 * 'external' pages are never coalesced and their ->order field
+	 * untrusted as they bypass hyp_pool_init(). Enforce order-0.
+	 */
+	if (phys < pool->range_start || phys >= pool->range_end) {
+		order = 0;
+		coalesce = false;
+	}
+
+	memset(hyp_page_to_virt(p), 0, PAGE_SIZE << order);
 
-	/* Skip coalescing for 'external' pages being freed into the pool. */
-	if (phys < pool->range_start || phys >= pool->range_end)
+	if (!coalesce)
 		goto insert;
 
 	/*
@@ -237,8 +246,10 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages,
 
 	/* Init the vmemmap portion */
 	p = hyp_phys_to_page(phys);
-	for (i = 0; i < nr_pages; i++)
+	for (i = 0; i < nr_pages; i++) {
 		hyp_set_page_refcounted(&p[i]);
+		p[i].order = 0;
+	}
 
 	/* Attach the unused pages to the buddy tree */
 	for (i = reserved_pages; i < nr_pages; i++)
-- 
2.54.0.746.g67dd491aae-goog



^ permalink raw reply related

* [PATCH v3 0/3] Fix __pkvm_init_vm error path
From: Vincent Donnefort @ 2026-05-21 14:33 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, kernel-team, qperret, tabba,
	Vincent Donnefort

Sashiko reported a potential refcount leak in the unlikely case where
insert_vm_table_entry fails.

While at it, I have added a fail-safe to __pkvm_hyp_donate_host to ensure this
function doesn't allow leaking refcounted pages.

Changes since v3:
 
  * Enforce order-0 for external pages, making the vmemmap ->order field
    completely private to hyp_pool.

Changes since v2 (https://lore.kernel.org/all/20260521102149.804874-1-vdonnefort@google.com/):
  
  * Proactively init hyp_page order field in hyp_pool_init  

v1 (https://lore.kernel.org/all/20260521081250.655226-1-vdonnefort@google.com/)

Vincent Donnefort (3):
  KVM: arm64: Reset page order in pKVM hyp_pool
  KVM: arm64: Fix __pkvm_init_vm error path
  KVM: arm64: Add fail-safe for refcounted pages in
    __pkvm_hyp_donate_host

 arch/arm64/kvm/hyp/include/nvhe/mem_protect.h |  1 +
 arch/arm64/kvm/hyp/nvhe/mem_protect.c         | 35 +++++++++++++------
 arch/arm64/kvm/hyp/nvhe/page_alloc.c          | 21 ++++++++---
 arch/arm64/kvm/hyp/nvhe/pkvm.c                |  4 ++-
 4 files changed, 45 insertions(+), 16 deletions(-)


base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8
-- 
2.54.0.746.g67dd491aae-goog



^ permalink raw reply

* Re: [PATCH v5 2/8] nvmem: core: Enforce stride and alignment checks for nvmem_device functions
From: Thomas Weißschuh @ 2026-05-21 14:32 UTC (permalink / raw)
  To: Gregor Herburger
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Ray Jui, Scott Branden, Broadcom internal kernel review list,
	Eric Anholt, Stefan Wahren, Srinivas Kandagatla, Kees Cook,
	Gustavo A. R. Silva, devicetree, linux-rpi-kernel,
	linux-arm-kernel, linux-kernel, linux-hardening
In-Reply-To: <20260520-rpi-otp-driver-v5-2-b26e5908eeac@linutronix.de>

On Wed, May 20, 2026 at 04:27:54PM +0200, Gregor Herburger wrote:
> The stride and word_size attributes in the nvmem_config struct are
> currently only used when reading/writing through sysfs functions
> bin_attr_nvmem_read/bin_attr_nvmem_write and in the nvmem_cell api.
> Reads and writes with nvmem_device_write/nvmem_device_read still allow
> unaligned access.
> 
> Add a check to these functions to enforce word_size and stride_length
> aligned reads and writes.
> 
> Signed-off-by: Gregor Herburger <gregor.herburger@linutronix.de>

Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>

> ---
>  drivers/nvmem/core.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)

(...)


^ permalink raw reply

* Re: [PATCH v5 1/8] soc: bcm2835: Use IS_REACHABLE for function declaration
From: Thomas Weißschuh @ 2026-05-21 14:32 UTC (permalink / raw)
  To: Gregor Herburger
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Ray Jui, Scott Branden, Broadcom internal kernel review list,
	Eric Anholt, Stefan Wahren, Srinivas Kandagatla, Kees Cook,
	Gustavo A. R. Silva, devicetree, linux-rpi-kernel,
	linux-arm-kernel, linux-kernel, linux-hardening
In-Reply-To: <20260520-rpi-otp-driver-v5-1-b26e5908eeac@linutronix.de>

On Wed, May 20, 2026 at 04:27:53PM +0200, Gregor Herburger wrote:
> The drivers that depend on the RASPBERRYPI_FIRMWARE use
> 
> 	depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
> 
> This should ensure that the driver is not compiled in when
> RASPBERRYPI_FIRMWARE is 'm' on COMPILE_TEST which leads to linker
> errors.
> 
> The same can be achieved by using IS_REACHABLE in the
> raspberrypi-firmware header. This evaluates to false when invoked from
> built-in code. This way the Kconfig can be written as
> 
> 	depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST
> 
> Which is a more readable variant.
> 
> Signed-off-by: Gregor Herburger <gregor.herburger@linutronix.de>

Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>

(...)


^ permalink raw reply

* Re: [PATCH v7 18/28] media: rockchip: rga: use card type to specify rga type
From: Michael Tretter @ 2026-05-21 14:28 UTC (permalink / raw)
  To: Sven Püschel
  Cc: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
	Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Hans Verkuil, linux-media, linux-rockchip, linux-arm-kernel,
	linux-kernel, devicetree, kernel, nicolas, sebastian.reichel,
	p.zabel, Nicolas Dufresne
In-Reply-To: <20260521-spu-rga3-v7-18-3f33e8c7145f@pengutronix.de>

On Thu, 21 May 2026 00:44:23 +0200, Sven Püschel wrote:
> In preparation of the RGA3 support add a filed to the rga_hw struct

*field

> to specify the desired card type value. This allows the user to
> differentiate the RGA2 and RGA3 video device nodes.
> 
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
> ---
>  drivers/media/platform/rockchip/rga/rga-hw.c | 1 +
>  drivers/media/platform/rockchip/rga/rga.c    | 4 +++-
>  drivers/media/platform/rockchip/rga/rga.h    | 1 +
>  3 files changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
> index f2900812ba76f..43fd023b7571c 100644
> --- a/drivers/media/platform/rockchip/rga/rga-hw.c
> +++ b/drivers/media/platform/rockchip/rga/rga-hw.c
> @@ -577,6 +577,7 @@ static struct rga_fmt formats[] = {
>  };
>  
>  const struct rga_hw rga2_hw = {
> +	.card_type = "rga2",

This changes the card type from "rockchip-rga" to "rga2", which is a
user space visible change and may break user space. Is this acceptable
or even intentional?

Michael

>  	.formats = formats,
>  	.num_formats = ARRAY_SIZE(formats),
>  	.cmdbuf_size = RGA_CMDBUF_SIZE,
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index 22954bbae55fc..91775b43ff617 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -354,8 +354,10 @@ static const struct v4l2_file_operations rga_fops = {
>  static int
>  vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
>  {
> +	struct rockchip_rga *rga = video_drvdata(file);
> +
>  	strscpy(cap->driver, RGA_NAME, sizeof(cap->driver));
> -	strscpy(cap->card, "rockchip-rga", sizeof(cap->card));
> +	strscpy(cap->card, rga->hw->card_type, sizeof(cap->card));
>  	strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info));
>  
>  	return 0;
> diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
> index df525c6aea8b6..cee2e75ea89f1 100644
> --- a/drivers/media/platform/rockchip/rga/rga.h
> +++ b/drivers/media/platform/rockchip/rga/rga.h
> @@ -149,6 +149,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
>  };
>  
>  struct rga_hw {
> +	const char *card_type;
>  	struct rga_fmt *formats;
>  	u32 num_formats;
>  	size_t cmdbuf_size;
> 
> -- 
> 2.54.0
> 
> 


^ permalink raw reply

* Re: [PATCH v14 08/44] arm64: RMI: Ensure that the RMM has GPT entries for memory
From: Marc Zyngier @ 2026-05-21 14:24 UTC (permalink / raw)
  To: Steven Price
  Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
	Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
	linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
	Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
	Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
	Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <868q9cx4ac.wl-maz@kernel.org>

On Thu, 21 May 2026 14:47:55 +0100,
Marc Zyngier <maz@kernel.org> wrote:
> 
> On Wed, 13 May 2026 14:17:16 +0100,
> Steven Price <steven.price@arm.com> wrote:
> > 
> > +/*
> > + * For now we set the tracking_region_size to 0 for RMI_RMM_CONFIG_SET().
> > + * TODO: Support other tracking sizes (via Kconfig option).
> > + */
> > +#ifdef CONFIG_PAGE_SIZE_4KB
> > +#define RMM_GRANULE_TRACKING_SIZE	SZ_1G
> > +#elif defined(CONFIG_PAGE_SIZE_16KB)
> > +#define RMM_GRANULE_TRACKING_SIZE	SZ_32M
> > +#elif defined(CONFIG_PAGE_SIZE_64KB)
> > +#define RMM_GRANULE_TRACKING_SIZE	SZ_512M
> > +#endif
> 
> Basically, a level 2 mapping. Which means this whole block really is:
> 
> #define RMM_GRANULE_TRAKING_SIZE	(2 * PAGE_SHIFT - 3)

Obviously wrong:

#define RMM_GRANULE_TRAKING_SIZE	BIT(2 * PAGE_SHIFT - 3)

	M.

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


^ permalink raw reply

* Re: [PATCH 18/43] KVM: arm64: gic-v5: Define remaining IRS MMIO registers
From: Sascha Bischoff @ 2026-05-21 14:22 UTC (permalink / raw)
  To: maz@kernel.org
  Cc: yuzenghui@huawei.com, Timothy Hayes, Suzuki Poulose, nd,
	peter.maydell@linaro.org, kvmarm@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, kvm@vger.kernel.org,
	Joey Gouly, lpieralisi@kernel.org, oliver.upton@linux.dev
In-Reply-To: <86pl37xnl8.wl-maz@kernel.org>

On Thu, 2026-05-07 at 16:10 +0100, Marc Zyngier wrote:
> On Mon, 27 Apr 2026 17:12:11 +0100,
> Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> > 
> > Complete the set of defined IRS MMIO registers in the GICv5 header
> > file. Up until now, the set of defined IRS MMIO registers has been
> > driven by code requirements. However, in order to properly emulate
> > the
> > IRS MMIO interface in KVM, the complete set of IRS MMIO registers
> > needs to be added.
> 
> I really think you need to pick a register update "style". Either you
> add all the definitions in one go, or you add them with the patch
> that
> start making use of it.
> 
> My preference goes with the former.

I've gone with the former. In my new series, this is one of the first
changes, and I've consolidated the addition of GICv5 register
definitions there.

> 
> > 
> > Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> > ---
> >  include/linux/irqchip/arm-gic-v5.h | 105
> > ++++++++++++++++++++++++++---
> >  1 file changed, 96 insertions(+), 9 deletions(-)
> > 
> > diff --git a/include/linux/irqchip/arm-gic-v5.h
> > b/include/linux/irqchip/arm-gic-v5.h
> > index 54b573783cd75..9ea3674a6613b 100644
> > --- a/include/linux/irqchip/arm-gic-v5.h
> > +++ b/include/linux/irqchip/arm-gic-v5.h
> > @@ -62,6 +62,14 @@
> >  #define GICV5_OUTER_SHARE		0b10
> >  #define GICV5_INNER_SHARE		0b11
> >  
> > +#define GICV5_AIDR_COMPONENT_IRS	0b00
> > +#define GICV5_AIDR_COMPONENT_ITS	0b01
> > +#define GICV5_AIDR_COMPONENT_IWB	0b10
> > +
> > +#define GICV5_AIDR_ARCH_MAJ_REV_V5	0
> > +#define GICV5_AIDR_ARCH_MIN_REV_V0	0
> > +#define GICV5_IIDR_IMPLEMENTER_ARM	0x43b
> > +
> 
> I'm in two minds about this. We used the same hack (advertising this
> as an ARM implementation) for GICv2/v3, but that's really not one.
> 
> And in any case, the ARM encoding should not leave in this file -- it
> is only used for the KVM implementation, and everything else should
> be
> implementation agnostic (until we need to implement a workaround, of
> course...).

This was actually stale - the IRS MMIO code used KVM's existing
definition, and I'd just failed to drop this from the header. Aside
from that, I've left things as they are for the time being.

Thanks,
Sascha

> 
> Thanks,
> 
> 	M.
> 


^ permalink raw reply

* Re: [PATCH v7 15/28] media: rockchip: rga: align stride to 4 bytes
From: Michael Tretter @ 2026-05-21 14:22 UTC (permalink / raw)
  To: Sven Püschel
  Cc: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
	Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Hans Verkuil, linux-media, linux-rockchip, linux-arm-kernel,
	linux-kernel, devicetree, kernel, nicolas, sebastian.reichel,
	p.zabel, Nicolas Dufresne
In-Reply-To: <20260521-spu-rga3-v7-15-3f33e8c7145f@pengutronix.de>

On Thu, 21 May 2026 00:44:20 +0200, Sven Püschel wrote:
> Add an alignment setting to rga_hw to set the desired stride alignment.
> As the RGA2 register for the stride counts in word units, the code
> already divides the bytesperline value by 4 when writing it into the
> register. Therefore fix the alignment to a multiple of 4 to avoid
> potential off by one errors due from the division.
> 
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>

Reviewed-by: Michael Tretter <m.tretter@pengutronix.de>

> 
> ---
> Changed in v6:
> - Drop unintended change from fmt->fourcc to pix_fmt->pixelformat
>   Flagged by Sashiko:
>   https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=15
> ---
>  drivers/media/platform/rockchip/rga/rga-hw.c |  1 +
>  drivers/media/platform/rockchip/rga/rga.c    | 11 ++++++-----
>  drivers/media/platform/rockchip/rga/rga.h    |  1 +
>  3 files changed, 8 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
> index 9881c14f908d5..dac3cb6aa17d3 100644
> --- a/drivers/media/platform/rockchip/rga/rga-hw.c
> +++ b/drivers/media/platform/rockchip/rga/rga-hw.c
> @@ -580,6 +580,7 @@ const struct rga_hw rga2_hw = {
>  	.max_width = MAX_WIDTH,
>  	.min_height = MIN_HEIGHT,
>  	.max_height = MAX_HEIGHT,
> +	.stride_alignment = 4,
>  
>  	.start = rga_hw_start,
>  	.handle_irq = rga_handle_irq,
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index bf6bbcbfc869b..d080cb672740b 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -234,10 +234,10 @@ static int rga_open(struct file *file)
>  	ctx->in = def_frame;
>  	ctx->out = def_frame;
>  
> -	v4l2_fill_pixfmt_mp(&ctx->in.pix,
> -			    ctx->in.fmt->fourcc, def_width, def_height);
> -	v4l2_fill_pixfmt_mp(&ctx->out.pix,
> -			    ctx->out.fmt->fourcc, def_width, def_height);
> +	v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc,
> +				    def_width, def_height, rga->hw->stride_alignment);
> +	v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc,
> +				    def_width, def_height, rga->hw->stride_alignment);
>  
>  	if (mutex_lock_interruptible(&rga->mutex)) {
>  		ret = -ERESTARTSYS;
> @@ -393,7 +393,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
>  		fmt = &hw->formats[0];
>  
>  	v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize);
> -	v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height);
> +	v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc,
> +				    pix_fmt->width, pix_fmt->height, hw->stride_alignment);
>  	pix_fmt->field = V4L2_FIELD_NONE;
>  
>  	return 0;
> diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
> index 04aeb7b429523..38518146910a6 100644
> --- a/drivers/media/platform/rockchip/rga/rga.h
> +++ b/drivers/media/platform/rockchip/rga/rga.h
> @@ -150,6 +150,7 @@ struct rga_hw {
>  	size_t cmdbuf_size;
>  	u32 min_width, min_height;
>  	u32 max_width, max_height;
> +	u8 stride_alignment;
>  
>  	void (*start)(struct rockchip_rga *rga,
>  		      struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
> 
> -- 
> 2.54.0
> 
> 

-- 
Pengutronix e.K.                           | Michael Tretter             |
Steuerwalder Str. 21                       | https://www.pengutronix.de/ |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |


^ permalink raw reply

* Re: [PATCH v7 14/28] media: rockchip: rga: move cmdbuf to rga_ctx
From: Michael Tretter @ 2026-05-21 14:20 UTC (permalink / raw)
  To: Sven Püschel
  Cc: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
	Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Hans Verkuil, linux-media, linux-rockchip, linux-arm-kernel,
	linux-kernel, devicetree, kernel, nicolas, sebastian.reichel,
	p.zabel, Nicolas Dufresne
In-Reply-To: <20260521-spu-rga3-v7-14-3f33e8c7145f@pengutronix.de>

On Thu, 21 May 2026 00:44:19 +0200, Sven Püschel wrote:
> Move the command buffer to the rga_ctx struct in preparation to reuse
> an already prepared command buffer. This allows to split the command
> buffer setup in a further commit to setup a template for the command
> buffer at streamon and only update the buffer addresses in device_run
> and trigger the command stream. No sync point is added, as one command
> buffer should only be used for one conversion at a time.
> 
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>

Reviewed-by: Michael Tretter <m.tretter@pengutronix.de>

> ---
>  drivers/media/platform/rockchip/rga/rga-hw.c | 28 +++++++---------
>  drivers/media/platform/rockchip/rga/rga.c    | 48 ++++++++++++++++------------
>  drivers/media/platform/rockchip/rga/rga.h    |  5 +--
>  3 files changed, 41 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
> index 17f7a67c0b4bb..9881c14f908d5 100644
> --- a/drivers/media/platform/rockchip/rga/rga-hw.c
> +++ b/drivers/media/platform/rockchip/rga/rga-hw.c
> @@ -122,8 +122,7 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct
>  
>  static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
>  {
> -	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	unsigned int reg;
>  
>  	reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG;
> @@ -135,8 +134,7 @@ static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
>  
>  static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
>  {
> -	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	unsigned int reg;
>  
>  	reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG;
> @@ -148,8 +146,7 @@ static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
>  
>  static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
>  {
> -	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	unsigned int reg;
>  
>  	reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG;
> @@ -162,7 +159,7 @@ static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
>  static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
>  {
>  	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	unsigned int scale_dst_w, scale_dst_h;
>  	unsigned int src_h, src_w, dst_h, dst_w;
>  	union rga_src_info src_info;
> @@ -322,8 +319,7 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx,
>  				 struct rga_addr_offset *offset)
>  {
>  	struct rga_corners_addr_offset src_offsets;
> -	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	unsigned int src_h, src_w, src_x, src_y;
>  
>  	src_h = ctx->in.crop.height;
> @@ -350,8 +346,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
>  {
>  	struct rga_addr_offset *dst_offset;
>  	struct rga_corners_addr_offset offsets;
> -	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	unsigned int dst_h, dst_w, dst_x, dst_y;
>  	unsigned int mir_mode = 0;
>  	unsigned int rot_mode = 0;
> @@ -397,8 +392,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
>  
>  static void rga_cmd_set_mode(struct rga_ctx *ctx)
>  {
> -	struct rockchip_rga *rga = ctx->rga;
> -	u32 *dest = rga->cmdbuf_virt;
> +	u32 *dest = ctx->cmdbuf_virt;
>  	union rga_mode_ctrl mode;
>  	union rga_alpha_ctrl0 alpha_ctrl0;
>  	union rga_alpha_ctrl1 alpha_ctrl1;
> @@ -423,7 +417,7 @@ static void rga_cmd_set(struct rga_ctx *ctx,
>  {
>  	struct rockchip_rga *rga = ctx->rga;
>  
> -	memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
> +	memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
>  
>  	rga_cmd_set_src_addr(ctx, src->dma_desc_pa);
>  	/*
> @@ -439,11 +433,11 @@ static void rga_cmd_set(struct rga_ctx *ctx,
>  	rga_cmd_set_dst_info(ctx, &dst->offset);
>  	rga_cmd_set_trans_info(ctx);
>  
> -	rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy);
> +	rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy);
>  
>  	/* sync CMD buf for RGA */
> -	dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy,
> -		PAGE_SIZE, DMA_BIDIRECTIONAL);
> +	dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
> +				   PAGE_SIZE, DMA_BIDIRECTIONAL);
>  }
>  
>  static void rga_hw_start(struct rockchip_rga *rga,
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index 77b8c7ab74274..bf6bbcbfc869b 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -219,6 +219,16 @@ static int rga_open(struct file *file)
>  	ctx = kzalloc_obj(*ctx);
>  	if (!ctx)
>  		return -ENOMEM;
> +
> +	/* Create CMD buffer */
> +	ctx->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
> +					   &ctx->cmdbuf_phy, GFP_KERNEL,
> +					   DMA_ATTR_WRITE_COMBINE);
> +	if (!ctx->cmdbuf_virt) {
> +		ret = -ENOMEM;
> +		goto rel_ctx;
> +	}
> +
>  	ctx->rga = rga;
>  	/* Set default formats */
>  	ctx->in = def_frame;
> @@ -230,15 +240,13 @@ static int rga_open(struct file *file)
>  			    ctx->out.fmt->fourcc, def_width, def_height);
>  
>  	if (mutex_lock_interruptible(&rga->mutex)) {
> -		kfree(ctx);
> -		return -ERESTARTSYS;
> +		ret = -ERESTARTSYS;
> +		goto rel_cmdbuf;
>  	}
>  	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init);
>  	if (IS_ERR(ctx->fh.m2m_ctx)) {
>  		ret = PTR_ERR(ctx->fh.m2m_ctx);
> -		mutex_unlock(&rga->mutex);
> -		kfree(ctx);
> -		return ret;
> +		goto unlock_mutex;
>  	}
>  	v4l2_fh_init(&ctx->fh, video_devdata(file));
>  	v4l2_fh_add(&ctx->fh, file);
> @@ -252,6 +260,15 @@ static int rga_open(struct file *file)
>  	mutex_unlock(&rga->mutex);
>  
>  	return 0;
> +
> +unlock_mutex:
> +	mutex_unlock(&rga->mutex);
> +rel_cmdbuf:
> +	dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
> +		       ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
> +rel_ctx:
> +	kfree(ctx);
> +	return ret;
>  }
>  
>  static int rga_release(struct file *file)
> @@ -266,6 +283,10 @@ static int rga_release(struct file *file)
>  	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>  	v4l2_fh_del(&ctx->fh, file);
>  	v4l2_fh_exit(&ctx->fh);
> +
> +	dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
> +		       ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
> +
>  	kfree(ctx);
>  
>  	mutex_unlock(&rga->mutex);
> @@ -720,19 +741,10 @@ static int rga_probe(struct platform_device *pdev)
>  
>  	pm_runtime_put(rga->dev);
>  
> -	/* Create CMD buffer */
> -	rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
> -					   &rga->cmdbuf_phy, GFP_KERNEL,
> -					   DMA_ATTR_WRITE_COMBINE);
> -	if (!rga->cmdbuf_virt) {
> -		ret = -ENOMEM;
> -		goto rel_m2m;
> -	}
> -
>  	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
>  	if (ret) {
>  		v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
> -		goto free_dma;
> +		goto rel_m2m;
>  	}
>  
>  	v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n",
> @@ -740,9 +752,6 @@ static int rga_probe(struct platform_device *pdev)
>  
>  	return 0;
>  
> -free_dma:
> -	dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
> -		       rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
>  rel_m2m:
>  	v4l2_m2m_release(rga->m2m_dev);
>  rel_vdev:
> @@ -759,9 +768,6 @@ static void rga_remove(struct platform_device *pdev)
>  {
>  	struct rockchip_rga *rga = platform_get_drvdata(pdev);
>  
> -	dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
> -		       rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
> -
>  	v4l2_info(&rga->v4l2_dev, "Removing\n");
>  
>  	v4l2_m2m_release(rga->m2m_dev);
> diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
> index 27b3c9b4f220c..04aeb7b429523 100644
> --- a/drivers/media/platform/rockchip/rga/rga.h
> +++ b/drivers/media/platform/rockchip/rga/rga.h
> @@ -53,6 +53,9 @@ struct rga_ctx {
>  	struct rga_frame out;
>  	struct v4l2_ctrl_handler ctrl_handler;
>  
> +	void *cmdbuf_virt;
> +	dma_addr_t cmdbuf_phy;
> +
>  	int osequence;
>  	int csequence;
>  
> @@ -89,8 +92,6 @@ struct rockchip_rga {
>  	spinlock_t ctrl_lock;
>  
>  	struct rga_ctx *curr;
> -	dma_addr_t cmdbuf_phy;
> -	void *cmdbuf_virt;
>  
>  	const struct rga_hw *hw;
>  };
> 
> -- 
> 2.54.0
> 
> 


^ permalink raw reply

* Re: [PATCH 17/43] KVM: arm64: gic-v5: Enable VPE DBs on VPE reset and disable on teardown
From: Sascha Bischoff @ 2026-05-21 14:17 UTC (permalink / raw)
  To: maz@kernel.org
  Cc: yuzenghui@huawei.com, Timothy Hayes, Suzuki Poulose, nd,
	peter.maydell@linaro.org, kvmarm@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, kvm@vger.kernel.org,
	Joey Gouly, lpieralisi@kernel.org, oliver.upton@linux.dev
In-Reply-To: <861pfozijq.wl-maz@kernel.org>

On Wed, 2026-05-06 at 16:03 +0100, Marc Zyngier wrote:
> On Mon, 27 Apr 2026 17:11:50 +0100,
> Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> > 
> > Ensure that each VPE doorbell is enabled on the host prior to
> > running
> > the guest by enabling it as part of vgic_v5_reset(). Add a function
> > to
> > disable it again as part of VM teardown, vgic_v5_disable_vcpu(),
> > which
> > also frees the IRQ again.
> > 
> > Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> > ---
> >  arch/arm64/kvm/vgic/vgic-v5.c | 29 +++++++++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> > 
> > diff --git a/arch/arm64/kvm/vgic/vgic-v5.c
> > b/arch/arm64/kvm/vgic/vgic-v5.c
> > index 9347bc6895223..48ffcb2494db3 100644
> > --- a/arch/arm64/kvm/vgic/vgic-v5.c
> > +++ b/arch/arm64/kvm/vgic/vgic-v5.c
> > @@ -736,6 +736,31 @@ void vgic_v5_reset(struct kvm_vcpu *vcpu)
> >  		kvm_vm_dead(vcpu->kvm);
> >  		return;
> >  	}
> > +
> > +	enable_irq(vgic_v5_vpe_db(vcpu));
> 
> Odd. Why do you need to manage the enabling of the doorbell? I see
> from the previous patch that you set it as IRQ_NOAUTOEN, but this
> seems to be cargo-culted from GICv4 (which really needs it).
> 
> My expectations that since the doorbell is controlled from KVM and is
> only one-shot, we should be OK. This is the flow that v4.1 adopts,
> without a need to baby-sit the interrupt state.

I've reworked this to make most of it go away. We didn't need the
IRQ_NOAUTOEN (it was just copied from GICv4), and that means that much
of this can go away. In general, I've rolled these changes into the
ones that init and tear down GICv5 VMs in general given how tightly
coupled they are in the first place.

> 
> > +}
> > +
> > +static void vgic_v5_disable_vcpu(struct kvm_vcpu *vcpu)
> > +{
> > +	int virq = vgic_v5_vpe_db(vcpu);
> > +
> > +	if (!vcpu->kvm->arch.vgic.gicv5_vm.domain)
> > +		return;
> > +
> > +	if (!virq)
> > +		return;
> > +
> > +	/*
> > +	 * We are called in the vgic_v5_teardown path. We no
> > longer need the
> > +	 * doorbell virqs.
> > +	 */
> > +	disable_irq(virq);
> 
> And then this can go as well.

Yup, gone.

> 
> > +
> > +	/* Free the doorbell irq (counter-part to request_irq)*/
> > +	free_irq(virq, vcpu);
> > +
> > +	/* Remove the irq from the domain too */
> > +	irq_domain_free_irqs(virq, 1);
> 
> Surely you remove the domain at some point. That should cover the
> freeing of all the doorbells.

Again, I've reworked and consolidated this.

> 
> >  }
> >  
> >  int vgic_v5_map_resources(struct kvm *kvm)
> > @@ -875,6 +900,10 @@ void vgic_v5_teardown(struct kvm *kvm)
> >  			kvm_err("Failed to release VM 0x%x\n",
> > dist->gicv5_vm.vm_id);
> >  	}
> >  
> > +	/* Goodbye doorbells */
> > +	kvm_for_each_vcpu(i, vcpu, kvm)
> > +		vgic_v5_disable_vcpu(vcpu);
> > +
> >  	vgic_v5_teardown_per_vm_domain(&kvm->arch.vgic.gicv5_vm);
> >  
> >  	vgic_v5_release_vm_id(kvm);
> 
> I reckon this patch would be better folded into the previous one, so
> that we can see both the requesting and freeing of the doorbell
> interrupt in the same patch.

Agreed, and that's what I've done now.

Thanks,
Sascha

> 
> Thanks,
> 
> 	M.
> 


^ permalink raw reply

* Re: [PATCH] firmware: smccc: Fix Arm SMCCC SOC_ID name call
From: Sudeep Holla @ 2026-05-21 14:18 UTC (permalink / raw)
  To: Mark Rutland, Lorenzo Pieralisi, Sudeep Holla, Andre Przywara
  Cc: Paul Benoit, linux-arm-kernel, linux-kernel
In-Reply-To: <20250902172053.304911-1-andre.przywara@arm.com>

On Tue, 02 Sep 2025 18:20:53 +0100, Andre Przywara wrote:
> Commit 5f9c23abc477 ("firmware: smccc: Support optional Arm SMCCC SOC_ID
> name") introduced the SOC_ID name string call, which reports a human
> readable string describing the SoC, as returned by firmware.
> The SMCCC spec v1.6 describes this feature as AArch64 only, since we rely
> on 8 characters to be transmitted per register. Consequently the SMCCC
> call must use the AArch64 calling convention, which requires bit 30 of
> the FID to be set. The spec is a bit confusing here, since it mentions
> that in the parameter description ("2: SoC name (optionally implemented for
> SMC64 calls, ..."), but still prints the FID explicitly as 0x80000002.
> But as this FID is using the SMC32 calling convention (correct for the
> other two calls), it will not match what mainline TF-A is expecting, so
> any call would return NOT_SUPPORTED.
> 
> [...]

Applied to sudeep.holla/linux (for-next/ffa/updates), thanks!

[1/1] firmware: smccc: Fix Arm SMCCC SOC_ID name call
      https://git.kernel.org/sudeep.holla/c/70492cfce2a4
--
Regards,
Sudeep



^ permalink raw reply

* Re: [PATCH v7 13/28] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info
From: Michael Tretter @ 2026-05-21 14:17 UTC (permalink / raw)
  To: Sven Püschel
  Cc: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
	Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Hans Verkuil, linux-media, linux-rockchip, linux-arm-kernel,
	linux-kernel, devicetree, kernel, nicolas, sebastian.reichel,
	p.zabel, Nicolas Dufresne
In-Reply-To: <20260521-spu-rga3-v7-13-3f33e8c7145f@pengutronix.de>

On Thu, 21 May 2026 00:44:18 +0200, Sven Püschel wrote:
> Calculate the x_div and y_div variables with the information from
> v4l2_format_info instead of storing these in the rga_fmt struct.
> 
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>

Reviewed-by: Michael Tretter <m.tretter@pengutronix.de>

> ---
>  drivers/media/platform/rockchip/rga/rga-hw.c | 45 +++++++---------------------
>  drivers/media/platform/rockchip/rga/rga.h    |  2 --
>  2 files changed, 11 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
> index 40498796507e0..17f7a67c0b4bb 100644
> --- a/drivers/media/platform/rockchip/rga/rga-hw.c
> +++ b/drivers/media/platform/rockchip/rga/rga-hw.c
> @@ -42,6 +42,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
>  {
>  	struct rga_corners_addr_offset offsets;
>  	struct rga_addr_offset *lt, *lb, *rt, *rb;
> +	const struct v4l2_format_info *format_info;
>  	unsigned int x_div = 0,
>  		     y_div = 0, uv_stride = 0, pixel_width = 0;
>  
> @@ -50,8 +51,16 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
>  	rt = &offsets.right_top;
>  	rb = &offsets.right_bottom;
>  
> -	x_div = frm->fmt->x_div;
> -	y_div = frm->fmt->y_div;
> +	format_info = v4l2_format_info(frm->pix.pixelformat);
> +	/* x_div is only used for the u/v planes.
> +	 * When the format doesn't have these, use 1 to avoid a division by zero.
> +	 */
> +	if (format_info->bpp[1])
> +		x_div = format_info->hdiv * format_info->bpp_div[1] /
> +			format_info->bpp[1];
> +	else
> +		x_div = 1;
> +	y_div = format_info->vdiv;
>  	uv_stride = frm->stride / x_div;
>  	pixel_width = frm->stride / frm->pix.width;
>  
> @@ -476,128 +485,96 @@ static struct rga_fmt formats[] = {
>  		.color_swap = RGA_COLOR_ALPHA_SWAP,
>  		.hw_format = RGA_COLOR_FMT_ABGR8888,
>  		.depth = 32,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_ABGR32,
>  		.color_swap = RGA_COLOR_RB_SWAP,
>  		.hw_format = RGA_COLOR_FMT_ABGR8888,
>  		.depth = 32,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_XBGR32,
>  		.color_swap = RGA_COLOR_RB_SWAP,
>  		.hw_format = RGA_COLOR_FMT_XBGR8888,
>  		.depth = 32,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_RGB24,
>  		.color_swap = RGA_COLOR_NONE_SWAP,
>  		.hw_format = RGA_COLOR_FMT_RGB888,
>  		.depth = 24,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_BGR24,
>  		.color_swap = RGA_COLOR_RB_SWAP,
>  		.hw_format = RGA_COLOR_FMT_RGB888,
>  		.depth = 24,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_ARGB444,
>  		.color_swap = RGA_COLOR_RB_SWAP,
>  		.hw_format = RGA_COLOR_FMT_ABGR4444,
>  		.depth = 16,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_ARGB555,
>  		.color_swap = RGA_COLOR_RB_SWAP,
>  		.hw_format = RGA_COLOR_FMT_ABGR1555,
>  		.depth = 16,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_RGB565,
>  		.color_swap = RGA_COLOR_RB_SWAP,
>  		.hw_format = RGA_COLOR_FMT_BGR565,
>  		.depth = 16,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_NV21,
>  		.color_swap = RGA_COLOR_UV_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV420SP,
>  		.depth = 12,
> -		.y_div = 2,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_NV61,
>  		.color_swap = RGA_COLOR_UV_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV422SP,
>  		.depth = 16,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_NV12,
>  		.color_swap = RGA_COLOR_NONE_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV420SP,
>  		.depth = 12,
> -		.y_div = 2,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_NV12M,
>  		.color_swap = RGA_COLOR_NONE_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV420SP,
>  		.depth = 12,
> -		.y_div = 2,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_NV16,
>  		.color_swap = RGA_COLOR_NONE_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV422SP,
>  		.depth = 16,
> -		.y_div = 1,
> -		.x_div = 1,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_YUV420,
>  		.color_swap = RGA_COLOR_NONE_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV420P,
>  		.depth = 12,
> -		.y_div = 2,
> -		.x_div = 2,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_YUV422P,
>  		.color_swap = RGA_COLOR_NONE_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV422P,
>  		.depth = 16,
> -		.y_div = 1,
> -		.x_div = 2,
>  	},
>  	{
>  		.fourcc = V4L2_PIX_FMT_YVU420,
>  		.color_swap = RGA_COLOR_UV_SWAP,
>  		.hw_format = RGA_COLOR_FMT_YUV420P,
>  		.depth = 12,
> -		.y_div = 2,
> -		.x_div = 2,
>  	},
>  };
>  
> diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
> index 640e510285341..27b3c9b4f220c 100644
> --- a/drivers/media/platform/rockchip/rga/rga.h
> +++ b/drivers/media/platform/rockchip/rga/rga.h
> @@ -20,8 +20,6 @@
>  struct rga_fmt {
>  	u32 fourcc;
>  	int depth;
> -	u8 y_div;
> -	u8 x_div;
>  	u8 color_swap;
>  	u8 hw_format;
>  };
> 
> -- 
> 2.54.0
> 
> 

-- 
Pengutronix e.K.                           | Michael Tretter             |
Steuerwalder Str. 21                       | https://www.pengutronix.de/ |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |


^ permalink raw reply

* Re: [PATCH v12 5/5] mfd: Add support for MediaTek SPMI PMICs and MT6363/73
From: Lee Jones @ 2026-05-21 14:13 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno
  Cc: linux-mediatek, robh, krzk+dt, conor+dt, matthias.bgg, lgirdwood,
	broonie, devicetree, linux-kernel, linux-arm-kernel, kernel,
	wenst, Nícolas F. R. A. Prado
In-Reply-To: <20260511101355.122478-6-angelogioacchino.delregno@collabora.com>

On Mon, 11 May 2026, AngeloGioacchino Del Regno wrote:

> This driver adds support for the MediaTek SPMI PMICs and their
> interrupt controller (which is present in 95% of the cases).
> 
> Other than probing all of the sub-devices of a SPMI PMIC, this
> sets up a regmap from the relevant SPMI bus and initializes an
> interrupt controller with its irq domain and irqchip to handle
> chained interrupts, with the SPMI bus itself being its parent
> irq controller, and the PMIC being the outmost device.
> 
> This driver hence holds all of the information about a specific
> PMIC's interrupts and will properly handle them, calling the
> ISR for any subdevice that requested an interrupt.
> 
> As for the interrupt spec, this driver wants either three or
> two interrupt cells, but in the case 3 were given it ignores
> the first one: this is because of how this first revision of
> of the MediaTek SPMI 2.0 Controller works, which doesn't hold
> hold irq number information in its register, but delegates
> that to the SPMI device - it's possible that this will change
> in the future with a newer revision of the controller IP, and
> this is the main reason for that.
> 
> To make use of this implementation, this driver also adds the
> required bits to support MediaTek MT6363 and MT6373 SPMI PMICs.
> 
> Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> ---
>  drivers/mfd/Kconfig                       |  16 +
>  drivers/mfd/Makefile                      |   1 +
>  drivers/mfd/mtk-spmi-pmic.c               | 427 ++++++++++++++++++++++
>  include/linux/mfd/mt63x3_spmi/registers.h |  34 ++
>  4 files changed, 478 insertions(+)
>  create mode 100644 drivers/mfd/mtk-spmi-pmic.c
>  create mode 100644 include/linux/mfd/mt63x3_spmi/registers.h
> 
> diff --git "a/drivers/mfd/Kconfig" "b/drivers/mfd/Kconfig"
> index 7192c9d1d268..3e9acdf648b7 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1148,6 +1148,22 @@ config MFD_MT6397
>  	  accessing the device; additional drivers must be enabled in order
>  	  to use the functionality of the device.
>  
> +config MFD_MTK_SPMI_PMIC
> +	tristate "MediaTek SPMI PMICs"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	depends on OF
> +	depends on SPMI
> +	select REGMAP_SPMI
> +	help
> +	  Say yes here to enable support for MediaTek's SPMI PMICs.
> +	  These PMICs made their first appearance in board designs using the
> +	  MediaTek Dimensity 9400 series of SoCs.
> +	  Note that this will only be useful when paired with descriptions
> +	  of the independent functions as child nodes in the device tree.
> +
> +	  Say M here if you want to include support for the MediaTek SPMI
> +	  PMICs as a module. The module will be called "mtk-spmi-pmic".
> +
>  config MFD_MENF21BMC
>  	tristate "MEN 14F021P00 Board Management Controller Support"
>  	depends on I2C
> diff --git "a/drivers/mfd/Makefile" "b/drivers/mfd/Makefile"
> index e75e8045c28a..e00d283450c6 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -190,6 +190,7 @@ obj-$(CONFIG_MFD_MT6360)	+= mt6360-core.o
>  obj-$(CONFIG_MFD_MT6370)	+= mt6370.o
>  mt6397-objs			:= mt6397-core.o mt6397-irq.o mt6358-irq.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397.o
> +obj-$(CONFIG_MFD_MTK_SPMI_PMIC)	+= mtk-spmi-pmic.o
>  
>  obj-$(CONFIG_RZ_MTU3)		+= rz-mtu3.o
>  obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
> diff --git "a/drivers/mfd/mtk-spmi-pmic.c" "b/drivers/mfd/mtk-spmi-pmic.c
> new file mode 100644
> index 000000000000..d1fc8156e696
> --- /dev/null
> +++ b/drivers/mfd/mtk-spmi-pmic.c
> @@ -0,0 +1,427 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * MediaTek SPMI PMICs Driver
> + *
> + * Copyright (c) 2024 MediaTek Inc.
> + * Copyright (c) 2025 Collabora Ltd
> + *
> + * Authors:
> + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/gfp.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/mt63x3_spmi/registers.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/regmap.h>
> +#include <linux/spmi.h>
> +#include <linux/types.h>
> +
> +#define MTK_SPMI_PMIC_VAL_BITS		8
> +#define MTK_SPMI_PMIC_REG_CHIP_ID	0xb
> +#define MTK_SPMI_PMIC_RCS_IRQ_DONE	0x41b
> +
> +/**
> + * struct mtk_spmi_pmic_irq_group - Group of interrupts in SPMI PMIC
> + * @num_int_regs: Number of registers for this group of interrupts
> + * @con_reg:      PMIC Interrupt Group Control 0 register
> + * @sta_reg:      PMIC Interrupt Group Status 0 register
> + * @group_num:    PMIC Interrupt Group number - also corresponds to the
> + *                status bit in the global IRQ Control register
> + */
> +struct mtk_spmi_pmic_irq_grp {
> +	u8 hwirq_base;
> +	u8 num_int_regs;
> +	u16 con_reg;
> +	u16 sta_reg;
> +	u8 group_num;
> +};
> +
> +/**
> + * struct mtk_spmi_pmic_variant - SPMI PMIC variant-specific data
> + * @pmic_irq:    Group of interrupts in SPMI PMIC
> + * @num_groups:  Number of groups of interrupts
> + * @con_reg_len: Length in bytes of Control registers, depends on
> + *               existence of SET and CLR registers in the layout
> + * @irq_grp_reg: Global interrupt status register, explains which
> + *               group needs attention because of a group IRQ;
> + *               if this is zero, it means that there is only one
> + *               group and the device has no irqgroup register
> + * @chip_id_reg: Chip ID Register
> + */
> +struct mtk_spmi_pmic_variant {
> +	const struct mtk_spmi_pmic_irq_grp *pmic_irq;
> +	u8 num_groups;
> +	u8 con_reg_len;
> +	u8 irq_grp_reg;
> +	u8 chip_id_reg;
> +};

Where is chip_id_reg used?

> +
> +/**
> + * struct mtk_spmi_pmic - Main driver structure
> + * @variant:  SPMI PMIC variant-specific data
> + * @dev:      Handle to SPMI Device
> + * @dom:      IRQ Domain of the PMIC's interrupt controller
> + * @regmap:   Handle to PMIC regmap
> + * @irq:      PMIC chained interrupt
> + */
> +struct mtk_spmi_pmic {
> +	const struct mtk_spmi_pmic_variant *variant;
> +	struct device *dev;
> +	struct irq_domain *dom;
> +	struct regmap *regmap;
> +	int irq;
> +};
> +
> +static void mtk_spmi_pmic_irq_set_unmasking(struct irq_data *d, bool unmask)
> +{
> +	struct mtk_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
> +	const struct mtk_spmi_pmic_variant *variant = pmic->variant;
> +	struct regmap *regmap = pmic->regmap;
> +	irq_hw_number_t hwirq = irqd_to_hwirq(d);
> +	unsigned short i;
> +
> +	for (i = 0; i < variant->num_groups; i++) {

for (ini i = 0; ...

Did I not bring this up last time?

> +		const struct mtk_spmi_pmic_irq_grp *irq_grp = &variant->pmic_irq[i];
> +		u32 con_reg;
> +		u8 irq_en_bit;
> +
> +		if (hwirq < irq_grp->hwirq_base)
> +			continue;
> +
> +		con_reg = irq_grp->con_reg + (variant->con_reg_len * i);
> +		irq_en_bit = hwirq - irq_grp->hwirq_base;
> +		regmap_assign_bits(regmap, con_reg, BIT(irq_en_bit), unmask);
> +
> +		break;
> +	}
> +}
> +
> +static void mtk_spmi_pmic_irq_mask(struct irq_data *d)
> +{
> +	mtk_spmi_pmic_irq_set_unmasking(d, false);
> +}
> +
> +static void mtk_spmi_pmic_irq_unmask(struct irq_data *d)
> +{
> +	mtk_spmi_pmic_irq_set_unmasking(d, true);
> +}
> +
> +static struct irq_chip mtk_spmi_pmic_irq_chip = {
> +	.name = "mtk-spmi-pmic",
> +	.irq_mask = mtk_spmi_pmic_irq_mask,
> +	.irq_unmask = mtk_spmi_pmic_irq_unmask,
> +	.flags = IRQCHIP_SKIP_SET_WAKE,
> +};
> +
> +static struct lock_class_key mtk_spmi_pmic_irq_lock_class, mtk_spmi_pmic_irq_request_class;
> +
> +static int mtk_spmi_pmic_irq_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
> +				       unsigned long *out_hwirq, unsigned int *out_type)
> +{
> +	struct mtk_spmi_pmic *pmic = d->host_data;
> +	u32 intsize = fwspec->param_count;
> +	u32 *intspec = fwspec->param;
> +	unsigned int irq_type_index;
> +	unsigned int irq_num_index;
> +
> +	/*
> +	 * Interrupt cell index - For interrupt size 3:
> +	 * [0] - SID Interrupt number
> +	 * [1] - SPMI PMIC (Sub-)Device Interrupt number
> +	 * [2] - Interrupt Type mask
> +	 *
> +	 * When only two cells are specified the SID Interrupt is not present.
> +	 */
> +	if (intsize != 2 && intsize != 3) {
> +		dev_err(pmic->dev, "Expected IRQ specifier of size 2 or 3, got %u\n", intsize);
> +		return -EINVAL;
> +	}
> +
> +	/* irq_num_index refers to the SPMI (Sub-)Device Interrupt number */
> +	irq_num_index = intsize - 2;
> +	irq_type_index = irq_num_index + 1;
> +
> +	/*
> +	 * For 3 cells, the IRQ number in intspec[0] is ignored on purpose here!
> +	 *
> +	 * This is because of how at least the first revision of the SPMI 2.0
> +	 * controller works in MediaTek SoCs: the controller will raise an
> +	 * interrupt for each SID (but doesn't know the details), and the
> +	 * specific IRQ number that got raised must be read from the PMIC or
> +	 * its sub-device driver.
> +	 * It's possible that this will change in the future with a newer
> +	 * revision of the SPMI controller, and this is why the device tree
> +	 * holds the full interrupt specifier.
> +	 *
> +	 * out_hwirq: SPMI PMIC (Sub-)Device Interrupt number
> +	 * out_type:  Interrupt type sense mask
> +	 */
> +	*out_hwirq = intspec[irq_num_index];
> +	*out_type = intspec[irq_type_index] & IRQ_TYPE_SENSE_MASK;
> +
> +	return 0;
> +}
> +
> +static int mtk_spmi_pmic_irq_alloc(struct irq_domain *d, unsigned int virq,
> +				   unsigned int nr_irqs, void *data)
> +{
> +	struct mtk_spmi_pmic *pmic = d->host_data;
> +	struct irq_fwspec *fwspec = data;
> +	irq_hw_number_t hwirq;
> +	unsigned int irqtype;
> +	int i, ret;
> +
> +	ret = mtk_spmi_pmic_irq_translate(d, fwspec, &hwirq, &irqtype);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < nr_irqs; i++) {

As above.

> +		irq_set_lockdep_class(virq, &mtk_spmi_pmic_irq_lock_class,
> +				      &mtk_spmi_pmic_irq_request_class);
> +		irq_domain_set_info(d, virq, hwirq, &mtk_spmi_pmic_irq_chip,
> +				    pmic, handle_level_irq, NULL, NULL);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct irq_domain_ops mtk_spmi_pmic_irq_domain_ops = {
> +	.alloc = mtk_spmi_pmic_irq_alloc,
> +	.free = irq_domain_free_irqs_common,
> +	.translate = mtk_spmi_pmic_irq_translate,
> +};
> +
> +static int mtk_spmi_pmic_handle_group_irq(struct mtk_spmi_pmic *pmic, int group)
> +{
> +	const struct mtk_spmi_pmic_irq_grp *irq_grp = &pmic->variant->pmic_irq[group];
> +	struct regmap *regmap = pmic->regmap;
> +	struct device *dev = pmic->dev;
> +	int i, ret;

Etc.

> +
> +	for (i = 0; i < irq_grp->num_int_regs; i++) {
> +		u32 status, saved_status;
> +
> +		ret = regmap_read(regmap, irq_grp->sta_reg + i, &status);
> +		if (ret) {
> +			dev_err(dev, "Could not read IRQ status register: %d", ret);
> +			return ret;
> +		}
> +
> +		if (status == 0)
> +			continue;
> +
> +		saved_status = status;
> +		do {
> +			irq_hw_number_t hwirq;
> +			u8 bit = __ffs(status);
> +
> +			/* Each register has 8 bits: this is the first IRQ of this group */
> +			hwirq = MTK_SPMI_PMIC_VAL_BITS * i;
> +
> +			/* Offset by this group's start interrupt */
> +			hwirq += irq_grp->hwirq_base;
> +
> +			/* Finally, offset by the fired IRQ's bit number */
> +			hwirq += bit;
> +
> +			status &= ~BIT(bit);
> +
> +			generic_handle_domain_irq_safe(pmic->dom, hwirq);
> +		} while (status);
> +
> +		/* Clear the interrupts by writing the previous status */
> +		regmap_write(regmap, irq_grp->sta_reg + i, saved_status);
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_spmi_pmic_handle_chained_irq(struct irq_desc *desc)
> +{
> +	struct mtk_spmi_pmic *pmic = irq_desc_get_handler_data(desc);
> +	const struct mtk_spmi_pmic_variant *variant = pmic->variant;
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct regmap *regmap = pmic->regmap;
> +	bool irq_handled = false;
> +	int i, ret;
> +	u32 val;
> +
> +	chained_irq_enter(chip, desc);
> +
> +	/* If irq_grp_reg is present there are multiple IRQ groups */
> +	if (variant->irq_grp_reg > 0) {
> +		ret = regmap_read(regmap, variant->irq_grp_reg, &val);
> +		if (ret)
> +			handle_bad_irq(desc);
> +
> +		/* This is very unlikely to happen */
> +		if (val == 0) {
> +			chained_irq_exit(chip, desc);
> +			return;
> +		}
> +	} else {
> +		val = BIT(0);
> +	}
> +
> +	for (i = 0; i < variant->num_groups; i++) {
> +		const struct mtk_spmi_pmic_irq_grp *irq_grp = &variant->pmic_irq[i];
> +		u8 group_bit = BIT(irq_grp[i].group_num);

Did you mean to index twice here?

irq_grp[i + i].group_num

> +
> +		if (val & group_bit) {
> +			ret = mtk_spmi_pmic_handle_group_irq(pmic, i);
> +			if (ret == 0)
> +				irq_handled = true;
> +		}
> +	}
> +
> +	/* The RCS flag has to be cleared even if the IRQ was not handled. */
> +	ret = regmap_write(regmap, MTK_SPMI_PMIC_RCS_IRQ_DONE, 1);
> +	if (ret)
> +		dev_warn(pmic->dev, "Could not clear RCS flag!\n");
> +
> +	if (!irq_handled)
> +		handle_bad_irq(desc);
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void mtk_spmi_pmic_irq_remove(void *data)
> +{
> +	struct mtk_spmi_pmic *pmic = (struct mtk_spmi_pmic *)data;

You shouldn't need from a void.

> +
> +	irq_set_chained_handler_and_data(pmic->irq, NULL, NULL);
> +	irq_domain_remove(pmic->dom);
> +}
> +
> +static int mtk_spmi_pmic_irq_init(struct device *dev, struct regmap *regmap,
> +				  const struct mtk_spmi_pmic_variant *variant)
> +{
> +	struct fwnode_handle *fwnode = of_fwnode_handle(dev->of_node);
> +	struct mtk_spmi_pmic *pmic;
> +	int ret;
> +
> +	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
> +	if (!pmic)
> +		return -ENOMEM;
> +
> +	pmic->irq = of_irq_get(dev->of_node, 0);
> +	if (pmic->irq < 0)
> +		return dev_err_probe(dev, pmic->irq, "Cannot get IRQ\n");
> +
> +	pmic->dev = dev;
> +	pmic->regmap = regmap;
> +	pmic->variant = variant;
> +
> +	pmic->dom = irq_domain_create_tree(fwnode, &mtk_spmi_pmic_irq_domain_ops, pmic);
> +	if (!pmic->dom)
> +		return dev_err_probe(dev, -ENOMEM, "Cannot create IRQ domain\n");
> +
> +	ret = devm_add_action_or_reset(dev, mtk_spmi_pmic_irq_remove, pmic);
> +	if (ret) {
> +		irq_domain_remove(pmic->dom);

You just double-freed.

> +		return ret;
> +	}
> +
> +	irq_set_chained_handler_and_data(pmic->irq, mtk_spmi_pmic_handle_chained_irq, pmic);
> +
> +	return 0;
> +}
> +
> +#define MTK_SPMI_PMIC_IRQ_GROUP(pmic, group_name, group_index, first_irq, last_irq)	\
> +{											\
> +	.hwirq_base = first_irq,							\
> +	.num_int_regs = ((last_irq - first_irq) / MTK_SPMI_PMIC_VAL_BITS) + 1,		\
> +	.con_reg = pmic##_REG_##group_name##_TOP_INT_CON0,				\
> +	.sta_reg = pmic##_REG_##group_name##_TOP_INT_STATUS0,				\
> +	.group_num = group_index,							\
> +}
> +
> +static const struct mtk_spmi_pmic_irq_grp mt6363_irq_groups[] = {
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6363, BUCK,	0,  0,   9),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6363, LDO,	1, 16,  40),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6363, PSC,	2, 48,  57),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6363, MISC,	3, 64,  79),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6363, HK,	4, 80,  87),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6363, BM,	6, 88, 107)
> +};
> +
> +static const struct mtk_spmi_pmic_irq_grp mt6373_irq_groups[] = {
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6373, BUCK,	0,  0,  9),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6373, LDO,	1, 16, 39),
> +	MTK_SPMI_PMIC_IRQ_GROUP(MT6373, MISC,	3, 56, 71),
> +};
> +
> +static const struct mtk_spmi_pmic_variant mt6363_variant = {
> +	.pmic_irq = mt6363_irq_groups,
> +	.num_groups = ARRAY_SIZE(mt6363_irq_groups),
> +	.con_reg_len = 3,
> +	.irq_grp_reg = MT6363_REG_TOP_INT_STATUS1,
> +	.chip_id_reg = MTK_SPMI_PMIC_REG_CHIP_ID,
> +};
> +
> +static const struct mtk_spmi_pmic_variant mt6373_variant = {
> +	.pmic_irq = mt6373_irq_groups,
> +	.num_groups = ARRAY_SIZE(mt6373_irq_groups),
> +	.con_reg_len = 3,
> +	.irq_grp_reg = MT6373_REG_TOP_INT_STATUS1,
> +	.chip_id_reg = MTK_SPMI_PMIC_REG_CHIP_ID,
> +};
> +
> +static const struct regmap_config mtk_spmi_regmap_config = {
> +	.reg_bits	= 16,
> +	.val_bits	= MTK_SPMI_PMIC_VAL_BITS,
> +	.max_register	= 0xffff,
> +	.fast_io	= true,
> +};
> +
> +static int mtk_spmi_pmic_probe(struct spmi_device *sdev)
> +{
> +	const struct mtk_spmi_pmic_variant *variant;
> +	struct device *dev = &sdev->dev;
> +	struct regmap *regmap;
> +	int ret;
> +
> +	regmap = devm_regmap_init_spmi_ext(sdev, &mtk_spmi_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	variant = (const struct mtk_spmi_pmic_variant *)device_get_match_data(dev);

Again, no casting required.

> +	if (variant && variant->num_groups) {
> +		ret = mtk_spmi_pmic_irq_init(dev, regmap, variant);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return devm_of_platform_populate(dev);
> +}
> +
> +static const struct of_device_id mtk_pmic_spmi_id_table[] = {
> +	{ .compatible = "mediatek,mt6363", .data = &mt6363_variant },
> +	{ .compatible = "mediatek,mt6373", .data = &mt6373_variant },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mtk_pmic_spmi_id_table);
> +
> +static struct spmi_driver mtk_spmi_pmic_driver = {
> +	.probe = mtk_spmi_pmic_probe,
> +	.driver = {
> +		.name = "mtk-spmi-pmic",
> +		.of_match_table = mtk_pmic_spmi_id_table,
> +	},
> +};
> +module_spmi_driver(mtk_spmi_pmic_driver);
> +
> +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
> +MODULE_DESCRIPTION("MediaTek SPMI PMIC driver");
> +MODULE_LICENSE("GPL");
> diff --git "a/include/linux/mfd/mt63x3_spmi/registers.h" "b/include/linux/mfd/mt63x3_spmi/registers.h
> new file mode 100644
> index 000000000000..808927280b40
> --- /dev/null
> +++ "b/include/linux/mfd/mt63x3_spmi/registers.h"
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + * Copyright (c) 2025 Collabora Ltd
> + */
> +
> +#ifndef __MFD_MT63X3_SPMI_REGISTERS_H__
> +#define __MFD_MT63X3_SPMI_REGISTERS_H__
> +
> +/* MT6363 PMIC Registers */
> +#define MT6363_REG_MISC_TOP_INT_CON0		0x37
> +#define MT6363_REG_MISC_TOP_INT_STATUS0		0x43
> +#define MT6363_REG_TOP_INT_STATUS1		0x4e
> +#define MT6363_REG_PSC_TOP_INT_CON0		0x90f
> +#define MT6363_REG_PSC_TOP_INT_STATUS0		0x91b
> +#define MT6363_REG_BM_TOP_INT_CON0		0xc24
> +#define MT6363_REG_BM_TOP_INT_STATUS0		0xc36
> +#define MT6363_REG_HK_TOP_INT_CON0		0xf92
> +#define MT6363_REG_HK_TOP_INT_STATUS0		0xf9e
> +#define MT6363_REG_BUCK_TOP_INT_CON0		0x1411
> +#define MT6363_REG_BUCK_TOP_INT_STATUS0		0x141d
> +#define MT6363_REG_LDO_TOP_INT_CON0		0x1b11
> +#define MT6363_REG_LDO_TOP_INT_STATUS0		0x1b29
> +
> +/* MT6373 PMIC Registers */
> +#define MT6373_REG_MISC_TOP_INT_CON0		0x3c
> +#define MT6373_REG_MISC_TOP_INT_STATUS0		0x48
> +#define MT6373_REG_TOP_INT_STATUS1		0x53
> +#define MT6373_REG_BUCK_TOP_INT_CON0		0x1411
> +#define MT6373_REG_BUCK_TOP_INT_STATUS0		0x141d
> +#define MT6373_REG_LDO_TOP_INT_CON0		0x1b10
> +#define MT6373_REG_LDO_TOP_INT_STATUS0		0x1b22
> +
> +#endif /* __MFD_MT63X3_SPMI_REGISTERS_H__ */
> -- 
> 2.53.0
> 

-- 
Lee Jones


^ permalink raw reply

* Re: [PATCH 36/43] KVM: arm64: gic-v5: Implement save/restore mechanisms for ISTs
From: Sascha Bischoff @ 2026-05-21 14:12 UTC (permalink / raw)
  To: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	kvm@vger.kernel.org, Vladimir Murzin
  Cc: yuzenghui@huawei.com, Timothy Hayes, Suzuki Poulose,
	oliver.upton@linux.dev, peter.maydell@linaro.org, nd,
	maz@kernel.org, Joey Gouly, lpieralisi@kernel.org
In-Reply-To: <187900ec-4585-42d9-946c-e6de3111552d@arm.com>

On Fri, 2026-05-01 at 19:54 +0100, Vladimir Murzin wrote:
> Hi Sascha,

Hi Vladimir,

> 
> On 4/27/26 17:18, Sascha Bischoff wrote:
> > + tmp = le64_to_cpu(READ_ONCE(vmte->val[3]));
> > + host_id_bits = FIELD_GET(GICV5_VMTEL2E_IST_ID_BITS, tmp);
> > + host_istsz = FIELD_GET(GICV5_VMTEL2E_IST_ISTSZ, tmp);
> > + host_l2sz = FIELD_GET(GICV5_VMTEL2E_IST_L2SZ, tmp);
> > +
> > + /* We always use a Linear SPI IST on the host */
> > + for (int i = 0; i < BIT(host_id_bits); ++i) {
> 
> IIUC here we iterate over host's SPIs and save them into buffer
> provided
> by the user. One thing I noticed is that if user set number of SPIs
> via
> KVM_DEV_ARM_VGIC_GRP_NR_IRQS bellow 16, say 1 (btw, it seems there is
> no
> way to set SPIs to 0...), it would be rounded up to 16 in 
> kvm_vgic_v5_irs_init(). So from user perspective it needs to save
> just
> single SPI, thus it could provide buffer just for that single entry.
> With such
> setup we can end up corrupting user's memory. We can argue that it is
> user's
> problem... perhaps we need to spell explicitly requirements for the
> buffer or
> provide interface to the user to query required size or respect
> nr_spis set
> by user or some combinations of all :)

I've gone and have tightened the requirements for SPIs (and actually
match what GICv3 does here).

For GICv3, the default number of SPIs is 32, and the smallest number of
IRQs that can be specified via KVM_DEV_ARM_VGIC_GRP_NR_IRQS is 64 (16
SGIs, 16 PPIs, and 32 SPIs) - there's no way to specify 0 SPIs. The
number of SPIs must be a multiple of 32.

I've gone and mimicked this for GICv5. I've removed the ability to set
0 SPIs, making 32 the new minimum. The interface now also mandates that
we need a multiple of 32 SPIs.

Overall this means that the interface works just like the GICv3 one
(except that it doesn't also convey the private IRQs), and that we will
never be at risk of SPI IST alignment issues (which effectively mandate
at least 16 SPIs when no metadata is required). This does, of course,
mean that the SPI IST must ALWAYS be saved/restored, but if anything
that's clearer. Size-wise, it is always 32 if nothing was specified, or
it exactly matches what userspace (successfully) conveyed to KVM.

> 
> Cheers
> Vladimir

Thank you for pointing that out. It was definitely not sane before.

Sascha


^ permalink raw reply

* Re: [PATCH v14 03/28] drm: Add new general DRM property "color format"
From: Daniel Stone @ 2026-05-21 14:12 UTC (permalink / raw)
  To: Nicolas Frattaroli
  Cc: Harry Wentland, Leo Li, Rodrigo Siqueira, Alex Deucher,
	Christian König, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Sandy Huang, Heiko Stübner,
	Andy Yan, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
	Tvrtko Ursulin, Dmitry Baryshkov, Sascha Hauer, Rob Herring,
	Jonathan Corbet, Shuah Khan, kernel, amd-gfx, dri-devel,
	linux-kernel, linux-arm-kernel, linux-rockchip, intel-gfx,
	intel-xe, linux-doc, wayland-devel, Werner Sembach,
	Andri Yngvason, Marius Vlad
In-Reply-To: <20260423-color-format-v14-3-449a419ccbd4@collabora.com>

Hi,

On Thu, 23 Apr 2026 at 20:04, Nicolas Frattaroli
<nicolas.frattaroli@collabora.com> wrote:
> +       } else if (property == connector->color_format_property) {
> +               if (val > INT_MAX || !drm_connector_color_format_valid(val)) {
> +                       drm_dbg_atomic(connector->dev,
> +                                      "[CONNECTOR:%d:%s] unknown color format %llu\n",
> +                                      connector->base.id, connector->name, val);
> +                       return -EINVAL;
> +               }

Shouldn't this already be ensured by drm_property_change_valid_get()?

Cheers,
Daniel


^ permalink raw reply

* Re: [RFC PATCH 2/2] arm64: mm: add SMCCC-backed cache invalidate provider
From: Conor Dooley @ 2026-05-21 14:12 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Srirangan Madhavan, catalin.marinas, will, mark.rutland,
	lpieralisi, sudeep.holla, linux-arm-kernel, linux-kernel, vsethi,
	jevans, raghupathyk, srikars, nbenech, alwilliamson, Dan Williams
In-Reply-To: <20260521121812.2e4abd71@jic23-huawei>

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

On Thu, May 21, 2026 at 12:18:12PM +0100, Jonathan Cameron wrote:
> On Thu, 21 May 2026 07:30:47 +0000
> Srirangan Madhavan <smadhavan@nvidia.com> wrote:
> 
> > Add an arm64 cache maintenance backend that discovers SMCCC cache
> > clean+invalidate support, queries attributes, handles transient BUSY and
> > RATE_LIMITED responses with bounded retries, and registers with the generic
> > cache coherency framework.
> > 
> > Signed-off-by: Srirangan Madhavan <smadhavan@nvidia.com>
> Hi Srirangan,
> 
> Other than the file location and Kconfig option comments, everything else
> is really trivial. + some musings about maybe being worth more clever
> fusing of ops in the future if it turns out to be useful.
> 
> > ---
> >  MAINTAINERS                 |   1 +
> >  arch/arm64/mm/Makefile      |   1 +
> >  arch/arm64/mm/cache_maint.c | 180 ++++++++++++++++++++++++++++++++++++
> 
> File location wise, this is a driver for a subsystem, be it one closely
> coupled to arm.  Arm maintainers, do you want it in there or in drivers/cache ?
> My personal preference is always to keep drivers with subsystems but I don't
> care that much.

At the risk of stepping on Will's/Catalin's toes, I'll butt in here..

If this was on riscv, using an ecall into sbi firmware, I'd be asking
for it to be put in drivers/cache. The mechanism for requesting the
ops is arch-specific, but the actual execution of it is not, right?
The actual execution is going to depend on the device-specific
firmware that the smc is made to. That puts it in the same boat as
clk-scmi to me, but I'm not really familiar with how these things break
down on arm64 to be sure.

> >  3 files changed, 182 insertions(+)
> >  create mode 100644 arch/arm64/mm/cache_maint.c
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 2fb1c75afd16..33c35f8e6e40 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -25383,6 +25383,7 @@ M:	Jonathan Cameron <jic23@kernel.org>
> >  S:	Maintained
> >  T:	git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/
> >  F:	Documentation/devicetree/bindings/cache/
> > +F:	arch/arm64/mm/cache_maint.c
> 
> I wonder if this should just have a separate maintainers entry? 
> We did that for the hisi driver.
> 
> If not maybe add yourself as at least a Reviewer so that you get +CC'd
> on relevant changes.
> 
> Conor, what do you think makes sense here.

I think it is very weird to have an arch/arm64/mm file in this entry,
implying that I would be applying patches for it, which I do not
consider to be appropriate. If it gets moved to drivers/cache, then it
I think a standalone maintainers entry for the driver is a good idea.

Cheers,
Conor.

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

^ permalink raw reply

* Re: [PATCH net-next v5 00/13] net: lan966x: add support for PCIe FDMA
From: Daniel Machon @ 2026-05-21 14:12 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Horatiu Vultur, Steen Hegelund, UNGLinuxDriver,
	Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
	John Fastabend, Stanislav Fomichev, Herve Codina, Arnd Bergmann,
	Greg Kroah-Hartman, Mohsin Bashir
  Cc: netdev, linux-kernel, bpf, linux-arm-kernel
In-Reply-To: <20260520-lan966x-pci-fdma-v5-0-ca56197ae05b@microchip.com>

> This series adds FDMA (Frame DMA) support for the PCIe path. When
> operating as a PCIe endpoint, the internal FDMA engine on lan966x cannot
> directly access host memory, so DMA buffers are allocated as contiguous
> coherent memory and mapped through the PCIe Address Translation Unit
> (ATU). The ATU provides outbound windows that translate internal FDMA
> addresses to PCIe bus addresses, allowing the FDMA engine to read and
> write host memory. Because the ATU requires contiguous address regions,
> page_pool and normal per-page DMA mappings cannot be used. Instead,
> frames are transferred using memcpy between the ATU-mapped buffers and
> the network stack. With this, throughput increases from ~33 Mbps to
> ~620 Mbps for default MTU.

I went through the sashiko review for v5 (gemini and nipa). AFAICT no new
series-level issues were raised — the only Critical (XDP features exposed before
the xdp_setup() PCIe short-circuit lands) is a per-patch bisectability concern
that sashiko itself flags as "likely fixed by the subsequent patch", and indeed
patch [11/13] adds the guard. The NAPI bailout issue Paolo raised on v4 is
fixed in this version and not reflagged.


^ permalink raw reply

* Re: [PATCH v7 12/28] media: rockchip: rga: avoid odd frame sizes for YUV formats
From: Michael Tretter @ 2026-05-21 14:11 UTC (permalink / raw)
  To: Sven Püschel
  Cc: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
	Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Hans Verkuil, linux-media, linux-rockchip, linux-arm-kernel,
	linux-kernel, devicetree, kernel, nicolas, sebastian.reichel,
	p.zabel, Nicolas Dufresne
In-Reply-To: <20260521-spu-rga3-v7-12-3f33e8c7145f@pengutronix.de>

On Thu, 21 May 2026 00:44:17 +0200, Sven Püschel wrote:
> Avoid odd frame sizes for YUV formats, as they may cause undefined
> behavior. This is done in preparation for the RGA3, which hangs when the
> output format is set to 129x129 pixel YUV420 SP (NV12).
> 
> This requirement is documented explicitly for the RGA3 in  section 5.6.3
> of the RK3588 TRM Part 2. For the RGA2 the RK3588 TRM Part 2
> (section 6.1.2) and RK3568 TRM Part 2 (section 14.2) only mentions the
> x/y offsets and stride aligning requirements. But the vendor driver for
> the RGA2 also contains checks for the width and height to be aligned to
> 2 bytes.
> 
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
> ---
>  drivers/media/platform/rockchip/rga/rga.c | 19 ++++++++++++++-----
>  1 file changed, 14 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index f599c992829dd..77b8c7ab74274 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -337,6 +337,19 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
>  	struct rga_ctx *ctx = file_to_rga_ctx(file);
>  	const struct rga_hw *hw = ctx->rga->hw;
>  	struct rga_fmt *fmt;
> +	struct v4l2_frmsize_stepwise frmsize = {
> +		.min_width = hw->min_width,
> +		.max_width = hw->max_width,
> +		.min_height = hw->min_height,
> +		.max_height = hw->max_height,
> +		.step_width = 1,
> +		.step_height = 1,
> +	};
> +
> +	if (v4l2_is_format_yuv(v4l2_format_info(pix_fmt->pixelformat))) {
> +		frmsize.step_width = 2;
> +		frmsize.step_height = 2;

If I understand correctly, this limitation may be a result of 4:2:0
chroma subsampling. Thus, formats with 4:2:2 subsampling would also work
with step_height = 1.

As this may be some hardware limitation, a comment that points to the
TRM (in addition to the commit message) may be beneficial, too.

Michael

> +	}
>  
>  	if (V4L2_TYPE_IS_CAPTURE(f->type)) {
>  		const struct rga_frame *frm;
> @@ -358,11 +371,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
>  	if (!fmt)
>  		fmt = &hw->formats[0];
>  
> -	pix_fmt->width = clamp(pix_fmt->width,
> -			       hw->min_width, hw->max_width);
> -	pix_fmt->height = clamp(pix_fmt->height,
> -				hw->min_height, hw->max_height);
> -
> +	v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize);
>  	v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height);
>  	pix_fmt->field = V4L2_FIELD_NONE;
>  
> 
> -- 
> 2.54.0
> 
> 


^ permalink raw reply

* Re: [PATCH 0/4] firmware: arm_scmi: Fix protocol parsing and validation
From: Sudeep Holla @ 2026-05-21 14:09 UTC (permalink / raw)
  To: Cristian Marussi, arm-scmi, linux-arm-kernel, Sudeep Holla
In-Reply-To: <20260517-scmi_fixes-v1-0-d86daec4defd@kernel.org>

On Sun, 17 May 2026 20:02:39 +0100, Sudeep Holla wrote:
> This series fixes a few SCMI protocol parsing and validation issues found
> while checking the driver message layouts against the SCMI specification.
> 
> The first patch fixes a clear response width mismatch in SENSOR_CONFIG_GET,
> where the driver requested a 4-byte response but read it as a 64-bit value.
> 
> The next two patches harden notification parsing for variable-sized payloads.
> BASE_ERROR_EVENT and SENSOR_UPDATE both carry counted trailing arrays, so the
> received payload size must be validated before copying or parsing those
> entries.
> 
> [...]

Applied to sudeep.holla/linux (for-next/scmi/updates), thanks!


[1/4] firmware: arm_scmi: Read sensor config as 32-bit value
      https://git.kernel.org/sudeep.holla/c/f6fe7c3c007d
[2/4] firmware: arm_scmi: Validate BASE_ERROR_EVENT payload size
      https://git.kernel.org/sudeep.holla/c/56e7e64cdd0e
[3/4] firmware: arm_scmi: Validate SENSOR_UPDATE payload size
      https://git.kernel.org/sudeep.holla/c/32bc5496b481
[4/4] firmware: arm_scmi: Validate Powercap domains before state access
      https://git.kernel.org/sudeep.holla/c/fcca603c6a09

--
Regards,
Sudeep



^ 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