Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 2/2] KVM: arm64: Support BBM level 3
From: Mostafa Saleh @ 2026-07-17 13:09 UTC (permalink / raw)
  To: linux-kernel, kvmarm, linux-arm-kernel
  Cc: maz, oupton, seiden, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, vdonnefort, tabba, Mostafa Saleh
In-Reply-To: <20260717130901.2239134-1-smostafa@google.com>

If the system supports hardware Break-Before-Make (BBM) level 3, use it
to replace stage-2 PTEs directly instead of falling back to the software
break-before-make sequence.

1) Get a reference count on the containing table for the new PTE.
2) Atomically update the PTE with the new valid descriptor.
3) Invalidate the TLB for the old PTE.
4) Drop the reference count holding the old PTE.

One interesting case, as BBML3 will update the PTE atomically, it
can only know it raced with another core at the point of the cmpxchg
failing, unlike the SW implementation which locks the PTE first.
And as we must issue CMOs to the new mapped page before the update,
that means with BBML3 racing cores will issue redundant CMOs,
to improve this:
- We only use BBML3 if the old PTE was live.
- To reduce the window of the race, an early check is added before
  the CMO to exit early, but that does not eliminate the race.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 arch/arm64/kvm/hyp/pgtable.c | 53 +++++++++++++++++++++++++++++++-----
 1 file changed, 46 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index 127b7f9541b1..69d52308236f 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -838,7 +838,8 @@ static void stage2_clean_old_pte(const struct kvm_pgtable_visit_ctx *ctx,
 /**
  * stage2_try_break_pte() - Invalidates a pte according to the
  *			    'break-before-make' requirements of the
- *			    architecture.
+ *			    architecture, if BMML3 is supported it
+ *			    will be used, otherwise fallback to SW.
  *
  * @ctx: context of the visited pte.
  * @mmu: stage-2 mmu
@@ -854,6 +855,18 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
 {
 	kvm_pte_t locked_pte;
 
+	if (system_supports_bbml3() && kvm_pte_valid(ctx->old)) {
+		kvm_pte_t curr_pte = READ_ONCE(*ctx->ptep);
+
+		/*
+		 * All handled in stage2_make_pte(). However exit early if we already
+		 * lost the race to avoid extra CMOs.
+		 */
+		 if (curr_pte != ctx->old)
+			return false;
+		 return true;
+	}
+
 	if (stage2_pte_is_locked(ctx->old)) {
 		/*
 		 * Should never occur if this walker has exclusive access to the
@@ -873,16 +886,35 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
 	return true;
 }
 
-static void stage2_make_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_t new)
+/* Must be paired with stage2_try_break_pte() */
+static bool stage2_make_pte(const struct kvm_pgtable_visit_ctx *ctx, struct kvm_s2_mmu *mmu,
+			    kvm_pte_t new)
 {
 	struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops;
 
-	WARN_ON(!stage2_pte_is_locked(*ctx->ptep));
-
 	if (stage2_pte_is_counted(new))
 		mm_ops->get_page(ctx->ptep);
 
+	if (system_supports_bbml3() && kvm_pte_valid(ctx->old)) {
+		/*
+		 * Barrier is required because stage2_try_set_pte() uses
+		 * WRITE_ONCE for non-shared walks, lacking release semantics
+		 * used in the software BBM case.
+		 */
+		smp_wmb();
+		if (!stage2_try_set_pte(ctx, new)) {
+			if (stage2_pte_is_counted(new))
+				mm_ops->put_page(ctx->ptep);
+			return false;
+		}
+
+		stage2_clean_old_pte(ctx, mmu);
+		return true;
+	}
+
+	WARN_ON(!stage2_pte_is_locked(*ctx->ptep));
 	smp_store_release(ctx->ptep, new);
+	return true;
 }
 
 static bool stage2_unmap_defer_tlb_flush(struct kvm_pgtable *pgt)
@@ -1014,7 +1046,8 @@ static int stage2_map_walker_try_leaf(const struct kvm_pgtable_visit_ctx *ctx,
 	    stage2_pte_executable(new))
 		mm_ops->icache_inval_pou(kvm_pte_follow(new, mm_ops), granule);
 
-	stage2_make_pte(ctx, new);
+	if (!stage2_make_pte(ctx, data->mmu, new))
+		return -EAGAIN;
 
 	return 0;
 }
@@ -1069,7 +1102,10 @@ static int stage2_map_walk_leaf(const struct kvm_pgtable_visit_ctx *ctx,
 	 * will be mapped lazily.
 	 */
 	new = kvm_init_table_pte(childp, mm_ops);
-	stage2_make_pte(ctx, new);
+	if (!stage2_make_pte(ctx, data->mmu, new)) {
+		mm_ops->put_page(childp);
+		return -EAGAIN;
+	}
 
 	return 0;
 }
@@ -1557,7 +1593,10 @@ static int stage2_split_walker(const struct kvm_pgtable_visit_ctx *ctx,
 	 * writes the PTE using smp_store_release().
 	 */
 	new = kvm_init_table_pte(childp, mm_ops);
-	stage2_make_pte(ctx, new);
+	if (!stage2_make_pte(ctx, mmu, new)) {
+		kvm_pgtable_stage2_free_unlinked(mm_ops, childp, level);
+		return -EAGAIN;
+	}
 	return 0;
 }
 
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [RFC PATCH 1/2] KVM: arm64: Add stage2_clean_old_pte()
From: Mostafa Saleh @ 2026-07-17 13:08 UTC (permalink / raw)
  To: linux-kernel, kvmarm, linux-arm-kernel
  Cc: maz, oupton, seiden, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, vdonnefort, tabba, Mostafa Saleh
In-Reply-To: <20260717130901.2239134-1-smostafa@google.com>

At the moment, the pgtable code rely on BBM in SW which looks like:
Break: stage2_try_break_pte()
	1) Break PTE and lock it
	2) TLBI
	3) Put the ref on the old PTE

Make: stage2_make_pte()
	1) Get a ref on the new PTE
	2) Install the live PTE

With BBML3, the sequence will look as
	1) Get ref on the new PTE
	2) Install new PTE
	3) TLBI
	4) Put the ref on the old PTE

Which requires moving step #2 #3 from the break function to the make
function, although it is possible to do that for SW BBM also, that
means the stage2_try_break_pte() did not fully break the PTE as it
is referenced in TLBs, although that works it seems fragile.

Instead, move this logic to a new function stage2_clean_old_pte()
that can be called from BBML3.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 arch/arm64/kvm/hyp/pgtable.c | 67 ++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 30 deletions(-)

diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index 91a7dfad6686..127b7f9541b1 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -810,39 +810,10 @@ static bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_
 	return cmpxchg(ctx->ptep, ctx->old, new) == ctx->old;
 }
 
-/**
- * stage2_try_break_pte() - Invalidates a pte according to the
- *			    'break-before-make' requirements of the
- *			    architecture.
- *
- * @ctx: context of the visited pte.
- * @mmu: stage-2 mmu
- *
- * Returns: true if the pte was successfully broken.
- *
- * If the removed pte was valid, performs the necessary serialization and TLB
- * invalidation for the old value. For counted ptes, drops the reference count
- * on the containing table page.
- */
-static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
+static void stage2_clean_old_pte(const struct kvm_pgtable_visit_ctx *ctx,
 				 struct kvm_s2_mmu *mmu)
 {
 	struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops;
-	kvm_pte_t locked_pte;
-
-	if (stage2_pte_is_locked(ctx->old)) {
-		/*
-		 * Should never occur if this walker has exclusive access to the
-		 * page tables.
-		 */
-		WARN_ON(!kvm_pgtable_walk_shared(ctx));
-		return false;
-	}
-
-	locked_pte = FIELD_PREP(KVM_INVALID_PTE_TYPE_MASK,
-				KVM_INVALID_PTE_TYPE_LOCKED);
-	if (!stage2_try_set_pte(ctx, locked_pte))
-		return false;
 
 	if (!kvm_pgtable_walk_skip_bbm_tlbi(ctx)) {
 		/*
@@ -862,6 +833,42 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
 
 	if (stage2_pte_is_counted(ctx->old))
 		mm_ops->put_page(ctx->ptep);
+}
+
+/**
+ * stage2_try_break_pte() - Invalidates a pte according to the
+ *			    'break-before-make' requirements of the
+ *			    architecture.
+ *
+ * @ctx: context of the visited pte.
+ * @mmu: stage-2 mmu
+ *
+ * Returns: true if the pte was successfully broken.
+ *
+ * If the removed pte was valid, performs the necessary serialization and TLB
+ * invalidation for the old value. For counted ptes, drops the reference count
+ * on the containing table page.
+ */
+static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
+				 struct kvm_s2_mmu *mmu)
+{
+	kvm_pte_t locked_pte;
+
+	if (stage2_pte_is_locked(ctx->old)) {
+		/*
+		 * Should never occur if this walker has exclusive access to the
+		 * page tables.
+		 */
+		WARN_ON(!kvm_pgtable_walk_shared(ctx));
+		return false;
+	}
+
+	locked_pte = FIELD_PREP(KVM_INVALID_PTE_TYPE_MASK,
+				KVM_INVALID_PTE_TYPE_LOCKED);
+	if (!stage2_try_set_pte(ctx, locked_pte))
+		return false;
+
+	stage2_clean_old_pte(ctx, mmu);
 
 	return true;
 }
-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply related

* [RFC PATCH 0/2] KVM: arm64: Support BBM level 3
From: Mostafa Saleh @ 2026-07-17 13:08 UTC (permalink / raw)
  To: linux-kernel, kvmarm, linux-arm-kernel
  Cc: maz, oupton, seiden, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, vdonnefort, tabba, Mostafa Saleh

This patch series adds support for BBM level 3 to KVM pgtable, it
depends on [1]

Motivation
==========
I have been looking into this for the context of:
- Page table sharing between the host CPU stage-2 and the SMMUv3 for
  protected KVM.
- Use the pagtable code to populate SMMUv3 stage-2 shadowed
  page table [2]

However, BBM level 3 is still useful for CPU only operations as it
avoids intermediately breaking translation.

Design
======
Some of the conditions that BBM level 3 will be useful in (RWHZWS):
1) A change to a PTE memory type, shareability, cacheability or OA
2) Changing from block to table
3) Changing from table to block

At the moment in the hyp page table code:
- For #1) stage2_map_walker_try_leaf(): Replaces a leaf with another
  one which does not match the same OA/perms/attrs.

- For #2) Switch block to table is used from:
  - kvm_pgtable_stage2_split(): Explicitly splitting a block (used
    for dirty logging), where a block is replaced by a table with
    the same attributes.
  - stage2_map_walk_leaf(): Updating a mapping that is partially
    part of an existing block.

- #3) Does not exist in the code at the moment as coalescing is not
  supported.

The first patch is a preparation to be able to clean up the old pte
for BBML3, the second patch adds the main logic.

Initially, I encapsulated the full logic of BBM in one function,
which was not readable, due to different ordering and dealing with
CMO, TLBI.

Instead, I kept the logic into 2 functions, where BBML3 is added in
the make step.

One interesting case, as BBML3 will update the PTE atomically, it
can only know it raced with another core at the point of the cmpxchg
failing, unlike the SW implementation which locks the PTE first.
And as we must issue CMOs to the new mapped page before the update,
that means with BBML3 racing cores will issue redundant CMOs, to
improve this:
- We only use BBML3 if the old PTE was live
- To reduce the window of the race an early check is added before
  the CMO to exit early, but that does not eliminate the race.

Testing
=======
This was tested:
- C1-Pro cores, unfortunately the version I have does not run
  upstream, I backported the patches to Android kernel (6.18).

- mainline(7.2-rc3) kernel on a Qualcomm X1 with a hacked cpufeature
  as it does not support BBM, I did not see conflict aborts or TLB
  corruption.

I tested with VHE and protected (hvhe) modes, running VMs
(and protected), and running some selftests, that might exercise and
stress this path tools/testing/selftests/kvm:
- demand_paging_test
- memslot_perf_test
- memslot_modification_stress_test
- dirty_log_test

Future work
===========
Some other changes that would be useful for the SMMUv3:
- Eagerly install table on block split, we can now replace a block
  with a fully populated table atomically when we unmap a partial
  part of the block.

There is more to support page table sharing (such as dealing with
TLB invalidation, coherency…), I submitted a talk to LPC to discuss
this further.

[1] https://lore.kernel.org/linux-arm-kernel/20260715053408.1950475-1-linu.cherian@arm.com/
[2] https://lore.kernel.org/linux-iommu/20260715115906.2664882-1-smostafa@google.com/

Mostafa Saleh (2):
  KVM: arm64: Add stage2_clean_old_pte()
  KVM: arm64: Support BBM level 3

 arch/arm64/kvm/hyp/pgtable.c | 118 ++++++++++++++++++++++++-----------
 1 file changed, 82 insertions(+), 36 deletions(-)

-- 
2.55.0.229.g6434b31f56-goog



^ permalink raw reply

* Re: [PATCH v2 1/2] dt-bindings: iio: adc: Add Nuvoton MA35D1 EADC
From: Rob Herring @ 2026-07-17 13:06 UTC (permalink / raw)
  To: Chi-Wen Weng
  Cc: Jonathan Cameron, jic23, krzk+dt, conor+dt, dlechner, nuno.sa,
	andy, linux-arm-kernel, linux-iio, devicetree, linux-kernel,
	cwweng
In-Reply-To: <8d757505-ea93-4dd4-a374-79f143f6f949@gmail.com>

On Thu, Jul 16, 2026 at 7:59 PM Chi-Wen Weng <cwweng.linux@gmail.com> wrote:
>
> Hi Jonathan,
>
> Thank you for the review.
>
>  > Not sure this doc helps. What are these interrupts for?
>  > The driver only uses one of them so why are there 4?
>  >
>  > May well need interrupt-names to allow gaps in the list to
>  > work but hard to tell without more information.
>
> The hardware has four EADC interrupt outputs, ADINT0 to ADINT3. The
> conversion-complete source for each interrupt can be selected through
> the EADC interrupt source registers.
>
> However, this initial driver only uses sample module 0 and routes its
> end-of-conversion event to ADINT0. So the binding does not need to
> describe the unused interrupt outputs at this stage.
>
> I will simplify this in v3 and document only one interrupt entry for
> ADINT0. If support for the other ADINT lines is added later, the binding
> can be extended with interrupt-names at that point.

No, document what the h/w has, not what a driver currently uses. The
only exception I can think of here would be if only 1 interrupt pin is
usable on a given system. Then it would be just 1 interrupt with
interrupt-names to define which one is used.

Rob


^ permalink raw reply

* [PATCH v5 7/7] KVM: arm64: selftests: Add stage-2 block transition test
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

Add a test for the two stage-2 granularity changes dirty logging forces
at fault time, asserting the guest completes with no KVM_RUN error. The
first scenario collapses a page into a hugetlb-backed block: it writes
under logging, re-write-protects the page via GET_DIRTY_LOG, then writes
again with logging off. The second splits blocks: it faults in several
non-executable 2M blocks, enables logging, then executes in each block so
an execute permission fault splits it. It is skipped when CTR_EL0.DIC is
set, since mappings are then executable and no execute fault occurs.

Both paths make the fault handler allocate under mmu_lock, so a backend
that fails to stage that memory returns a KVM_RUN error or crashes the
host. The property holds on any host. On a pKVM host, where a
non-protected guest uses the pkvm_pgtable_*() backend, it also guards
that backend's fault-time staging.

Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../kvm/arm64/stage2_block_transitions.c      | 226 ++++++++++++++++++
 2 files changed, 227 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/arm64/stage2_block_transitions.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d28a057fa6c2..ed2877825eee 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -179,6 +179,7 @@ TEST_GEN_PROGS_arm64 += arm64/psci_test
 TEST_GEN_PROGS_arm64 += arm64/sea_to_user
 TEST_GEN_PROGS_arm64 += arm64/set_id_regs
 TEST_GEN_PROGS_arm64 += arm64/smccc_filter
+TEST_GEN_PROGS_arm64 += arm64/stage2_block_transitions
 TEST_GEN_PROGS_arm64 += arm64/vcpu_width_config
 TEST_GEN_PROGS_arm64 += arm64/vgic_init
 TEST_GEN_PROGS_arm64 += arm64/vgic_irq
diff --git a/tools/testing/selftests/kvm/arm64/stage2_block_transitions.c b/tools/testing/selftests/kvm/arm64/stage2_block_transitions.c
new file mode 100644
index 000000000000..5fd47f4ada1f
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/stage2_block_transitions.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ *
+ * stage2_block_transitions - Exercise stage-2 block/page granularity changes
+ * that dirty logging forces at fault time, and assert the guest completes.
+ *
+ * Both scenarios need the fault handler to allocate at fault time (a fresh
+ * mapping and/or page-table pages while holding mmu_lock), so a fault path
+ * that fails to stage that memory manifests as a KVM_RUN error or, worse, a
+ * host crash. The asserted property is host-agnostic: the guest runs the
+ * sequence to completion and every KVM_RUN succeeds. On a pKVM host, where a
+ * non-protected guest's stage-2 faults are serviced by the pkvm_pgtable_*()
+ * backend, the same sequences also guard that backend's fault-time staging.
+ *
+ * Scenario 1 - block collapse on dirty-logging disable:
+ *   A write under dirty logging installs a 4K page; GET_DIRTY_LOG
+ *   re-write-protects it; logging is disabled; a second write takes a
+ *   permission fault that collapses the page into a hugetlb-backed block,
+ *   which requires a fresh mapping object under mmu_lock.
+ *
+ * Scenario 2 - block split under dirty logging:
+ *   Several hugetlb-backed blocks are faulted in as non-executable blocks,
+ *   dirty logging is enabled (write-protect only), then the guest executes
+ *   into each block. Each instruction fetch takes an execute permission
+ *   fault that must split the block into pages during logging, draining
+ *   page-table pages. Skipped on CTR_EL0.DIC hardware, where mappings are
+ *   made executable eagerly and the execute fault never occurs.
+ */
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/mman.h>
+#include <linux/sizes.h>
+#include <sys/mman.h>
+
+#include <asm/sysreg.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "test_util.h"
+#include "ucall.h"
+
+#define DATA_SLOT		1
+#define TEST_GVA		0xc0000000UL
+#define BLOCK_SIZE		SZ_2M
+
+/* AArch64 "ret" (ret x30): a self-contained, returnable executable payload. */
+#define RET_INSN		0xd65f03c0U
+
+/*
+ * A non-protected guest's per-VM stage-2 pool is seeded only with the PGD
+ * donation, which stage-2 init immediately consumes, so the page-table budget
+ * for a fault that does not top up is just the handful (~2x the stage-2 min
+ * pages) of memcache leftovers. Executing into this many distinct blocks
+ * demands far more than that budget: a fault path that tops up on every fault
+ * completes all of them, one that skips non-write faults runs out mid-sequence.
+ */
+#define NR_BLOCKS		16
+
+/* Scenario 2 guest -> host sync stages. */
+#define STAGE_SKIP_DIC		1
+#define STAGE_BLOCKS_READY	2
+
+static void collapse_guest_code(u64 gva)
+{
+	u64 *data = (u64 *)gva;
+
+	/* Under dirty logging: install a 4K writable page. */
+	WRITE_ONCE(*data, 0x1);
+	GUEST_SYNC(1);
+
+	/* Logging disabled: a permission fault collapses the page into a block. */
+	WRITE_ONCE(*data, 0x2);
+	GUEST_SYNC(2);
+
+	GUEST_DONE();
+}
+
+static void test_block_collapse(void)
+{
+	struct kvm_vcpu *vcpu;
+	unsigned long *bmap;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	size_t npages;
+	u64 gpa;
+
+	vm = vm_create_with_one_vcpu(&vcpu, collapse_guest_code);
+	npages = BLOCK_SIZE / vm->page_size;
+
+	gpa = (vm_compute_max_gfn(vm) * vm->page_size) - BLOCK_SIZE;
+	gpa = align_down(gpa, BLOCK_SIZE);
+
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_HUGETLB_2MB, gpa,
+				    DATA_SLOT, npages, KVM_MEM_LOG_DIRTY_PAGES);
+	virt_map(vm, TEST_GVA, gpa, npages);
+	vcpu_args_set(vcpu, 1, TEST_GVA);
+
+	bmap = bitmap_zalloc(BLOCK_SIZE / getpagesize());
+
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC && uc.args[1] == 1,
+		    "Expected first sync, got cmd %lu arg %lu", uc.cmd, uc.args[1]);
+
+	/* GET_DIRTY_LOG re-write-protects the dirtied page; then stop logging. */
+	kvm_vm_get_dirty_log(vm, DATA_SLOT, bmap);
+	vm_mem_region_set_flags(vm, DATA_SLOT, 0);
+
+	/* The collapsing permission fault: a broken fault path faults here. */
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC && uc.args[1] == 2,
+		    "Expected second sync, got cmd %lu arg %lu", uc.cmd, uc.args[1]);
+
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE,
+		    "Expected done, got cmd %lu", uc.cmd);
+
+	free(bmap);
+	kvm_vm_free(vm);
+}
+
+static void guest_sync_insn(u64 va)
+{
+	/* Make the just-written instruction coherent for execution (!DIC). */
+	asm volatile("dc cvau, %0\n"
+		     "dsb ish\n"
+		     "ic ivau, %0\n"
+		     "dsb ish\n"
+		     "isb\n"
+		     :: "r" (va) : "memory");
+}
+
+static void split_guest_code(u64 base_gva, u64 nblocks)
+{
+	u64 i, va;
+
+	if (FIELD_GET(CTR_EL0_DIC_MASK, read_sysreg(ctr_el0))) {
+		GUEST_SYNC(STAGE_SKIP_DIC);
+		GUEST_DONE();
+		return;
+	}
+
+	/* Fault in each block (non-executable) and stage an executable payload. */
+	for (i = 0; i < nblocks; i++) {
+		va = base_gva + i * BLOCK_SIZE;
+		WRITE_ONCE(*(u32 *)va, RET_INSN);
+		guest_sync_insn(va);
+	}
+	GUEST_SYNC(STAGE_BLOCKS_READY);
+
+	/* Logging is now on: executing into each block splits it into pages. */
+	for (i = 0; i < nblocks; i++) {
+		va = base_gva + i * BLOCK_SIZE;
+		((void (*)(void))va)();
+	}
+
+	GUEST_DONE();
+}
+
+static void test_exec_split_drain(void)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	size_t npages;
+	u64 gpa;
+
+	vm = vm_create_with_one_vcpu(&vcpu, split_guest_code);
+	npages = NR_BLOCKS * (BLOCK_SIZE / vm->page_size);
+
+	gpa = (vm_compute_max_gfn(vm) * vm->page_size) - NR_BLOCKS * BLOCK_SIZE;
+	gpa = align_down(gpa, BLOCK_SIZE);
+
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_HUGETLB_2MB, gpa,
+				    DATA_SLOT, npages, 0);
+	virt_map(vm, TEST_GVA, gpa, npages);
+	vcpu_args_set(vcpu, 2, TEST_GVA, (u64)NR_BLOCKS);
+
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC,
+		    "Expected sync, got cmd %lu", uc.cmd);
+	if (uc.args[1] == STAGE_SKIP_DIC) {
+		ksft_print_msg("SKIP block split: CTR_EL0.DIC == 1\n");
+		kvm_vm_free(vm);
+		return;
+	}
+	TEST_ASSERT(uc.args[1] == STAGE_BLOCKS_READY,
+		    "Expected blocks-ready sync, got arg %lu", uc.args[1]);
+
+	/* Write-protect the blocks; the guest then splits them by executing. */
+	vm_mem_region_set_flags(vm, DATA_SLOT, KVM_MEM_LOG_DIRTY_PAGES);
+
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE,
+		    "Expected done, got cmd %lu", uc.cmd);
+
+	kvm_vm_free(vm);
+}
+
+/*
+ * The explicit-size hugetlb backing hard-fails region creation if the pages
+ * are not already reserved, so probe here and skip rather than abort. The
+ * peak reservation is scenario 2's; the two scenarios run and free in turn.
+ */
+static void require_hugepages(size_t bytes)
+{
+	void *mem = mmap(NULL, bytes, PROT_READ | PROT_WRITE,
+			 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_HUGE_2MB,
+			 -1, 0);
+
+	if (mem == MAP_FAILED)
+		ksft_exit_skip("Need %zu bytes of reserved 2M hugepages\n", bytes);
+	munmap(mem, bytes);
+}
+
+int main(void)
+{
+	require_hugepages(NR_BLOCKS * BLOCK_SIZE);
+
+	test_block_collapse();
+	test_exec_split_drain();
+
+	ksft_print_msg("All ok!\n");
+	return 0;
+}
-- 
2.39.5



^ permalink raw reply related

* [PATCH v5 6/7] KVM: arm64: Don't advertise eager page splitting under pKVM
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

Under pKVM the stage-2 walker resolves to pkvm_pgtable_stage2_split(), a
WARN_ON_ONCE(1) stub, yet KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE is still
enabled and reported for non-protected guests: the capability check keys
on the per-VM protected state while the walker dispatch keys on the
host-global mode. Enabling the cap and then dirty-logging the guest
reaches the stub, splatting a userspace-reachable WARN.

Reject the capability, and stop reporting a chunk size and the
supported block sizes, for every VM once pKVM is enabled, keyed on the
host-global mode like the split dispatch. Gating only protected VMs
would leave the non-protected guests that reach the stub still able to
enable it. Userspace decides whether eager splitting is available from
the block-size bitmap (QEMU falls back to no eager splitting when it
reads 0), so leaving it advertised steers an explicit request into the
enable failure instead of the fallback.

Fixes: e912efed485a ("KVM: arm64: Introduce the EL1 pKVM MMU")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/include/asm/kvm_pkvm.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 57afb07d6b13..beea00e693a0 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -45,6 +45,9 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
 		return true;
 	case KVM_CAP_ARM_MTE:
 		return false;
+	case KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE:
+	case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
+		return false;
 	default:
 		return !kvm || !kvm_vm_is_protected(kvm);
 	}
-- 
2.39.5



^ permalink raw reply related

* [PATCH v5 4/7] KVM: arm64: Skip pKVM stage-2 flush when FWB is enabled
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

pkvm_pgtable_stage2_flush() cleans the D-cache for every mapping in the
range even on hardware with stage-2 Force Write-Back, where FWB keeps
guest memory coherent to the PoC and the maintenance is unnecessary. The
generic kvm_pgtable_stage2_flush() returns early in that case, but the
pKVM MMU does not, so it needlessly cleans the whole range on, e.g.,
every set/way trap.

Return early when FWB is enabled, matching the generic walker.

Fixes: e912efed485a ("KVM: arm64: Introduce the EL1 pKVM MMU")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/kvm/pkvm.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index f70c601dcf4c..ba6570e37545 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -518,6 +518,10 @@ int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
 	struct pkvm_mapping *mapping;
 
 	lockdep_assert_held(&kvm->mmu_lock);
+
+	if (cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
+		return 0;
+
 	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
 		if (!mapping->nc)
 			__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
-- 
2.39.5



^ permalink raw reply related

* [PATCH v5 2/7] KVM: arm64: Top up the memcache for pKVM permission faults
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

A permission fault normally only relaxes a leaf, so user_mem_abort()
skips the memcache top-up. Under pKVM such a fault can instead collapse
pages into a block. That needs a fresh pkvm_mapping object, and without
it cache->mapping is NULL, so pkvm_pgtable_stage2_map() dereferences NULL
and faults the host under mmu_lock. Staging only the object is not
enough: the hypervisor requires kvm_mmu_cache_min_pages in the memcache
even for the allocation-free install, so under memcache pressure the
collapse returns -ENOMEM and trips the WARN_ON(ret) in
pkvm_pgtable_stage2_map() where a non-pKVM guest succeeds.

Top up the full memcache for pKVM permission faults so both the mapping
object and the min-pages are staged before mmu_lock.

Fixes: db14091d8f75 ("KVM: arm64: Stage-2 huge mappings for np-guests")
Reported-by: Bradley Morgan <include@grrlz.net>
Link: https://lore.kernel.org/all/20260623161545.EA08E1F000E9@smtp.kernel.org/
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/kvm/mmu.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 6c941aaa10c6..4d7c9bdcef00 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2114,10 +2114,14 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	 * and so normally don't require allocations from the memcache. The
 	 * only exception to this is when dirty logging is enabled at runtime
 	 * and a write fault needs to collapse a block entry into a table.
+	 * Under pKVM a permission fault can also collapse pages into a block,
+	 * which needs a fresh mapping object, and the hypervisor requires the
+	 * min-pages memcache even when the install allocates nothing.
 	 */
 	memcache = get_mmu_memcache(s2fd->vcpu);
 	if (!perm_fault || (memslot_is_logging(s2fd->memslot) &&
-			    kvm_is_write_fault(s2fd->vcpu))) {
+			    kvm_is_write_fault(s2fd->vcpu)) ||
+	    is_protected_kvm_enabled()) {
 		ret = topup_mmu_memcache(s2fd->vcpu, memcache);
 		if (ret)
 			return ret;
-- 
2.39.5



^ permalink raw reply related

* [PATCH v5 5/7] KVM: arm64: Don't WARN on pKVM stage-2 map failures
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

pkvm_pgtable_stage2_map() wraps the __pkvm_host_share_guest() and
__pkvm_host_donate_guest() return in WARN_ON(), but those hypercalls
fail for reasons that are not EL1 invariant violations: -EINVAL for a
pfn that is not memblock RAM (check_range_allowed_memory() rejects a
device page mapped into a non-protected guest) and -ENOMEM under
memcache pressure. Both are reachable from a guest fault, so the WARN
splats on host input.

Return the error without warning. The unshare and write-protect WARNs
stay, since a failure there does signal a broken EL1 invariant.

Fixes: 3669ddd8fa8b5 ("KVM: arm64: Add a range to pkvm_mappings")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/kvm/pkvm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index ba6570e37545..1212a2c5613d 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -466,7 +466,7 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
 					size / PAGE_SIZE, prot);
 	}
 
-	if (WARN_ON(ret))
+	if (ret)
 		return ret;
 
 	swap(mapping, cache->mapping);
-- 
2.39.5



^ permalink raw reply related

* [PATCH v5 3/7] KVM: arm64: Top up stage-2 memcache for dirty logging faults
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

From: Bradley Morgan <include@grrlz.net>

Dirty logging forces new stage-2 mappings to page size but does not
always split an existing block first (eager splitting is best effort
and off by default). A non-write permission fault on such a block, an
instruction fetch, still needs a page-table page to split it, but the
top-up is gated on write faults.

With the cache empty, kvm_mmu_memory_cache_alloc() hits its
guest-triggerable WARN_ON(!nobjs) and falls back to a GFP_ATOMIC
allocation under mmu_lock, with a BUG_ON() if that fails.

Top up the memcache for any permission fault while dirty logging is
active.

Fixes: 6f745f1bb5bf ("KVM: arm64: Convert user_mem_abort() to generic page-table API")
Link: https://lore.kernel.org/all/20260623165634.699011F000E9@smtp.kernel.org/
Signed-off-by: Bradley Morgan <include@grrlz.net>
[tabba: reword the commit message for the generic, non-pKVM failure mode]
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/kvm/mmu.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 4d7c9bdcef00..74e7e7f7564c 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2113,14 +2113,13 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	 * Permission faults just need to update the existing leaf entry,
 	 * and so normally don't require allocations from the memcache. The
 	 * only exception to this is when dirty logging is enabled at runtime
-	 * and a write fault needs to collapse a block entry into a table.
+	 * and a fault needs to collapse a block entry into a table.
 	 * Under pKVM a permission fault can also collapse pages into a block,
 	 * which needs a fresh mapping object, and the hypervisor requires the
 	 * min-pages memcache even when the install allocates nothing.
 	 */
 	memcache = get_mmu_memcache(s2fd->vcpu);
-	if (!perm_fault || (memslot_is_logging(s2fd->memslot) &&
-			    kvm_is_write_fault(s2fd->vcpu)) ||
+	if (!perm_fault || memslot_is_logging(s2fd->memslot) ||
 	    is_protected_kvm_enabled()) {
 		ret = topup_mmu_memcache(s2fd->vcpu, memcache);
 		if (ret)
-- 
2.39.5



^ permalink raw reply related

* [PATCH v5 0/7] KVM: arm64: pKVM stage-2 mapping and memcache fixes
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel

Hi folks,

Changes since v4 [1]:
  - Restored the permission-fault and dirty-logging memcache top-ups
    that v4 dropped. Both fix real bugs.
  - Patch 1 uses the anonymous bitfield encoding rather than the
    open-coded mask and helpers. (Marc)
  - Reshaped the permission-fault top-up to stage the full memcache
    under pKVM, not just the mapping object. The object-only form still
    returned -ENOMEM and tripped a WARN under the hypervisor's
    unconditional min-pages check.
  - Re-scoped the dirty-logging top-up to its generic, non-pKVM failure
    mode, now that the permission-fault patch covers pKVM.
  - Added three adjacent fixes found while going through the series, and
    a selftest for the block transitions.
  - Patches 1 and 3 keep Bradley's Signed-off-by from v3; my changes
    to each are noted in a [tabba: ...] line.

I picked this up while reviewing Bradley's "mapping cache" series [2]:
v4 dropped two fixes from v3 that address real bugs, so I've collected
the three fixes back together, reshaped patch 1 per Marc's review [3],
added a few adjacent fixes I found along the way and a selftest, and am
reposting as v5.

Most of the fixes are in the pKVM stage-2 walker. On a pKVM host a
non-protected guest's stage-2 faults go through pkvm_pgtable_*(), which
diverges from the generic walker in several ways that are bugs: cache
maintenance on non-cacheable mappings (patch 1), a missing memcache
top-up on permission faults that under pKVM still allocate (patch 2), a
full flush walk on FWB hardware (patch 4), a WARN on a guest-reachable
map failure (patch 5), and an eager-split capability whose pKVM backend
is only a stub (patch 6). These affect non-protected guests only, since
dispatch and memcache selection key on the host-global pKVM mode, and
protected guests take no permission faults.

Patch 3 is not pKVM-specific. During dirty logging a non-write
permission fault, an instruction fetch, still needs a page-table page
to split a block, but the memcache top-up is gated on write faults.
That fault path is generic, so the fix is too.

The two memcache top-ups came from sashiko review-bot findings [4][5],
and both check out against the code.

The series is structured as follows:

  1:    Skip cache maintenance for non-cacheable mappings.
  2-3:  Top up the memcache for the permission and dirty-logging faults
        that force stage-2 block transitions.
  4-6:  Adjacent pkvm_pgtable_*() fixes: FWB flush early-out, drop a
        spurious map WARN, and gate the eager-split capability.
  7:    Selftest for the block-collapse and block-split transitions.

Testing: the selftest in patch 7 covers the two dirty-logging block
transitions (page->block collapse and block->page split) that patch 2
stages; on the base kernel the collapse oopses the host in
pkvm_pgtable_stage2_map() with a NULL dereference under mmu_lock, and
with the series applied it passes. It is a standalone test rather than
an extension of kvm_page_table_test, whose default anonymous-4K backing
forms no huge-page blocks, so an automated run never exercises these
transitions, and whose worker/stage harness does not fit the multi-stage
logging sequence. Run it with 2M hugepages
reserved and, for the split half, on a CPU with CTR_EL0.DIC == 0 (e.g.
-cpu cortex-a710 under QEMU); it self-skips those otherwise. It also
passes on a non-pKVM host (VHE and nVHE), where patch 3's generic change
applies. The other fixes are not exercised by the selftest and rest on
the analysis in their commit messages: patch 3's fault path is generic
and non-pKVM (under pKVM patch 2 already tops it up, and its failure is
a WARN_ON(!nobjs) in kvm_mmu_memory_cache_alloc(), not a KVM_RUN error),
and patches 1 and 4-6 each need a specific pKVM configuration to hit.

Based on Linux v7.2-rc3 (a13c140cc289).

Cheers,
/fuad

[1] https://lore.kernel.org/r/20260701192428.17430-1-include@grrlz.net
[2] https://lore.kernel.org/r/20260624160028.15591-1-include@grrlz.net
[3] https://lore.kernel.org/r/86qzllpy1g.wl-maz@kernel.org
[4] https://lore.kernel.org/all/20260623161545.EA08E1F000E9@smtp.kernel.org/
[5] https://lore.kernel.org/all/20260623165634.699011F000E9@smtp.kernel.org/

Bradley Morgan (2):
  KVM: arm64: Skip cache maintenance for non-cacheable pKVM mappings
  KVM: arm64: Top up stage-2 memcache for dirty logging faults

Fuad Tabba (5):
  KVM: arm64: Top up the memcache for pKVM permission faults
  KVM: arm64: Skip pKVM stage-2 flush when FWB is enabled
  KVM: arm64: Don't WARN on pKVM stage-2 map failures
  KVM: arm64: Don't advertise eager page splitting under pKVM
  KVM: arm64: selftests: Add stage-2 block transition test

 arch/arm64/include/asm/kvm_pkvm.h             |   8 +-
 arch/arm64/kvm/mmu.c                          |   9 +-
 arch/arm64/kvm/pkvm.c                         |  21 +-
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../kvm/arm64/stage2_block_transitions.c      | 226 ++++++++++++++++++
 5 files changed, 254 insertions(+), 11 deletions(-)
 create mode 100644 tools/testing/selftests/kvm/arm64/stage2_block_transitions.c


base-commit: a13c140cc289c0b7b3770bce5b3ad42ab35074aa
-- 
2.39.5



^ permalink raw reply

* [PATCH v5 1/7] KVM: arm64: Skip cache maintenance for non-cacheable pKVM mappings
From: Fuad Tabba @ 2026-07-17 13:03 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Shuah Khan, Quentin Perret,
	Vincent Donnefort, Alexandru Elisei, Gavin Shan, Dev Jain,
	Bradley Morgan, Fuad Tabba, linux-arm-kernel, kvmarm,
	linux-kernel
In-Reply-To: <20260717130317.1953574-1-fuad.tabba@linux.dev>

From: Bradley Morgan <include@grrlz.net>

The pKVM flush path walks its own pkvm_mappings list and cleans the
data cache for every mapping, unlike the generic stage-2 walker it
shadows, which skips non-cacheable leaves. Cleaning the cacheable
alias of a non-cacheable mapping is pointless and can corrupt a
device endpoint. Record whether a mapping is non-cacheable in spare
bits of nr_pages and skip cache maintenance for it.

Fixes: e912efed485a ("KVM: arm64: Introduce the EL1 pKVM MMU")
Suggested-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Bradley Morgan <include@grrlz.net>
[tabba: use Marc's anonymous bitfield in place of the open-coded mask and helpers]
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/include/asm/kvm_pkvm.h |  5 ++++-
 arch/arm64/kvm/pkvm.c             | 15 +++++++++------
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 74fedd9c5ff0..57afb07d6b13 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -195,7 +195,10 @@ struct pkvm_mapping {
 	struct rb_node node;
 	u64 gfn;
 	u64 pfn;
-	u64 nr_pages;
+	struct {
+		u64 nr_pages:48;
+		u64 nc:1;
+	};
 	u64 __subtree_last;	/* Internal member for interval tree */
 };
 
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 053e4f733e4b..f70c601dcf4c 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -369,7 +369,7 @@ static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64
 
 	for_each_mapping_in_range_safe(pgt, start, end, mapping) {
 		ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn,
-					mapping->nr_pages);
+					(u64)mapping->nr_pages);
 		if (WARN_ON(ret))
 			return ret;
 		pkvm_mapping_remove(mapping, &pgt->pkvm_mappings);
@@ -473,6 +473,7 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
 	mapping->gfn = gfn;
 	mapping->pfn = pfn;
 	mapping->nr_pages = size / PAGE_SIZE;
+	mapping->nc = !!(prot & (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC));
 	pkvm_mapping_insert(mapping, &pgt->pkvm_mappings);
 
 	return ret;
@@ -503,7 +504,7 @@ int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
 	lockdep_assert_held(&kvm->mmu_lock);
 	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
 		ret = kvm_call_hyp_nvhe(__pkvm_host_wrprotect_guest, handle, mapping->gfn,
-					mapping->nr_pages);
+					(u64)mapping->nr_pages);
 		if (WARN_ON(ret))
 			break;
 	}
@@ -517,9 +518,11 @@ int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
 	struct pkvm_mapping *mapping;
 
 	lockdep_assert_held(&kvm->mmu_lock);
-	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
-		__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
-					  PAGE_SIZE * mapping->nr_pages);
+	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
+		if (!mapping->nc)
+			__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
+						  PAGE_SIZE * mapping->nr_pages);
+	}
 
 	return 0;
 }
@@ -537,7 +540,7 @@ bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64
 	lockdep_assert_held(&kvm->mmu_lock);
 	for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
 		young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest, handle, mapping->gfn,
-					   mapping->nr_pages, mkold);
+					   (u64)mapping->nr_pages, mkold);
 
 	return young;
 }
-- 
2.39.5



^ permalink raw reply related

* Re: [PATCH v5 4/4] dmaengine: xilinx_dma: Extend metadata handling for AXI DMA and MCDMA
From: Pandey, Radhey Shyam @ 2026-07-17 11:19 UTC (permalink / raw)
  To: Srinivas Neeli, Vinod Koul, Radhey Shyam Pandey
  Cc: Frank Li, Michal Simek, Andrew Lunn, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Suraj Gupta,
	Marek Vasut, Tomi Valkeinen, Alex Bereza, Folker Schwesinger,
	dmaengine, netdev, linux-arm-kernel, linux-kernel, git
In-Reply-To: <20260717090824.2364230-5-srinivas.neeli@amd.com>

On 7/17/2026 2:38 PM, Srinivas Neeli wrote:
> From: Suraj Gupta <suraj.gupta2@amd.com>
> 
> xilinx_dma_get_metadata_ptr() returns the AXI DMA APP words from the SOP
> descriptor in both directions. This is wrong for RX, where the hardware
> writes the APP words into the EOF descriptor. It also leaves AXI MCDMA
> without metadata support.
> 
> Return the metadata from the SOP descriptor for TX and from the EOF
> descriptor for RX, matching where the hardware reads and writes the
> fields. For AXI DMA, expose the APP words (20 bytes). For AXI MCDMA,
> expose the control sideband, status, and APP fields (28 bytes). On TX
> the control sideband holds TID and TUSER configuration for the outgoing
> stream. On RX the sideband status holds the received TID, TDEST and TUSER
> from the incoming stream. The field layout differs between MM2S and S2MM,
> and the wider payload lets a consumer distinguish the two controllers.
> No in-tree consumer is affected.
> 
> Read xlnx,axistream-connected for AXI MCDMA. Attach metadata_ops in
> xilinx_mcdma_prep_slave_sg() when an AXI4-Stream interface is present,
> so MCDMA clients use the metadata API the same way as AXI DMA clients.
> 
> Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
> Co-developed-by: Srinivas Neeli <srinivas.neeli@amd.com>
> Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
> ---

Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Thanks!

> Changes in V5:
>   - Take the metadata pointer from the SOP descriptor for TX and the EOF
>     descriptor for RX, matching where the hardware reads and writes the
>     fields (TX previously used the EOF descriptor).
>   - AXI DMA now exposes only the APP words (20 bytes) in both directions,
>     instead of the status word followed by APP (24 bytes).
>   - AXI MCDMA exposes the control sideband, status and APP fields
>     (28 bytes), with the sideband position differing between MM2S and S2MM.
>   - Reworked the kernel-doc index table and commit message accordingly.
> 
> Changes in V4:
>   - Restructured xilinx_dma_get_metadata_ptr(): AXIDMA is now the
>     fall-through path instead of a separate branch guarded by
>     WARN_ON_ONCE()/ERR_PTR().
>   - Rewrote the kernel-doc as an index table covering AXI DMA, MCDMA S2MM
>     and MCDMA MM2S, and documented that the pointer and payload length are
>     the same for both MCDMA directions.
>   - Added an inline comment explaining the union aliasing.
>   - Condensed the commit message.
> 
> Changes in V3:
>   - Renamed subject to include "AXI DMA and MCDMA" (was "AXI MCDMA" only).
>   - Complete rewrite of commit message and implementation.
>   - Metadata pointer now returns status field at index 0 instead of APP
>     fields, exposing status and sideband information to clients.
>   - Changed from list_first_entry to list_last_entry to return the EOF
>     descriptor where hardware writes status and APP fields.
>   - Added explicit handling for both AXIDMA and MCDMA types with proper
>     payload length calculation.
>   - Added WARN_ON_ONCE for unsupported DMA types.
>   - Removed the 'chan' field from struct xilinx_dma_tx_descriptor (was
>     added in V2) as it's no longer needed; channel is obtained from
>     tx->chan instead.
>   - Dropped V2 patches 4/5 (dt-bindings xlnx,include-stscntrl-strm) and
>     5/5 (xferred_bytes support) as the approach changed to use residue.
> 
> Changes in V2:
>   - Added support for MCDMA metadata handling alongside AXIDMA.
>   - Added 'chan' field to struct xilinx_dma_tx_descriptor.
> ---
>   drivers/dma/xilinx/xilinx_dma.c | 48 +++++++++++++++++++++++++++++----
>   1 file changed, 43 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
> index 1b5b00f08c5f..6bf509d33e7c 100644
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -651,17 +651,51 @@ static inline void xilinx_aximcdma_buf(struct xilinx_dma_chan *chan,
>    * @tx: async transaction descriptor
>    * @payload_len: metadata payload length
>    * @max_len: metadata max length
> - * Return: The app field pointer.
> + *
> + * The metadata lives in the SOP descriptor for TX and the EOF descriptor for RX.
> + * Field order depends on dmatype and direction:
> + *
> + *   AXI DMA:         [0..] app
> + *   AXI MCDMA (TX):  [0] ctrl_sideband, [1] status, [2..] app
> + *   AXI MCDMA (RX):  [0] status, [1] sideband, [2..] app
> + *
> + * Return: Pointer to the first metadata word.
>    */
>   static void *xilinx_dma_get_metadata_ptr(struct dma_async_tx_descriptor *tx,
>   					 size_t *payload_len, size_t *max_len)
>   {
>   	struct xilinx_dma_tx_descriptor *desc = to_dma_tx_descriptor(tx);
> +	struct xilinx_dma_chan *chan = to_xilinx_chan(tx->chan);
> +
> +	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
> +		struct xilinx_aximcdma_tx_segment *seg;
> +
> +		if (chan->direction == DMA_DEV_TO_MEM) {
> +			seg = list_last_entry(&desc->segments,
> +					      struct xilinx_aximcdma_tx_segment, node);
> +			*max_len = *payload_len = sizeof(seg->hw.s2mm_status) +
> +						  sizeof(seg->hw.s2mm_sideband_status) +
> +						  sizeof(seg->hw.app);
> +			return &seg->hw.s2mm_status;
> +		}
> +		seg = list_first_entry(&desc->segments,
> +				       struct xilinx_aximcdma_tx_segment, node);
> +		*max_len = *payload_len = sizeof(seg->hw.mm2s_ctrl_sideband) +
> +					  sizeof(seg->hw.mm2s_status) +
> +					  sizeof(seg->hw.app);
> +		return &seg->hw.mm2s_ctrl_sideband;
> +	}
> +
>   	struct xilinx_axidma_tx_segment *seg;
>   
> -	*max_len = *payload_len = sizeof(u32) * XILINX_DMA_NUM_APP_WORDS;
> -	seg = list_first_entry(&desc->segments,
> -			       struct xilinx_axidma_tx_segment, node);
> +	if (chan->direction == DMA_DEV_TO_MEM)
> +		seg = list_last_entry(&desc->segments,
> +				      struct xilinx_axidma_tx_segment, node);
> +	else
> +		seg = list_first_entry(&desc->segments,
> +				       struct xilinx_axidma_tx_segment, node);
> +
> +	*max_len = *payload_len = sizeof(seg->hw.app);
>   	return seg->hw.app;
>   }
>   
> @@ -2639,6 +2673,9 @@ xilinx_mcdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
>   		segment->hw.control |= XILINX_MCDMA_BD_EOP;
>   	}
>   
> +	if (chan->xdev->has_axistream_connected)
> +		desc->async_tx.metadata_ops = &xilinx_dma_metadata_ops;
> +
>   	return &desc->async_tx;
>   
>   error:
> @@ -3287,7 +3324,8 @@ static int xilinx_dma_probe(struct platform_device *pdev)
>   
>   	dma_set_max_seg_size(xdev->dev, xdev->max_buffer_len);
>   
> -	if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
> +	if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA ||
> +	    xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
>   		xdev->has_axistream_connected =
>   			of_property_read_bool(node, "xlnx,axistream-connected");
>   	}



^ permalink raw reply

* Re: [RFC PATCH v1 8/8] misc/arm-cla: Add userspace interface
From: Arnd Bergmann @ 2026-07-17 12:54 UTC (permalink / raw)
  To: Ryan Roberts, Greg Kroah-Hartman, Catalin Marinas, Will Deacon,
	Mark Rutland, Jean-Philippe Brucker, Oded Gabbay, Jonathan Corbet
  Cc: linux-kernel, linux-arm-kernel, dri-devel, linux-doc
In-Reply-To: <20260717104759.123203-9-ryan.roberts@arm.com>

On Fri, Jul 17, 2026, at 12:47, Ryan Roberts wrote:
> Expose CLA devices through a character device so userspace can enumerate
> the available hardware and map accelerator register frames.
>
> Define version 1 of the CLA UAPI with a GET_PARAM ioctl. Report device
> topology, CPU affinity, domain membership, mmap offsets, architecture
> version and attached accelerator masks, together with the IIDR, DEVARCH
> and REVIDR of each accelerator.
>
> CLA registers can only be read from the CPU local to the device, while
> enumeration may occur on any CPU. Validate the supported CLA
> architecture version during device setup and cache the CLA and
> accelerator identification registers for later ioctl queries.

This interface looks very raw at the moment, I expect this will have
one or more larger redesigns.

Most importantly, a single character device to expose an arbitrary
number of underlying hardware features is an inherently flawed security
model. If any specific accelerator is ever found to have a
major vulnerability, that would mean administrators will have to
disable all of them by default.

> Support shared read-write mmap of one or more CLA register pages. Create
> a context for every domain covered by the mapping and resolve faults
> only while that context owns the domain. Queue unassigned contexts with
> the domain scheduler, drop mmap_lock while waiting for assignment and
> retry the fault after the context is woken.

I still need some time to better understand what this means.
Does a CPU have multiple concurrently running contexts? Is a
user process able to starve the allocation of other processes
by just requesting a lot of them?

> +static long cla_ioctl_get_param(unsigned long arg)
> +{
> +	struct arm_cla_param __user *uparam = (void __user *)arg;
> +	struct arm_cla_param param;
> +	int accel_id;
> +	int dev_id;
> +	int ret;
> +
> +	if (copy_from_user(&param, uparam, sizeof(param)))
> +		return -EFAULT;
> +
> +	ret = cla_ioctl_validate_param(&param);
> +	if (ret)
> +		return ret;
> +
> +	dev_id = dev_nospec(ARM_CLA_PARAM_INDEX_DEV(param.index));
> +	accel_id = accel_nospec(ARM_CLA_PARAM_INDEX_ACCEL(param.index));

Why is the dev_id/accel_id not a property of the device node itself?

> +	switch (param.param) {
> +	case ARM_CLA_PARAM_UABI_VERSION:
> +		param.value = ARM_CLA_UABI_VERSION;
> +		break;

UABI definitions are not versioned, you have to stay compatible
indefinitely. If you need something else, add a new command.

> +	wait_event_interruptible(ctx->waitq,
> +				 READ_ONCE(domain->assigned_ctx) == ctx ||
> +				 cla_ctx_is_dying(ctx) ||
> +				 READ_ONCE(domain->broken));

If you call wait_event_interruptible(), you have to check the return
code and deal with it being interrupted.

> +static const struct file_operations cla_fops = {
> +	.owner = THIS_MODULE,
> +	.mmap = cla_file_mmap,
> +	.unlocked_ioctl = cla_file_ioctl,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl = cla_file_ioctl,
> +#endif

No need for the #ifdef here. Technically setting .compat_ioctl=compat_ptr_ioctl
is the correct way here, though that may change in the future now that
s390 compat mode is gone.

      Arnd


^ permalink raw reply

* Re: [PATCH v6 3/8] KVM: arm64: Factor out reusable vCPU reset helpers
From: Steffen Eiden @ 2026-07-17 12:43 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, Oliver Upton, kvmarm, linux-arm-kernel,
	linux-kernel, Catalin Marinas, Will Deacon, Joey Gouly,
	Suzuki K Poulose, Zenghui Yu, Vincent Donnefort, Quentin Perret,
	Sebastian Ene, Hyunwoo Kim, Fuad Tabba
In-Reply-To: <20260715081238.1891918-4-fuad.tabba@linux.dev>

On Wed, Jul 15, 2026 at 09:12:33AM +0100, Fuad Tabba wrote:
> Pull the reusable pieces out of kvm_reset_vcpu(): expose the reset
> PSTATE values in kvm_arm.h, and split the core register reset and the
> PSCI-driven reset into kvm_reset_vcpu_core() and kvm_reset_vcpu_psci().
> A follow-up series reuses these to reset protected vCPUs at EL2.
> 
> No functional change intended.
> 
> Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>

That looks good. There is another usecase for this. I did a very similar
thing in [1] to reuse the reset functionality for arm64 on s390. I  just
did not the move the code to a different file.

FWIW: 

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>

[1] https://lore.kernel.org/all/20260706085229.979525-11-seiden@linux.ibm.com/


	Steffen



^ permalink raw reply

* Re: [RFC PATCH v1 3/8] misc/arm-cla: Probe firmware-described devices
From: Ryan Roberts @ 2026-07-17 12:36 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Catalin Marinas, Will Deacon,
	Mark Rutland, Jean-Philippe Brucker, Oded Gabbay, Jonathan Corbet
  Cc: linux-kernel, linux-arm-kernel, dri-devel, linux-doc
In-Reply-To: <7927c483-57ed-4253-a7df-40104ff2b192@app.fastmail.com>

On 17/07/2026 13:25, Arnd Bergmann wrote:
> On Fri, Jul 17, 2026, at 12:47, Ryan Roberts wrote:
>> +
>> +static const struct of_device_id cla_of_match[] = {
>> +	{.compatible = "arm,cla",},
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, cla_of_match);
> 
> This is missing a device tree binding, and probably needs to versioned
> in some form.

Given the aims of the RFC, I didn't think this would be a blocker for now - of
course it's on the list for if/when we get further along the road.

Thanks,
Ryan


> 
>       Arnd



^ permalink raw reply

* Re: [PATCH] dmaengine: Constify struct dma_descriptor_metadata_ops
From: Vinod Koul @ 2026-07-17 12:35 UTC (permalink / raw)
  To: Vignesh Raghavendra, Frank Li, Michal Simek, Christophe JAILLET
  Cc: linux-kernel, kernel-janitors, dmaengine, linux-arm-kernel
In-Reply-To: <b0a22171f3ed68e156a2fa84383e99c23ec6b2ff.1784037977.git.christophe.jaillet@wanadoo.fr>


On Tue, 14 Jul 2026 16:06:33 +0200, Christophe JAILLET wrote:
> 'struct dma_descriptor_metadata_ops' in not modified in these drivers.
> 
> Constifying these structures moves some data to a read-only section, so
> increases overall security, especially when the structure holds some
> function pointers.
> 
> On a x86_64, with allmodconfig, as an example:
> Before:
> ======
>    text	   data	    bss	    dec	    hex	filename
>  120635	  21584	     64	 142283	  22bcb	drivers/dma/xilinx/xilinx_dma.o
> 
> [...]

Applied, thanks!

[1/1] dmaengine: Constify struct dma_descriptor_metadata_ops
      commit: 338c853b7c3b422fefda195b9b8010e40611c96f

Best regards,
-- 
~Vinod




^ permalink raw reply

* Re: (subset) [PATCH v7 0/2] dmaengine: arm-dma350: handle shared channel IRQ wiring on sky1
From: Vinod Koul @ 2026-07-17 12:35 UTC (permalink / raw)
  To: peter.chen, fugang.duan, robh, krzk+dt, conor+dt, ychuang3,
	schung, robin.murphy, Frank.Li, Jun Guo
  Cc: dmaengine, devicetree, linux-kernel, cix-kernel-upstream,
	linux-arm-kernel
In-Reply-To: <20260521072924.3000282-1-jun.guo@cixtech.com>


On Thu, 21 May 2026 15:29:22 +0800, Jun Guo wrote:
> This series updates DMA-350 support for the SKY1 integration where all
> DMA
> channel interrupt outputs are wired to the same GIC SPI.
> 
> Patch 1 enables DMANSECCTRL.INTREN_ANYCHINTR in the driver so
> per-channel
> interrupt status is propagated even when channels share one parent IRQ
> line.
> 
> [...]

Applied, thanks!

[1/2] dmaengine: arm-dma350: enable ANYCH interrupt for shared IRQ wiring
      commit: 643c1e1ae3eb3cd9be31e83c2240e41849a6cb56

Best regards,
-- 
~Vinod




^ permalink raw reply

* Re: [RFC PATCH v1 0/8] Arm Core Local Accelerator Driver
From: Ryan Roberts @ 2026-07-17 12:33 UTC (permalink / raw)
  To: Marc Zyngier, Will Deacon
  Cc: Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Mark Rutland,
	Jean-Philippe Brucker, Oded Gabbay, Jonathan Corbet, linux-kernel,
	linux-arm-kernel, dri-devel, linux-doc, oupton, hch, jgg
In-Reply-To: <87ech1rf6n.wl-maz@kernel.org>

On 17/07/2026 13:09, Marc Zyngier wrote:
> Thanks Will for roping me in.

Sorry - I intentionally didn't include you because last time we spoke you said
you were only really interested in discussions on the virt side of things and I
didn't want to spam your inbox. Perhaps the wrong decision...

> 
> On Fri, 17 Jul 2026 12:33:17 +0100,
> Will Deacon <will@kernel.org> wrote:
>>
>>> I'm deliberately constraining the scope to bare-metal support for now.
>>> Virtualization is something we are considering (and have prototyped), but plan
>>> to post a separate RFC for that as follow-up, once we have agreement on
>>> direction for the bare-metal driver.
>>
>> I'd actually like to see what the virtualisation part looks like first
>> because doing it as a bolt-on later feels like the wrong approach. The
>> structure you have at the moment is remarkably clean, given the
>> architectural/CPU interactions (this thing even apparently builds as a
>> module, nice!), but I'm unsure how far you can push the separation once
>> you need to start hacking at KVM. Maybe the MMU notifiers are enough,
>> but I can't tell.
> 
> +1.
> 
> Virtualisation cannot be a "bolt on the side" exercise. It is an
> integral part of the arm64 tree, particularly for memory management
> and scheduling, all of which have a direct impact on KVM.
> 
> I don't think we can really evaluate anything here without looking at
> the full picture.

OK understood - as per reply to Will, we'll prioritise doing a version with virt
support.

Thanks,
Ryan


> 
> Thanks,
> 
> 	M.
> 



^ permalink raw reply

* Re: [RFC PATCH v1 0/8] Arm Core Local Accelerator Driver
From: Ryan Roberts @ 2026-07-17 12:30 UTC (permalink / raw)
  To: Will Deacon
  Cc: Greg Kroah-Hartman, Arnd Bergmann, Catalin Marinas, Mark Rutland,
	Jean-Philippe Brucker, Oded Gabbay, Jonathan Corbet, linux-kernel,
	linux-arm-kernel, dri-devel, linux-doc, maz, oupton, hch, jgg
In-Reply-To: <aloS_T3LQvpHeZUV@willie-the-truck>

Hi Will,

On 17/07/2026 12:33, Will Deacon wrote:
> Hi Ryan,
> 
> I haven't bothered to look at the code (looks like Sashiko is having fun
> with that), but I'm going to jump at this bit:

Thanks for the quick reply!

JP and I had a quick glance at the Sashiko feedback; looks like some useful
stuff, and a few false alarms. Nothing cricial though. Not that it matters too
much for this stage of discussion.

> 
> On Fri, Jul 17, 2026 at 11:47:44AM +0100, Ryan Roberts wrote:
>> * User space availability: The kernel driver exposes the capabilities of the
>>   hardware to user space. Arm plans to open source a user space driver, but does
>>   not yet have any committed date. I'd like to understand if the availability of
>>   this component will be a prerequisite for upstream acceptance of the kernel
>>   driver; either way, I'm hoping we can at least progress with some discussion
>>   in its absence.
> 
> From my perspective, I'm not particularly interested in having code in
> the upstream kernel tree that we can't meaningfully exercise or benefit
> from. I also think that the incentive for Arm to open source the
> user-space driver practically disappears if we merge the kernel part
> first. So, at the moment, this just looks like a burden to me, especially

Understood. Now that I have such a clear statement, hopefully I can use that to
work internally to get commitments and dates to build a proper plan. What I'm
trying to get out of this RFC though, is "is having an open source user space
the main blocker, or are there other significant challenges here too?".

> as it appears to create a brand new, device-specific UAPI for what is
> ostensibly a form of SVA - something which the community is actively
> working on already.
> 
> Relatedly, is there a spec and/or fastmodel/qemu (sorry...) support for
> this?

They both exist internally of course. There is a plan to publish the spec, but I
don't think we will converge on a timeframe until after the summer now. I'll
follow up regarding fastmodel.

> 
>> I'm deliberately constraining the scope to bare-metal support for now.
>> Virtualization is something we are considering (and have prototyped), but plan
>> to post a separate RFC for that as follow-up, once we have agreement on
>> direction for the bare-metal driver.
> 
> I'd actually like to see what the virtualisation part looks like first
> because doing it as a bolt-on later feels like the wrong approach. The
> structure you have at the moment is remarkably clean, given the
> architectural/CPU interactions (this thing even apparently builds as a
> module, nice!), but I'm unsure how far you can push the separation once
> you need to start hacking at KVM. Maybe the MMU notifiers are enough,
> but I can't tell.

The virtualization implementation is not as advanced as bare-metal and JP can
probably comment on it in more detail, but the intention is to expose the HW
using VFIO-MDEV (mediated device framework). We will need exported functions
from (the arm64 part of) KVM to grab the S2 pgdir and to get/put the VMID - we
would not be using MMU notifiers but instead sharing the pgtable and VMID and
piggybacking the CPU's TBLI operations. My current expectation is that it can be
made to work without deep KVM integration and still as a module, but let's see
where we get to with the code. (I have spoken with Marc about this briefly - I
would say he doesn't love it, but agreed to look at the code when we have it).

But I think I'm hearing that to get into a deeper discussion we will need to
post virt support? In which case, I'll prioritise getting something together.
I'm about to be on sabbatical for 4 weeks though, so probably won't be until
~early Sept.

Thanks,
Ryan


> 
> Will



^ permalink raw reply

* Re: [RFC PATCH v1 3/8] misc/arm-cla: Probe firmware-described devices
From: Arnd Bergmann @ 2026-07-17 12:25 UTC (permalink / raw)
  To: Ryan Roberts, Greg Kroah-Hartman, Catalin Marinas, Will Deacon,
	Mark Rutland, Jean-Philippe Brucker, Oded Gabbay, Jonathan Corbet
  Cc: linux-kernel, linux-arm-kernel, dri-devel, linux-doc
In-Reply-To: <20260717104759.123203-4-ryan.roberts@arm.com>

On Fri, Jul 17, 2026, at 12:47, Ryan Roberts wrote:
> +
> +static const struct of_device_id cla_of_match[] = {
> +	{.compatible = "arm,cla",},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, cla_of_match);

This is missing a device tree binding, and probably needs to versioned
in some form.

      Arnd


^ permalink raw reply

* [PATCH v1 1/4] dt-bindings: lcdif: Add interface pixel format
From: Francesco Dolcini @ 2026-07-17 12:18 UTC (permalink / raw)
  To: Marek Vasut, Stefan Agner, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam
  Cc: Francesco Dolcini, dri-devel, devicetree, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260717121847.488148-1-francesco@dolcini.it>

From: Francesco Dolcini <francesco.dolcini@toradex.com>

LCDIF has a hardware-configurable LCD_DATABUS_WIDTH field which selects
the parallel data bus transfer width. The selected mode changes how color
component bits are assigned to the LCD_DATA pins.

This must match the board wiring. On Colibri iMX6ULL, for example, only
18 LCD data lines are routed. A 24-bit panel can still be connected by
wiring the available LCD_DATA signals to the appropriate panel inputs.
Without describing the resulting interface format, LCDIF may assign the
color components to different data lines, causing shifted colors.

Add interface-pix-fmt so the binding can describe the configured LCDIF
bus format. The same property is already used by
display/imx/fsl,imx-parallel-display.yaml.

Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
---
 Documentation/devicetree/bindings/display/fsl,lcdif.yaml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/fsl,lcdif.yaml b/Documentation/devicetree/bindings/display/fsl,lcdif.yaml
index 2dd0411ec651..209ee5a96e39 100644
--- a/Documentation/devicetree/bindings/display/fsl,lcdif.yaml
+++ b/Documentation/devicetree/bindings/display/fsl,lcdif.yaml
@@ -58,6 +58,13 @@ properties:
     items:
       - const: rx
 
+  interface-pix-fmt:
+    $ref: /schemas/types.yaml#/definitions/string
+    enum:
+      - rgb24
+      - rgb565
+      - rgb666
+
   interrupts:
     items:
       - description: LCDIF DMA interrupt
-- 
2.47.3



^ permalink raw reply related

* [PATCH v1 4/4] ARM: dts: imx7-colibri: Set LCDIF format
From: Francesco Dolcini @ 2026-07-17 12:18 UTC (permalink / raw)
  To: Marek Vasut, Stefan Agner, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam
  Cc: Francesco Dolcini, dri-devel, devicetree, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260717121847.488148-1-francesco@dolcini.it>

From: Francesco Dolcini <francesco.dolcini@toradex.com>

The lcd DPI interface on colibri imx6ull uses a 18-bit width bus, set
the interface-pix-fmt accordingly.

Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
---
 arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi b/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi
index 8666dcd7fe97..f820a613109a 100644
--- a/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi
@@ -533,6 +533,7 @@ &lcdif {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_lcdif_dat
 		     &pinctrl_lcdif_ctrl>;
+	interface-pix-fmt = "rgb666";
 	status = "disabled";
 
 	port {
-- 
2.47.3



^ permalink raw reply related

* [PATCH v1 3/4] ARM: dts: imx6ull-colibri: Set LCDIF format
From: Francesco Dolcini @ 2026-07-17 12:18 UTC (permalink / raw)
  To: Marek Vasut, Stefan Agner, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam
  Cc: Francesco Dolcini, dri-devel, devicetree, linux-kernel, imx,
	linux-arm-kernel
In-Reply-To: <20260717121847.488148-1-francesco@dolcini.it>

From: Francesco Dolcini <francesco.dolcini@toradex.com>

The lcd DPI interface on colibri imx6ull uses a 18-bit width bus, set
the interface-pix-fmt accordingly.

Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
---
 arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi
index ec3c1e7301f4..e126fade2304 100644
--- a/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi
+++ b/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi
@@ -225,6 +225,7 @@ &lcdif {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_lcdif_dat
 		     &pinctrl_lcdif_ctrl>;
+	interface-pix-fmt = "rgb666";
 	status = "disabled";
 
 	port {
-- 
2.47.3



^ permalink raw reply related

* [PATCH v1 0/4] drm: mxsfb: Support LCDIF interface pixel format
From: Francesco Dolcini @ 2026-07-17 12:18 UTC (permalink / raw)
  To: Marek Vasut, Stefan Agner, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam
  Cc: Francesco Dolcini, dri-devel, devicetree, linux-kernel, imx,
	linux-arm-kernel

From: Francesco Dolcini <francesco.dolcini@toradex.com>

The LCDIF data bus format describes how color component bits are assigned
to the physical LCD_DATA pins. This is a property of the LCDIF-to-display
wiring and can differ from the input format advertised by the downstream
panel or bridge.

Such a distinction is required when a narrower LCDIF bus is connected to
a wider display interface. For example, an 18-bit or 16-bit LCDIF bus can
drive a 24-bit display by wiring the available color bits to the
appropriate display inputs. Configuring LCDIF from the display's 24-bit
format in this case selects the wrong LCD_DATABUS_WIDTH mode, changing the
color-bit assignment on the LCD_DATA pins and resulting in incorrect
colors.

Add the optional interface-pix-fmt property to the LCDIF binding and make
the mxsfb driver use it in preference to the format reported by the
downstream display. When the property is absent, the existing behavior is
preserved.

This follows the approach already used by the i.MX IPUv3 parallel display
driver in drivers/gpu/drm/imx/ipuv3/parallel-display.c.

Set the interface format for the Colibri iMX6ULL and Colibri iMX7 boards,
which route an 18-bit LCD interface.

Francesco Dolcini (4):
  dt-bindings: lcdif: Add interface pixel format
  drm: mxsfb: Allow optional LCDIF interface format override
  ARM: dts: imx6ull-colibri: Set LCDIF format
  ARM: dts: imx7-colibri: Set LCDIF format

 .../devicetree/bindings/display/fsl,lcdif.yaml   |  7 +++++++
 arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi   |  1 +
 arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi      |  1 +
 drivers/gpu/drm/mxsfb/mxsfb_drv.c                | 16 ++++++++++++++++
 drivers/gpu/drm/mxsfb/mxsfb_drv.h                |  2 ++
 drivers/gpu/drm/mxsfb/mxsfb_kms.c                |  8 ++++++++
 6 files changed, 35 insertions(+)

-- 
2.47.3



^ 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