* [PATCH v5 30/38] KVM: arm64: Implement the MEM_UNSHARE hypercall for protected VMs
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Implement the ARM_SMCCC_KVM_FUNC_MEM_UNSHARE hypercall to allow
protected VMs to unshare memory that was previously shared with the host
using the ARM_SMCCC_KVM_FUNC_MEM_SHARE hypercall.
Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 +
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 34 +++++++++++++++++++
arch/arm64/kvm/hyp/nvhe/pkvm.c | 22 ++++++++++++
3 files changed, 57 insertions(+)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index fea8aecae5ef..99d8398afe20 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -35,6 +35,7 @@ extern unsigned long hyp_nr_cpus;
int __pkvm_prot_finalize(void);
int __pkvm_host_share_hyp(u64 pfn);
int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn);
+int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn);
int __pkvm_host_unshare_hyp(u64 pfn);
int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);
int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages);
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 593eca37f863..db94323b430c 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -991,6 +991,40 @@ int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn)
return ret;
}
+int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn)
+{
+ struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
+ u64 meta, phys, ipa = hyp_pfn_to_phys(gfn);
+ kvm_pte_t pte;
+ int ret;
+
+ host_lock_component();
+ guest_lock_component(vm);
+
+ ret = get_valid_guest_pte(vm, ipa, &pte, &phys);
+ if (ret)
+ goto unlock;
+
+ ret = -EPERM;
+ if (pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)) != PKVM_PAGE_SHARED_OWNED)
+ goto unlock;
+ if (__host_check_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED))
+ goto unlock;
+
+ ret = 0;
+ meta = host_stage2_encode_gfn_meta(vm, gfn);
+ WARN_ON(host_stage2_set_owner_metadata_locked(phys, PAGE_SIZE,
+ PKVM_ID_GUEST, meta));
+ WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, PAGE_SIZE, phys,
+ pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_OWNED),
+ &vcpu->vcpu.arch.pkvm_memcache, 0));
+unlock:
+ guest_unlock_component(vm);
+ host_unlock_component();
+
+ return ret;
+}
+
int __pkvm_host_unshare_hyp(u64 pfn)
{
u64 phys = hyp_pfn_to_phys(pfn);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 408307603863..6f3b94a37fe3 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -1025,6 +1025,19 @@ static bool pkvm_memshare_call(u64 *ret, struct kvm_vcpu *vcpu, u64 *exit_code)
return false;
}
+static void pkvm_memunshare_call(u64 *ret, struct kvm_vcpu *vcpu)
+{
+ struct pkvm_hyp_vcpu *hyp_vcpu;
+ u64 ipa = smccc_get_arg1(vcpu);
+
+ if (!PAGE_ALIGNED(ipa))
+ return;
+
+ hyp_vcpu = container_of(vcpu, struct pkvm_hyp_vcpu, vcpu);
+ if (!__pkvm_guest_unshare_host(hyp_vcpu, hyp_phys_to_pfn(ipa)))
+ ret[0] = SMCCC_RET_SUCCESS;
+}
+
/*
* Handler for protected VM HVC calls.
*
@@ -1042,6 +1055,7 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO);
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_SHARE);
+ val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_UNSHARE);
break;
case ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID:
if (smccc_get_arg1(vcpu) ||
@@ -1060,6 +1074,14 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
handled = pkvm_memshare_call(val, vcpu, exit_code);
break;
+ case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
+ if (smccc_get_arg2(vcpu) ||
+ smccc_get_arg3(vcpu)) {
+ break;
+ }
+
+ pkvm_memunshare_call(val, vcpu);
+ break;
default:
/* Punt everything else back to the host, for now. */
handled = false;
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 25/38] KVM: arm64: Introduce hypercall to force reclaim of a protected page
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Introduce a new hypercall, __pkvm_force_reclaim_guest_page(), to allow
the host to forcefully reclaim a physical page that was previous donated
to a protected guest. This results in the page being zeroed and the
previous guest mapping being poisoned so that new pages cannot be
subsequently donated at the same IPA.
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/include/asm/kvm_pgtable.h | 6 +
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 +
arch/arm64/kvm/hyp/include/nvhe/memory.h | 6 +
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 1 +
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 129 +++++++++++++++++-
arch/arm64/kvm/hyp/nvhe/pkvm.c | 4 +-
8 files changed, 154 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index b6df8f64d573..04a230e906a7 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -90,6 +90,7 @@ enum __kvm_host_smccc_func {
__KVM_HOST_SMCCC_FUNC___pkvm_unreserve_vm,
__KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
__KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu,
+ __KVM_HOST_SMCCC_FUNC___pkvm_force_reclaim_guest_page,
__KVM_HOST_SMCCC_FUNC___pkvm_reclaim_dying_guest_page,
__KVM_HOST_SMCCC_FUNC___pkvm_start_teardown_vm,
__KVM_HOST_SMCCC_FUNC___pkvm_finalize_teardown_vm,
diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
index 2df22640833c..41a8687938eb 100644
--- a/arch/arm64/include/asm/kvm_pgtable.h
+++ b/arch/arm64/include/asm/kvm_pgtable.h
@@ -116,6 +116,12 @@ enum kvm_invalid_pte_type {
* ownership.
*/
KVM_HOST_INVALID_PTE_TYPE_DONATION,
+
+ /*
+ * The page has been forcefully reclaimed from the guest by the
+ * host.
+ */
+ KVM_GUEST_INVALID_PTE_TYPE_POISONED,
};
static inline bool kvm_pte_valid(kvm_pte_t pte)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index 29f81a1d9e1f..acc031103600 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -40,6 +40,7 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages);
int __pkvm_host_share_ffa(u64 pfn, u64 nr_pages);
int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages);
int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu);
+int __pkvm_host_force_reclaim_page_guest(phys_addr_t phys);
int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm);
int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu *vcpu,
enum kvm_pgtable_prot prot);
diff --git a/arch/arm64/kvm/hyp/include/nvhe/memory.h b/arch/arm64/kvm/hyp/include/nvhe/memory.h
index dee1a406b0c2..4cedb720c75d 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/memory.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/memory.h
@@ -30,6 +30,12 @@ enum pkvm_page_state {
* struct hyp_page.
*/
PKVM_NOPAGE = BIT(0) | BIT(1),
+
+ /*
+ * 'Meta-states' which aren't encoded directly in the PTE's SW bits (or
+ * the hyp_vmemmap entry for the host)
+ */
+ PKVM_POISON = BIT(2),
};
#define PKVM_PAGE_STATE_MASK (BIT(0) | BIT(1))
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index 506831804f64..a5a7bb453f3e 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -78,6 +78,7 @@ int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn);
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
int __pkvm_finalize_teardown_vm(pkvm_handle_t handle);
+struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle);
struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
unsigned int vcpu_idx);
void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu);
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 6db5aebd92dc..456c83207717 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -573,6 +573,13 @@ static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt)
cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva);
}
+static void handle___pkvm_force_reclaim_guest_page(struct kvm_cpu_context *host_ctxt)
+{
+ DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
+
+ cpu_reg(host_ctxt, 1) = __pkvm_host_force_reclaim_page_guest(phys);
+}
+
static void handle___pkvm_reclaim_dying_guest_page(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
@@ -634,6 +641,7 @@ static const hcall_t host_hcall[] = {
HANDLE_FUNC(__pkvm_unreserve_vm),
HANDLE_FUNC(__pkvm_init_vm),
HANDLE_FUNC(__pkvm_init_vcpu),
+ HANDLE_FUNC(__pkvm_force_reclaim_guest_page),
HANDLE_FUNC(__pkvm_reclaim_dying_guest_page),
HANDLE_FUNC(__pkvm_start_teardown_vm),
HANDLE_FUNC(__pkvm_finalize_teardown_vm),
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 51cb5c89fd20..73bdbd4a508e 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -616,6 +616,35 @@ static u64 host_stage2_encode_gfn_meta(struct pkvm_hyp_vm *vm, u64 gfn)
FIELD_PREP(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, gfn);
}
+static int host_stage2_decode_gfn_meta(kvm_pte_t pte, struct pkvm_hyp_vm **vm,
+ u64 *gfn)
+{
+ pkvm_handle_t handle;
+ u64 meta;
+
+ if (WARN_ON(kvm_pte_valid(pte)))
+ return -EINVAL;
+
+ if (FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) !=
+ KVM_HOST_INVALID_PTE_TYPE_DONATION) {
+ return -EINVAL;
+ }
+
+ if (FIELD_GET(KVM_HOST_DONATION_PTE_OWNER_MASK, pte) != PKVM_ID_GUEST)
+ return -EPERM;
+
+ meta = FIELD_GET(KVM_HOST_DONATION_PTE_EXTRA_MASK, pte);
+ handle = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK, meta);
+ *vm = get_vm_by_handle(handle);
+ if (!*vm) {
+ /* We probably raced with teardown; try again */
+ return -EAGAIN;
+ }
+
+ *gfn = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, meta);
+ return 0;
+}
+
static bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot)
{
/*
@@ -801,8 +830,20 @@ static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_pa
return 0;
}
+static bool guest_pte_is_poisoned(kvm_pte_t pte)
+{
+ if (kvm_pte_valid(pte))
+ return false;
+
+ return FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) ==
+ KVM_GUEST_INVALID_PTE_TYPE_POISONED;
+}
+
static enum pkvm_page_state guest_get_page_state(kvm_pte_t pte, u64 addr)
{
+ if (guest_pte_is_poisoned(pte))
+ return PKVM_POISON;
+
if (!kvm_pte_valid(pte))
return PKVM_NOPAGE;
@@ -831,6 +872,8 @@ static int get_valid_guest_pte(struct pkvm_hyp_vm *vm, u64 ipa, kvm_pte_t *ptep,
ret = kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, &level);
if (ret)
return ret;
+ if (guest_pte_is_poisoned(pte))
+ return -EHWPOISON;
if (!kvm_pte_valid(pte))
return -ENOENT;
if (level != KVM_PGTABLE_LAST_LEVEL)
@@ -1096,6 +1139,86 @@ static void hyp_poison_page(phys_addr_t phys)
hyp_fixmap_unmap();
}
+static int host_stage2_get_guest_info(phys_addr_t phys, struct pkvm_hyp_vm **vm,
+ u64 *gfn)
+{
+ enum pkvm_page_state state;
+ kvm_pte_t pte;
+ s8 level;
+ int ret;
+
+ if (!addr_is_memory(phys))
+ return -EFAULT;
+
+ state = get_host_state(hyp_phys_to_page(phys));
+ switch (state) {
+ case PKVM_PAGE_OWNED:
+ case PKVM_PAGE_SHARED_OWNED:
+ case PKVM_PAGE_SHARED_BORROWED:
+ /* The access should no longer fault; try again. */
+ return -EAGAIN;
+ case PKVM_NOPAGE:
+ break;
+ default:
+ return -EPERM;
+ }
+
+ ret = kvm_pgtable_get_leaf(&host_mmu.pgt, phys, &pte, &level);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(level != KVM_PGTABLE_LAST_LEVEL))
+ return -EINVAL;
+
+ return host_stage2_decode_gfn_meta(pte, vm, gfn);
+}
+
+int __pkvm_host_force_reclaim_page_guest(phys_addr_t phys)
+{
+ struct pkvm_hyp_vm *vm;
+ u64 gfn, ipa, pa;
+ kvm_pte_t pte;
+ int ret;
+
+ phys &= PAGE_MASK;
+
+ hyp_spin_lock(&vm_table_lock);
+ host_lock_component();
+
+ ret = host_stage2_get_guest_info(phys, &vm, &gfn);
+ if (ret)
+ goto unlock_host;
+
+ ipa = hyp_pfn_to_phys(gfn);
+ guest_lock_component(vm);
+ ret = get_valid_guest_pte(vm, ipa, &pte, &pa);
+ if (ret)
+ goto unlock_guest;
+
+ WARN_ON(pa != phys);
+ if (guest_get_page_state(pte, ipa) != PKVM_PAGE_OWNED) {
+ ret = -EPERM;
+ goto unlock_guest;
+ }
+
+ /* We really shouldn't be allocating, so don't pass a memcache */
+ ret = kvm_pgtable_stage2_annotate(&vm->pgt, ipa, PAGE_SIZE, NULL,
+ KVM_GUEST_INVALID_PTE_TYPE_POISONED,
+ 0);
+ if (ret)
+ goto unlock_guest;
+
+ hyp_poison_page(phys);
+ WARN_ON(host_stage2_set_owner_locked(phys, PAGE_SIZE, PKVM_ID_HOST));
+unlock_guest:
+ guest_unlock_component(vm);
+unlock_host:
+ host_unlock_component();
+ hyp_spin_unlock(&vm_table_lock);
+
+ return ret;
+}
+
int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm)
{
u64 ipa = hyp_pfn_to_phys(gfn);
@@ -1130,7 +1253,11 @@ int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm)
guest_unlock_component(vm);
host_unlock_component();
- return ret;
+ /*
+ * -EHWPOISON implies that the page was forcefully reclaimed already
+ * so return success for the GUP pin to be dropped.
+ */
+ return ret && ret != -EHWPOISON ? ret : 0;
}
int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu)
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 0ba6423cd0d5..cdeefe3d74ff 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -230,10 +230,12 @@ void pkvm_hyp_vm_table_init(void *tbl)
/*
* Return the hyp vm structure corresponding to the handle.
*/
-static struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle)
+struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle)
{
unsigned int idx = vm_handle_to_idx(handle);
+ hyp_assert_lock_held(&vm_table_lock);
+
if (unlikely(idx >= KVM_MAX_PVMS))
return NULL;
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 29/38] KVM: arm64: Implement the MEM_SHARE hypercall for protected VMs
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Implement the ARM_SMCCC_KVM_FUNC_MEM_SHARE hypercall to allow protected
VMs to share memory (e.g. the swiotlb bounce buffers) back to the host.
Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 +
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 32 ++++++++++
arch/arm64/kvm/hyp/nvhe/pkvm.c | 61 +++++++++++++++++++
3 files changed, 94 insertions(+)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index 8bc9a2489298..fea8aecae5ef 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -34,6 +34,7 @@ extern unsigned long hyp_nr_cpus;
int __pkvm_prot_finalize(void);
int __pkvm_host_share_hyp(u64 pfn);
+int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn);
int __pkvm_host_unshare_hyp(u64 pfn);
int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);
int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages);
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 31ca3db26fb5..593eca37f863 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -959,6 +959,38 @@ int __pkvm_host_share_hyp(u64 pfn)
return ret;
}
+int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn)
+{
+ struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
+ u64 phys, ipa = hyp_pfn_to_phys(gfn);
+ kvm_pte_t pte;
+ int ret;
+
+ host_lock_component();
+ guest_lock_component(vm);
+
+ ret = get_valid_guest_pte(vm, ipa, &pte, &phys);
+ if (ret)
+ goto unlock;
+
+ ret = -EPERM;
+ if (pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)) != PKVM_PAGE_OWNED)
+ goto unlock;
+ if (__host_check_page_state_range(phys, PAGE_SIZE, PKVM_NOPAGE))
+ goto unlock;
+
+ ret = 0;
+ WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, PAGE_SIZE, phys,
+ pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_SHARED_OWNED),
+ &vcpu->vcpu.arch.pkvm_memcache, 0));
+ WARN_ON(__host_set_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED));
+unlock:
+ guest_unlock_component(vm);
+ host_unlock_component();
+
+ return ret;
+}
+
int __pkvm_host_unshare_hyp(u64 pfn)
{
u64 phys = hyp_pfn_to_phys(pfn);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 1f184c4994fa..408307603863 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -973,6 +973,58 @@ int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
hyp_spin_unlock(&vm_table_lock);
return err;
}
+
+static u64 __pkvm_memshare_page_req(struct kvm_vcpu *vcpu, u64 ipa)
+{
+ u64 elr;
+
+ /* Fake up a data abort (level 3 translation fault on write) */
+ vcpu->arch.fault.esr_el2 = (ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT) |
+ ESR_ELx_WNR | ESR_ELx_FSC_FAULT |
+ FIELD_PREP(ESR_ELx_FSC_LEVEL, 3);
+
+ /* Shuffle the IPA around into the HPFAR */
+ vcpu->arch.fault.hpfar_el2 = (HPFAR_EL2_NS | (ipa >> 8)) & HPFAR_MASK;
+
+ /* This is a virtual address. 0's good. Let's go with 0. */
+ vcpu->arch.fault.far_el2 = 0;
+
+ /* Rewind the ELR so we return to the HVC once the IPA is mapped */
+ elr = read_sysreg(elr_el2);
+ elr -= 4;
+ write_sysreg(elr, elr_el2);
+
+ return ARM_EXCEPTION_TRAP;
+}
+
+static bool pkvm_memshare_call(u64 *ret, struct kvm_vcpu *vcpu, u64 *exit_code)
+{
+ struct pkvm_hyp_vcpu *hyp_vcpu;
+ u64 ipa = smccc_get_arg1(vcpu);
+
+ if (!PAGE_ALIGNED(ipa))
+ goto out_guest;
+
+ hyp_vcpu = container_of(vcpu, struct pkvm_hyp_vcpu, vcpu);
+ switch (__pkvm_guest_share_host(hyp_vcpu, hyp_phys_to_pfn(ipa))) {
+ case 0:
+ ret[0] = SMCCC_RET_SUCCESS;
+ goto out_guest;
+ case -ENOENT:
+ /*
+ * Convert the exception into a data abort so that the page
+ * being shared is mapped into the guest next time.
+ */
+ *exit_code = __pkvm_memshare_page_req(vcpu, ipa);
+ goto out_host;
+ }
+
+out_guest:
+ return true;
+out_host:
+ return false;
+}
+
/*
* Handler for protected VM HVC calls.
*
@@ -989,6 +1041,7 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO);
+ val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_SHARE);
break;
case ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID:
if (smccc_get_arg1(vcpu) ||
@@ -999,6 +1052,14 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
val[0] = PAGE_SIZE;
break;
+ case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID:
+ if (smccc_get_arg2(vcpu) ||
+ smccc_get_arg3(vcpu)) {
+ break;
+ }
+
+ handled = pkvm_memshare_call(val, vcpu, exit_code);
+ break;
default:
/* Punt everything else back to the host, for now. */
handled = false;
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 33/38] KVM: arm64: Extend pKVM page ownership selftests to cover guest donation
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Extend the pKVM page ownership selftests to donate and reclaim a page
to/from a guest.
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index db94323b430c..13d2cb2f5fab 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -1751,6 +1751,7 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
selftest_state.host = PKVM_PAGE_OWNED;
selftest_state.hyp = PKVM_NOPAGE;
@@ -1770,6 +1771,7 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
@@ -1782,6 +1784,7 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
hyp_unpin_shared_mem(virt, virt + size);
assert_page_state();
@@ -1801,6 +1804,7 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
selftest_state.host = PKVM_PAGE_OWNED;
@@ -1817,6 +1821,7 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
selftest_state.guest[1] = PKVM_PAGE_SHARED_BORROWED;
@@ -1830,6 +1835,23 @@ void pkvm_ownership_selftest(void *base)
selftest_state.host = PKVM_PAGE_OWNED;
assert_transition_res(0, __pkvm_host_unshare_guest, gfn + 1, 1, vm);
+ selftest_state.host = PKVM_NOPAGE;
+ selftest_state.guest[0] = PKVM_PAGE_OWNED;
+ assert_transition_res(0, __pkvm_host_donate_guest, pfn, gfn, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn + 1, 1, vcpu, prot);
+ assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
+ assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
+ assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
+
+ selftest_state.host = PKVM_PAGE_OWNED;
+ selftest_state.guest[0] = PKVM_NOPAGE;
+ assert_transition_res(0, __pkvm_host_reclaim_page_guest, gfn, vm);
+
selftest_state.host = PKVM_NOPAGE;
selftest_state.hyp = PKVM_PAGE_OWNED;
assert_transition_res(0, __pkvm_host_donate_hyp, pfn, 1);
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 31/38] KVM: arm64: Allow userspace to create protected VMs when pKVM is enabled
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Introduce a new VM type for KVM/arm64 to allow userspace to request the
creation of a "protected VM" when the host has booted with pKVM enabled.
For now, this feature results in a taint on first use as many aspects of
a protected VM are not yet protected!
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/kvm_pkvm.h | 2 +-
arch/arm64/kvm/arm.c | 8 +++++++-
arch/arm64/kvm/mmu.c | 3 ---
arch/arm64/kvm/pkvm.c | 8 +++++++-
include/uapi/linux/kvm.h | 5 +++++
5 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 7041e398fb4c..2954b311128c 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -17,7 +17,7 @@
#define HYP_MEMBLOCK_REGIONS 128
-int pkvm_init_host_vm(struct kvm *kvm);
+int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);
int pkvm_create_hyp_vm(struct kvm *kvm);
bool pkvm_hyp_vm_is_created(struct kvm *kvm);
void pkvm_destroy_hyp_vm(struct kvm *kvm);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 3589fc08266c..c2b666a46893 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -203,6 +203,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
int ret;
+ if (type & ~KVM_VM_TYPE_ARM_MASK)
+ return -EINVAL;
+
mutex_init(&kvm->arch.config_lock);
#ifdef CONFIG_LOCKDEP
@@ -234,9 +237,12 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
* If any failures occur after this is successful, make sure to
* call __pkvm_unreserve_vm to unreserve the VM in hyp.
*/
- ret = pkvm_init_host_vm(kvm);
+ ret = pkvm_init_host_vm(kvm, type);
if (ret)
goto err_uninit_mmu;
+ } else if (type & KVM_VM_TYPE_ARM_PROTECTED) {
+ ret = -EINVAL;
+ goto err_uninit_mmu;
}
kvm_vgic_early_init(kvm);
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 6a4151e3e4a3..45358ae8a300 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -881,9 +881,6 @@ static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
u64 mmfr0, mmfr1;
u32 phys_shift;
- if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
- return -EINVAL;
-
phys_shift = KVM_VM_TYPE_ARM_IPA_SIZE(type);
if (is_protected_kvm_enabled()) {
phys_shift = kvm_ipa_limit;
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 7f35df29e984..053e4f733e4b 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -225,9 +225,10 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm)
mutex_unlock(&kvm->arch.config_lock);
}
-int pkvm_init_host_vm(struct kvm *kvm)
+int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)
{
int ret;
+ bool protected = type & KVM_VM_TYPE_ARM_PROTECTED;
if (pkvm_hyp_vm_is_created(kvm))
return -EINVAL;
@@ -242,6 +243,11 @@ int pkvm_init_host_vm(struct kvm *kvm)
return ret;
kvm->arch.pkvm.handle = ret;
+ kvm->arch.pkvm.is_protected = protected;
+ if (protected) {
+ pr_warn_once("kvm: protected VMs are experimental and for development only, tainting kernel\n");
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+ }
return 0;
}
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 80364d4dbebb..073b2bcaf560 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -703,6 +703,11 @@ struct kvm_enable_cap {
#define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL
#define KVM_VM_TYPE_ARM_IPA_SIZE(x) \
((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
+
+#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31)
+#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
+ KVM_VM_TYPE_ARM_PROTECTED)
+
/*
* ioctls for /dev/kvm fds:
*/
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 35/38] KVM: arm64: Extend pKVM page ownership selftests to cover forced reclaim
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Extend the pKVM page ownership selftests to forcefully reclaim a donated
page and check that it cannot be re-donated at the same IPA.
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index d8f8ebe59129..0775a82ee4fe 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -1815,8 +1815,20 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
selftest_state.host = PKVM_PAGE_OWNED;
- selftest_state.guest[0] = PKVM_NOPAGE;
- assert_transition_res(0, __pkvm_host_reclaim_page_guest, gfn, vm);
+ selftest_state.guest[0] = PKVM_POISON;
+ assert_transition_res(0, __pkvm_host_force_reclaim_page_guest, phys);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
+
+ selftest_state.host = PKVM_NOPAGE;
+ selftest_state.guest[1] = PKVM_PAGE_OWNED;
+ assert_transition_res(0, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu);
+
+ selftest_state.host = PKVM_PAGE_OWNED;
+ selftest_state.guest[1] = PKVM_NOPAGE;
+ assert_transition_res(0, __pkvm_host_reclaim_page_guest, gfn + 1, vm);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
selftest_state.host = PKVM_NOPAGE;
selftest_state.hyp = PKVM_PAGE_OWNED;
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 32/38] KVM: arm64: Add some initial documentation for pKVM
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Add some initial documentation for pKVM to help people understand what
is supported, the limitations of protected VMs when compared to
non-protected VMs and also what is left to do.
Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
.../admin-guide/kernel-parameters.txt | 4 +-
Documentation/virt/kvm/arm/index.rst | 1 +
Documentation/virt/kvm/arm/pkvm.rst | 106 ++++++++++++++++++
3 files changed, 109 insertions(+), 2 deletions(-)
create mode 100644 Documentation/virt/kvm/arm/pkvm.rst
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 03a550630644..44854a67bc63 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3247,8 +3247,8 @@ Kernel parameters
for the host. To force nVHE on VHE hardware, add
"arm64_sw.hvhe=0 id_aa64mmfr1.vh=0" to the
command-line.
- "nested" is experimental and should be used with
- extreme caution.
+ "nested" and "protected" are experimental and should be
+ used with extreme caution.
kvm-arm.vgic_v3_group0_trap=
[KVM,ARM,EARLY] Trap guest accesses to GICv3 group-0
diff --git a/Documentation/virt/kvm/arm/index.rst b/Documentation/virt/kvm/arm/index.rst
index ec09881de4cf..0856b4942e05 100644
--- a/Documentation/virt/kvm/arm/index.rst
+++ b/Documentation/virt/kvm/arm/index.rst
@@ -10,6 +10,7 @@ ARM
fw-pseudo-registers
hyp-abi
hypercalls
+ pkvm
pvtime
ptp_kvm
vcpu-features
diff --git a/Documentation/virt/kvm/arm/pkvm.rst b/Documentation/virt/kvm/arm/pkvm.rst
new file mode 100644
index 000000000000..514992a79a83
--- /dev/null
+++ b/Documentation/virt/kvm/arm/pkvm.rst
@@ -0,0 +1,106 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================
+Protected KVM (pKVM)
+====================
+
+**NOTE**: pKVM is currently an experimental, development feature and
+subject to breaking changes as new isolation features are implemented.
+Please reach out to the developers at kvmarm@lists.linux.dev if you have
+any questions.
+
+Overview
+========
+
+Booting a host kernel with '``kvm-arm.mode=protected``' enables
+"Protected KVM" (pKVM). During boot, pKVM installs a stage-2 identity
+map page-table for the host and uses it to isolate the hypervisor
+running at EL2 from the rest of the host running at EL1/0.
+
+pKVM permits creation of protected virtual machines (pVMs) by passing
+the ``KVM_VM_TYPE_ARM_PROTECTED`` machine type identifier to the
+``KVM_CREATE_VM`` ioctl(). The hypervisor isolates pVMs from the host by
+unmapping pages from the stage-2 identity map as they are accessed by a
+pVM. Hypercalls are provided for a pVM to share specific regions of its
+IPA space back with the host, allowing for communication with the VMM.
+A Linux guest must be configured with ``CONFIG_ARM_PKVM_GUEST=y`` in
+order to issue these hypercalls.
+
+See hypercalls.rst for more details.
+
+Isolation mechanisms
+====================
+
+pKVM relies on a number of mechanisms to isolate PVMs from the host:
+
+CPU memory isolation
+--------------------
+
+Status: Isolation of anonymous memory and metadata pages.
+
+Metadata pages (e.g. page-table pages and '``struct kvm_vcpu``' pages)
+are donated from the host to the hypervisor during pVM creation and
+are consequently unmapped from the stage-2 identity map until the pVM is
+destroyed.
+
+Similarly to regular KVM, pages are lazily mapped into the guest in
+response to stage-2 page faults handled by the host. However, when
+running a pVM, these pages are first pinned and then unmapped from the
+stage-2 identity map as part of the donation procedure. This gives rise
+to some user-visible differences when compared to non-protected VMs,
+largely due to the lack of MMU notifiers:
+
+* Memslots cannot be moved or deleted once the pVM has started running.
+* Read-only memslots and dirty logging are not supported.
+* With the exception of swap, file-backed pages cannot be mapped into a
+ pVM.
+* Donated pages are accounted against ``RLIMIT_MLOCK`` and so the VMM
+ must have a sufficient resource limit or be granted ``CAP_IPC_LOCK``.
+ The lack of a runtime reclaim mechanism means that memory locked for
+ a pVM will remain locked until the pVM is destroyed.
+* Changes to the VMM address space (e.g. a ``MAP_FIXED`` mmap() over a
+ mapping associated with a memslot) are not reflected in the guest and
+ may lead to loss of coherency.
+* Accessing pVM memory that has not been shared back will result in the
+ delivery of a SIGSEGV.
+* If a system call accesses pVM memory that has not been shared back
+ then it will either return ``-EFAULT`` or forcefully reclaim the
+ memory pages. Reclaimed memory is zeroed by the hypervisor and a
+ subsequent attempt to access it in the pVM will return ``-EFAULT``
+ from the ``VCPU_RUN`` ioctl().
+
+CPU state isolation
+-------------------
+
+Status: **Unimplemented.**
+
+DMA isolation using an IOMMU
+----------------------------
+
+Status: **Unimplemented.**
+
+Proxying of Trustzone services
+------------------------------
+
+Status: FF-A and PSCI calls from the host are proxied by the pKVM
+hypervisor.
+
+The FF-A proxy ensures that the host cannot share pVM or hypervisor
+memory with Trustzone as part of a "confused deputy" attack.
+
+The PSCI proxy ensures that CPUs always have the stage-2 identity map
+installed when they are executing in the host.
+
+Protected VM firmware (pvmfw)
+-----------------------------
+
+Status: **Unimplemented.**
+
+Resources
+=========
+
+Quentin Perret's KVM Forum 2022 talk entitled "Protected KVM on arm64: A
+technical deep dive" remains a good resource for learning more about
+pKVM, despite some of the details having changed in the meantime:
+
+https://www.youtube.com/watch?v=9npebeVFbFw
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 37/38] KVM: arm64: Rename PKVM_PAGE_STATE_MASK
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Rename PKVM_PAGE_STATE_MASK to PKVM_PAGE_STATE_VMEMMAP_MASK to make it
clear that the mask applies to the page state recorded in the entries
of the 'hyp_vmemmap', rather than page states stored elsewhere (e.g. in
the ptes).
Suggested-by: Alexandru Elisei <alexandru.elisei@arm.com>
Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/include/nvhe/memory.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/memory.h b/arch/arm64/kvm/hyp/include/nvhe/memory.h
index 4cedb720c75d..b50712d47f6d 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/memory.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/memory.h
@@ -37,7 +37,7 @@ enum pkvm_page_state {
*/
PKVM_POISON = BIT(2),
};
-#define PKVM_PAGE_STATE_MASK (BIT(0) | BIT(1))
+#define PKVM_PAGE_STATE_VMEMMAP_MASK (BIT(0) | BIT(1))
#define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1)
static inline enum kvm_pgtable_prot pkvm_mkstate(enum kvm_pgtable_prot prot,
@@ -114,12 +114,12 @@ static inline void set_host_state(struct hyp_page *p, enum pkvm_page_state state
static inline enum pkvm_page_state get_hyp_state(struct hyp_page *p)
{
- return p->__hyp_state_comp ^ PKVM_PAGE_STATE_MASK;
+ return p->__hyp_state_comp ^ PKVM_PAGE_STATE_VMEMMAP_MASK;
}
static inline void set_hyp_state(struct hyp_page *p, enum pkvm_page_state state)
{
- p->__hyp_state_comp = state ^ PKVM_PAGE_STATE_MASK;
+ p->__hyp_state_comp = state ^ PKVM_PAGE_STATE_VMEMMAP_MASK;
}
/*
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 34/38] KVM: arm64: Register 'selftest_vm' in the VM table
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
In preparation for extending the pKVM page ownership selftests to cover
forceful reclaim of donated pages, rework the creation of the
'selftest_vm' so that it is registered in the VM table while the tests
are running.
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 +
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 53 ++++---------------
arch/arm64/kvm/hyp/nvhe/pkvm.c | 49 +++++++++++++++++
3 files changed, 61 insertions(+), 43 deletions(-)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index 99d8398afe20..5031879ccb87 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -76,6 +76,8 @@ static __always_inline void __load_host_stage2(void)
#ifdef CONFIG_NVHE_EL2_DEBUG
void pkvm_ownership_selftest(void *base);
+struct pkvm_hyp_vcpu *init_selftest_vm(void *virt);
+void teardown_selftest_vm(void);
#else
static inline void pkvm_ownership_selftest(void *base) { }
#endif
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 13d2cb2f5fab..d8f8ebe59129 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -1648,53 +1648,18 @@ struct pkvm_expected_state {
static struct pkvm_expected_state selftest_state;
static struct hyp_page *selftest_page;
-
-static struct pkvm_hyp_vm selftest_vm = {
- .kvm = {
- .arch = {
- .mmu = {
- .arch = &selftest_vm.kvm.arch,
- .pgt = &selftest_vm.pgt,
- },
- },
- },
-};
-
-static struct pkvm_hyp_vcpu selftest_vcpu = {
- .vcpu = {
- .arch = {
- .hw_mmu = &selftest_vm.kvm.arch.mmu,
- },
- .kvm = &selftest_vm.kvm,
- },
-};
-
-static void init_selftest_vm(void *virt)
-{
- struct hyp_page *p = hyp_virt_to_page(virt);
- int i;
-
- selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
- WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt));
-
- for (i = 0; i < pkvm_selftest_pages(); i++) {
- if (p[i].refcount)
- continue;
- p[i].refcount = 1;
- hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
- }
-}
+static struct pkvm_hyp_vcpu *selftest_vcpu;
static u64 selftest_ipa(void)
{
- return BIT(selftest_vm.pgt.ia_bits - 1);
+ return BIT(selftest_vcpu->vcpu.arch.hw_mmu->pgt->ia_bits - 1);
}
static void assert_page_state(void)
{
void *virt = hyp_page_to_virt(selftest_page);
u64 size = PAGE_SIZE << selftest_page->order;
- struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
+ struct pkvm_hyp_vcpu *vcpu = selftest_vcpu;
u64 phys = hyp_virt_to_phys(virt);
u64 ipa[2] = { selftest_ipa(), selftest_ipa() + PAGE_SIZE };
struct pkvm_hyp_vm *vm;
@@ -1709,10 +1674,10 @@ static void assert_page_state(void)
WARN_ON(__hyp_check_page_state_range(phys, size, selftest_state.hyp));
hyp_unlock_component();
- guest_lock_component(&selftest_vm);
+ guest_lock_component(vm);
WARN_ON(__guest_check_page_state_range(vm, ipa[0], size, selftest_state.guest[0]));
WARN_ON(__guest_check_page_state_range(vm, ipa[1], size, selftest_state.guest[1]));
- guest_unlock_component(&selftest_vm);
+ guest_unlock_component(vm);
}
#define assert_transition_res(res, fn, ...) \
@@ -1725,14 +1690,15 @@ void pkvm_ownership_selftest(void *base)
{
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_RWX;
void *virt = hyp_alloc_pages(&host_s2_pool, 0);
- struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
- struct pkvm_hyp_vm *vm = &selftest_vm;
+ struct pkvm_hyp_vcpu *vcpu;
u64 phys, size, pfn, gfn;
+ struct pkvm_hyp_vm *vm;
WARN_ON(!virt);
selftest_page = hyp_virt_to_page(virt);
selftest_page->refcount = 0;
- init_selftest_vm(base);
+ selftest_vcpu = vcpu = init_selftest_vm(base);
+ vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
size = PAGE_SIZE << selftest_page->order;
phys = hyp_virt_to_phys(virt);
@@ -1856,6 +1822,7 @@ void pkvm_ownership_selftest(void *base)
selftest_state.hyp = PKVM_PAGE_OWNED;
assert_transition_res(0, __pkvm_host_donate_hyp, pfn, 1);
+ teardown_selftest_vm();
selftest_page->refcount = 1;
hyp_put_page(&host_s2_pool, virt);
}
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 6f3b94a37fe3..8b906217c4c3 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -733,6 +733,55 @@ void __pkvm_unreserve_vm(pkvm_handle_t handle)
hyp_spin_unlock(&vm_table_lock);
}
+#ifdef CONFIG_NVHE_EL2_DEBUG
+static struct pkvm_hyp_vm selftest_vm = {
+ .kvm = {
+ .arch = {
+ .mmu = {
+ .arch = &selftest_vm.kvm.arch,
+ .pgt = &selftest_vm.pgt,
+ },
+ },
+ },
+};
+
+static struct pkvm_hyp_vcpu selftest_vcpu = {
+ .vcpu = {
+ .arch = {
+ .hw_mmu = &selftest_vm.kvm.arch.mmu,
+ },
+ .kvm = &selftest_vm.kvm,
+ },
+};
+
+struct pkvm_hyp_vcpu *init_selftest_vm(void *virt)
+{
+ struct hyp_page *p = hyp_virt_to_page(virt);
+ int i;
+
+ selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
+ WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt));
+
+ for (i = 0; i < pkvm_selftest_pages(); i++) {
+ if (p[i].refcount)
+ continue;
+ p[i].refcount = 1;
+ hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
+ }
+
+ selftest_vm.kvm.arch.pkvm.handle = __pkvm_reserve_vm();
+ insert_vm_table_entry(selftest_vm.kvm.arch.pkvm.handle, &selftest_vm);
+ return &selftest_vcpu;
+}
+
+void teardown_selftest_vm(void)
+{
+ hyp_spin_lock(&vm_table_lock);
+ remove_vm_table_entry(selftest_vm.kvm.arch.pkvm.handle);
+ hyp_spin_unlock(&vm_table_lock);
+}
+#endif /* CONFIG_NVHE_EL2_DEBUG */
+
/*
* Initialize the hypervisor copy of the VM state using host-donated memory.
*
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 36/38] KVM: arm64: Extend pKVM page ownership selftests to cover guest hvcs
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
Now that the guest can share and unshare memory with the host using
hypercalls, extend the pKVM page ownership selftest to exercise these
new transitions.
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 30 +++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 0775a82ee4fe..28a471d1927c 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -1814,11 +1814,41 @@ void pkvm_ownership_selftest(void *base)
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
+ selftest_state.host = PKVM_PAGE_SHARED_BORROWED;
+ selftest_state.guest[0] = PKVM_PAGE_SHARED_OWNED;
+ assert_transition_res(0, __pkvm_guest_share_host, vcpu, gfn);
+ assert_transition_res(-EPERM, __pkvm_guest_share_host, vcpu, gfn);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn + 1, 1, vcpu, prot);
+ assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
+ assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
+ assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
+
+ selftest_state.host = PKVM_NOPAGE;
+ selftest_state.guest[0] = PKVM_PAGE_OWNED;
+ assert_transition_res(0, __pkvm_guest_unshare_host, vcpu, gfn);
+ assert_transition_res(-EPERM, __pkvm_guest_unshare_host, vcpu, gfn);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
+ assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn + 1, 1, vcpu, prot);
+ assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
+ assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
+ assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
+ assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
+
selftest_state.host = PKVM_PAGE_OWNED;
selftest_state.guest[0] = PKVM_POISON;
assert_transition_res(0, __pkvm_host_force_reclaim_page_guest, phys);
assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu);
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot);
+ assert_transition_res(-EHWPOISON, __pkvm_guest_share_host, vcpu, gfn);
+ assert_transition_res(-EHWPOISON, __pkvm_guest_unshare_host, vcpu, gfn);
selftest_state.host = PKVM_NOPAGE;
selftest_state.guest[1] = PKVM_PAGE_OWNED;
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH v5 38/38] drivers/virt: pkvm: Add Kconfig dependency on DMA_RESTRICTED_POOL
From: Will Deacon @ 2026-03-30 14:48 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Quentin Perret, Fuad Tabba, Vincent Donnefort, Mostafa Saleh,
Alexandru Elisei
In-Reply-To: <20260330144841.26181-1-will@kernel.org>
pKVM guests practically rely on CONFIG_DMA_RESTRICTED_POOL=y in order
to establish shared memory regions with the host for virtio buffers.
Make CONFIG_ARM_PKVM_GUEST depend on CONFIG_DMA_RESTRICTED_POOL to avoid
the inevitable segmentation faults experience if you have the former but
not the latter.
Reported-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Will Deacon <will@kernel.org>
---
drivers/virt/coco/pkvm-guest/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/virt/coco/pkvm-guest/Kconfig b/drivers/virt/coco/pkvm-guest/Kconfig
index d2f344f1f98f..928b8e1668cc 100644
--- a/drivers/virt/coco/pkvm-guest/Kconfig
+++ b/drivers/virt/coco/pkvm-guest/Kconfig
@@ -1,6 +1,6 @@
config ARM_PKVM_GUEST
bool "Arm pKVM protected guest driver"
- depends on ARM64
+ depends on ARM64 && DMA_RESTRICTED_POOL
help
Protected guests running under the pKVM hypervisor on arm64
are isolated from the host and must issue hypercalls to enable
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* [PATCH] arm64: dts: meson-gxl-p230: fix ethernet PHY interrupt number
From: Jun Yan @ 2026-03-30 14:51 UTC (permalink / raw)
To: linux-kernel, linux-amlogic, linux-arm-kernel, devicetree
Cc: robh, krzk+dt, conor+dt, neil.armstrong, khilman, jbrunet,
martin.blumenstingl, Jun Yan
Correct the interrupt number assigned to the Realtek PHY in the p230
following the same logic as commit 3106507e1004 ("ARM64: dts: meson-gxm:
fix q200 interrupt number"),as reported in [PATCH 0/2] Ethernet PHY
interrupt improvements [1].
[1] https://lore.kernel.org/all/20171202214037.17017-1-martin.blumenstingl@googlemail.com/
Fixes: b94d22d94ad2 ("ARM64: dts: meson-gx: add external PHY interrupt on some platforms")
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index 7dffeb5931c9..701de57ff0f3 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -84,7 +84,8 @@ external_phy: ethernet-phy@0 {
reset-gpios = <&gpio GPIOZ_14 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio_intc>;
- interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
+ /* MAC_INTR on GPIOZ_15 */
+ interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
eee-broken-1000t;
};
};
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2 0/3] media: imx-csi: cleanup media pipeline start
From: Frank Li @ 2026-03-30 14:38 UTC (permalink / raw)
To: Michael Tretter, Hans Verkuil, Steve Longerbeam, Philipp Zabel,
Mauro Carvalho Chehab, Fabio Estevam, Greg Kroah-Hartman,
Shawn Guo, Sascha Hauer, linux-media, imx, linux-arm-kernel,
Pengutronix Kernel Team, linux-staging
In-Reply-To: <aco5Ei1B4vJWKMMj@pengutronix.de>
On Mon, Mar 30, 2026 at 10:49:22AM +0200, Michael Tretter wrote:
> Hi Hans,
>
> On Fri, 23 Jan 2026 17:57:23 +0100, Michael Tretter wrote:
> > On Thu, 18 Dec 2025 10:23:48 +0100, Michael Tretter wrote:
> > > The imx media device currently assumes that there is only a single media
> > > pipeline. However, the media graph has multiple imx capture devices.
> > > These may be started separately on media pipelines if they don't cause
> > > conflicts in the media graph.
> > >
> > > Move the media pipeline from the media device to the capture devices to
> > > properly track and handle multiple media pipelines for the imx-csi.
> > > Refactor the code to start the media pipeline from the driver to help
> > > the reader.
> >
>
> Could you take a look and apply this series for the imx-media driver,
> too? The patches are already reviewed by Frank Li and Philipp Zabel.
Michael Tretter:
Sorry, I will take care imx's stage driver. I already sent pull-request,
which included your other two patches.
Do you have others patches, which need me take care. I am not sure if
there are chance to send 2nd pull request for 7.1.
Frank
>
> Michael
>
> >
> > >
> > > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> > > ---
> > > Changes in v2:
> > > - Improve code readability in Patch 2
> > > - Update commit message of Patch 3 as suggested by Frank Li
> > > - Link to v1: https://patch.msgid.link/20251107-media-imx-cleanup-v1-0-f82a693c28f4@pengutronix.de
> > >
> > > ---
> > > Michael Tretter (3):
> > > media: imx-csi: move media_pipeline to video device
> > > media: imx-csi: explicitly start media pipeline on pad 0
> > > media: imx-csi: use media_pad_is_streaming helper
> > >
> > > drivers/staging/media/imx/imx-media-capture.c | 8 ++++----
> > > drivers/staging/media/imx/imx-media-utils.c | 12 ++++++++----
> > > drivers/staging/media/imx/imx-media.h | 7 ++++---
> > > 3 files changed, 16 insertions(+), 11 deletions(-)
> > > ---
> > > base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> > > change-id: 20251107-media-imx-cleanup-9022d941ae44
^ permalink raw reply
* RE: [PATCH v3 1/3] dt-bindings: arm: hpe,gxp: Add HPE GSC platform compatible
From: Hawkins, Nick @ 2026-03-30 14:53 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: catalin.marinas@arm.com, will@kernel.org, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260328-chirpy-sturdy-emu-9eb0cb@quoll>
Hi Krzysztof Kozlowski,
> > maintainers:
> > - Nick Hawkins <nick.hawkins@hpe.com>
> > @@ -18,6 +18,11 @@ properties:
> > - enum:
> > - hpe,gxp-dl360gen10
> > - const: hpe,gxp
> > + - description: GSC Based Boards
> > + items:
> > + - enum:
> > + - hpe,gsc-dl340gen12
> > + - const: hpe,gsc
> What sort of ordering are you going to use in this file? Usual choice is
> the fallback compatible, but I see you have chosen something else. That
> is fine, just keep that else sorting in the future instead of adding to
> the end.
Thank you for the feedback, just to confirm are you looking for me
to do something like this?:
- description: GSC Based Boards
items:
- enum:
- hpe,gsc-dl340gen12
- const: hpe,gsc
- description: GXP Based Boards
items:
- enum:
- hpe,gxp-dl360gen10
- const: hpe,gxp
That way we have the newest first and going forward if there is
something newer we add it to the top of the list rather than
the bottom?
Thanks,
-Nick Hawkins
^ permalink raw reply
* Re: [PATCH v3] coresight: tpdm: add traceid_show for checking traceid
From: James Clark @ 2026-03-30 14:55 UTC (permalink / raw)
To: Jie Gan, Suzuki K Poulose
Cc: coresight, linux-arm-kernel, linux-kernel, Mike Leach, Leo Yan,
Alexander Shishkin, Tingwei Zhang
In-Reply-To: <20260325-add-traceid-show-for-tpdm-v3-1-0eb836d4ec30@oss.qualcomm.com>
On 25/03/2026 3:10 am, Jie Gan wrote:
> Save the trace ID in drvdata during TPDM enablement and expose it
> to userspace to support trace data parsing.
>
> The TPDM device’s trace ID corresponds to the trace ID allocated
> to the connected TPDA device.
>
> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
> ---
> Changes in v3:
> 1. Only allow user to read the traceid while the TPDM device is enabled.
> - Link to v2: https://lore.kernel.org/r/20260316-add-traceid-show-for-tpdm-v2-1-1dec2a67e4ed@oss.qualcomm.com
>
> Changes in V2:
> 1. Use sysfs_emit instead of sprintf.
> Link to V1 - https://lore.kernel.org/all/20260306-add-traceid-show-for-tpdm-v1-1-0658a8edb972@oss.qualcomm.com/
> ---
> drivers/hwtracing/coresight/coresight-tpdm.c | 34 +++++++++++++++++++++++++++-
> drivers/hwtracing/coresight/coresight-tpdm.h | 2 ++
> 2 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> index da77bdaad0a4..c8339b973bfc 100644
> --- a/drivers/hwtracing/coresight/coresight-tpdm.c
> +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> @@ -481,7 +481,7 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
>
> static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event,
> enum cs_mode mode,
> - __maybe_unused struct coresight_path *path)
> + struct coresight_path *path)
> {
> struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>
> @@ -497,6 +497,7 @@ static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event,
> }
>
> __tpdm_enable(drvdata);
> + drvdata->traceid = path->trace_id;
> drvdata->enable = true;
> spin_unlock(&drvdata->spinlock);
>
> @@ -693,6 +694,29 @@ static struct attribute_group tpdm_attr_grp = {
> .attrs = tpdm_attrs,
> };
>
> +static ssize_t traceid_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + unsigned long val;
> + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> + if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED)
> + return -EINVAL;
> +
> + val = drvdata->traceid;
You probably need to take the coresight_mutex here otherwise you could
still return an invalid or stale value despite checking the mode.
There might also be some value in it returning the last used trace ID
even if the mode isn't enabled anymore. Because you can still read out
of the sink after disabling, so it makes more sense for a script to read
it at that point rather than when it's enabled. Also, you probably don't
want to be doing other things in your script in the point between
enabling and disabling.
> + return sysfs_emit(buf, "%#lx\n", val);
> +}
> +static DEVICE_ATTR_RO(traceid);
> +
> +static struct attribute *traceid_attrs[] = {
> + &dev_attr_traceid.attr,
> + NULL,
> +};
> +
> +static struct attribute_group traceid_attr_grp = {
> + .attrs = traceid_attrs,
> +};
> +
> static ssize_t dsb_mode_show(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> @@ -1367,6 +1391,12 @@ static const struct attribute_group *tpdm_attr_grps[] = {
> &tpdm_cmb_patt_grp,
> &tpdm_cmb_msr_grp,
> &tpdm_mcmb_attr_grp,
> + &traceid_attr_grp,
> + NULL,
> +};
> +
> +static const struct attribute_group *static_tpdm_attr_grps[] = {
> + &traceid_attr_grp,
> NULL,
> };
>
> @@ -1425,6 +1455,8 @@ static int tpdm_probe(struct device *dev, struct resource *res)
> desc.access = CSDEV_ACCESS_IOMEM(base);
> if (res)
> desc.groups = tpdm_attr_grps;
> + else
> + desc.groups = static_tpdm_attr_grps;
> drvdata->csdev = coresight_register(&desc);
> if (IS_ERR(drvdata->csdev))
> return PTR_ERR(drvdata->csdev);
> diff --git a/drivers/hwtracing/coresight/coresight-tpdm.h b/drivers/hwtracing/coresight/coresight-tpdm.h
> index 2867f3ab8186..11da64e1ade8 100644
> --- a/drivers/hwtracing/coresight/coresight-tpdm.h
> +++ b/drivers/hwtracing/coresight/coresight-tpdm.h
> @@ -300,6 +300,7 @@ struct cmb_dataset {
> * @cmb Specifics associated to TPDM CMB.
> * @dsb_msr_num Number of MSR supported by DSB TPDM
> * @cmb_msr_num Number of MSR supported by CMB TPDM
> + * @traceid Trace ID of the path.
> */
>
> struct tpdm_drvdata {
> @@ -313,6 +314,7 @@ struct tpdm_drvdata {
> struct cmb_dataset *cmb;
> u32 dsb_msr_num;
> u32 cmb_msr_num;
> + u8 traceid;
> };
>
> /* Enumerate members of various datasets */
>
> ---
> base-commit: b84a0ebe421ca56995ff78b66307667b62b3a900
> change-id: 20260316-add-traceid-show-for-tpdm-88d040651f00
>
> Best regards,
^ permalink raw reply
* Re: [PATCH v3 0/7] arm64: dts: ti: k3-am62a7-sk: Split r5f memory region
From: Mathieu Poirier @ 2026-03-30 14:56 UTC (permalink / raw)
To: Markus Schneider-Pargmann (TI)
Cc: Nishanth Menon, Rob Herring, Conor Dooley, Vignesh Raghavendra,
Tero Kristo, Dhruva Gole, Akashdeep Kaur, Kevin Hilman,
Bjorn Andersson, linux-remoteproc, linux-kernel, Kendall Willis,
devicetree, Vishal Mahaveer, Sebin Francis, Krzysztof Kozlowski,
linux-arm-kernel
In-Reply-To: <20260318-topic-am62a-ioddr-dt-v6-19-v3-0-c41473cb23c3@baylibre.com>
On Wed, Mar 18, 2026 at 04:13:06PM +0100, Markus Schneider-Pargmann (TI) wrote:
> Hi,
>
> Split the firmware memory region in more specific parts so it is better
> described where which information is stored. Specifically the LPM metadata
> region is important as bootloader software like U-Boot has to know where
> that data is to be able to read that data and resume from RAM.
>
> IO+DDR is a deep sleep state in which a few pins are set to be sensitive
> for wakeup while the DDR is kept in self refresh. Everything else is
> powered off.
>
> The changes in this series were suggested as part of the IO+DDR u-boot series:
> https://lore.kernel.org/r/814c211f-a9eb-4311-bb84-165b1a69755f@ti.com
>
> There are currently no real users of the memory-region that is split in
> this series. The size of the memory-region in total stays the same.
> The new layout is derived from the software running on the r5f
> processor:
> https://github.com/TexasInstruments/mcupsdk-core-k3/blob/k3_main/examples/drivers/ipc/ipc_rpmsg_echo_linux/am62ax-sk/r5fss0-0_freertos/ti-arm-clang/linker.cmd#L172
> https://github.com/TexasInstruments/mcupsdk-core-k3/blob/k3_main/source/drivers/device_manager/sciclient.h#L459
>
> Additionally the two important devicetree nodes for resuming from IO+DDR
> have the bootph-pre-ram flag added as this data needs to be read before
> the RAM is in use.
>
> Best
> Markus
>
> Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
> ---
> Changes in v3:
> - Squash the enforcement of the memory-region-names requirement in the
> patch adding the memory-region-names, as suggested.
> - Link to v2: https://lore.kernel.org/r/20260312-topic-am62a-ioddr-dt-v6-19-v2-0-37cb7ceec658@baylibre.com
>
> Changes in v2:
> - Make memory-region-names required if memory-region is present
> - Fixup memory-region and memory-region-names conditions. Require either
> 2 or 6 regions for memory-region and memory-region-names
> - Reword and restructure the binding documentation for memory-region and
> memory-region-names
> - Add memory-region-names to all uses of memory-region
> - Link to v1: https://lore.kernel.org/r/20260303-topic-am62a-ioddr-dt-v6-19-v1-0-12fe72bb40d2@baylibre.com
>
> ---
> Markus Schneider-Pargmann (TI) (7):
> dt-bindings: remoteproc: k3-r5f: Split up memory regions
> dt-bindings: remoteproc: k3-r5f: Add memory-region-names
I have picked-up the above two. The .dtsi files need to go through the TI
tree.
Thanks,
Mathieu
> arm64: dts: ti: k3: Use memory-region-names for r5f
> arm64: dts: ti: k3-am62a7-sk: Split r5f memory region
> arm64: dts: ti: k3-am62p5-sk: Split r5f memory region
> arm64: dts: ti: k3-am62a7-sk: Add r5f nodes to pre-ram bootphase
> arm64: dts: ti: k3-am62p5-sk: Add r5f nodes to pre-ram bootphase
>
> .../bindings/remoteproc/ti,k3-r5f-rproc.yaml | 55 ++++++++++++++++++----
> .../arm64/boot/dts/ti/k3-am62-ti-ipc-firmware.dtsi | 1 +
> .../boot/dts/ti/k3-am62a-ti-ipc-firmware.dtsi | 2 +
> arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 42 ++++++++++++++++-
> .../boot/dts/ti/k3-am62p-ti-ipc-firmware.dtsi | 2 +
> arch/arm64/boot/dts/ti/k3-am62p5-sk.dts | 42 ++++++++++++++++-
> .../arm64/boot/dts/ti/k3-am64-ti-ipc-firmware.dtsi | 4 ++
> .../arm64/boot/dts/ti/k3-am65-ti-ipc-firmware.dtsi | 2 +
> .../boot/dts/ti/k3-j7200-ti-ipc-firmware.dtsi | 4 ++
> .../boot/dts/ti/k3-j721e-ti-ipc-firmware.dtsi | 6 +++
> .../boot/dts/ti/k3-j721s2-ti-ipc-firmware.dtsi | 6 +++
> .../boot/dts/ti/k3-j722s-ti-ipc-firmware.dtsi | 3 ++
> .../k3-j784s4-j742s2-ti-ipc-firmware-common.dtsi | 8 ++++
> 13 files changed, 163 insertions(+), 14 deletions(-)
> ---
> base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
> change-id: 20260210-topic-am62a-ioddr-dt-v6-19-0da7712081d7
>
> Best regards,
> --
> Markus Schneider-Pargmann (TI) <msp@baylibre.com>
>
^ permalink raw reply
* Re: [PATCH v3 1/3] dt-bindings: arm: hpe,gxp: Add HPE GSC platform compatible
From: Krzysztof Kozlowski @ 2026-03-30 14:57 UTC (permalink / raw)
To: Hawkins, Nick
Cc: catalin.marinas@arm.com, will@kernel.org, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <DM4PR84MB192724F4949F614E3965E60A8852A@DM4PR84MB1927.NAMPRD84.PROD.OUTLOOK.COM>
On 30/03/2026 16:53, Hawkins, Nick wrote:
> Hi Krzysztof Kozlowski,
>
>>> maintainers:
>>> - Nick Hawkins <nick.hawkins@hpe.com>
>>> @@ -18,6 +18,11 @@ properties:
>>> - enum:
>>> - hpe,gxp-dl360gen10
>>> - const: hpe,gxp
>>> + - description: GSC Based Boards
>>> + items:
>>> + - enum:
>>> + - hpe,gsc-dl340gen12
>>> + - const: hpe,gsc
>
>> What sort of ordering are you going to use in this file? Usual choice is
>> the fallback compatible, but I see you have chosen something else. That
>> is fine, just keep that else sorting in the future instead of adding to
>> the end.
>
> Thank you for the feedback, just to confirm are you looking for me
> to do something like this?:
>
> - description: GSC Based Boards
> items:
> - enum:
> - hpe,gsc-dl340gen12
> - const: hpe,gsc
> - description: GXP Based Boards
> items:
> - enum:
> - hpe,gxp-dl360gen10
> - const: hpe,gxp
Yes, that's the typical/usual way.
>
> That way we have the newest first and going forward if there is
Usual order is the alphanumerical.
> something newer we add it to the top of the list rather than
> the bottom?
If you want to order by date of release of the SoC, that's fine. I think
Allwinner does it. It's pretty non obvious because the chips are
upstreamed different time than released, so it is difficult for people
knot knowing the release dates. Just don't use the rule of "add at the
beginning" or "at the end".
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH] usb: core: avoid double use of 'usb3-phy'
From: Gabor Juhos @ 2026-03-30 15:02 UTC (permalink / raw)
To: Greg Kroah-Hartman, Stanley Chang
Cc: Andrew Lunn, Gregory Clement, Sebastian Hesselbarth,
Miquel Raynal, linux-usb, linux-kernel, linux-arm-kernel,
Gabor Juhos
Commit 53a2d95df836 ("usb: core: add phy notify connect and disconnect")
causes double use of the 'usb3-phy' in certain cases.
Since that commit, if a generic PHY named 'usb3-phy' is specified in
the device tree, that is getting added to the 'phy_roothub' list of the
secondary HCD by the usb_phy_roothub_alloc_usb3_phy() function. However,
that PHY is getting added also to the primary HCD's 'phy_roothub' list
by usb_phy_roothub_alloc() if there is no generic PHY specified with
'usb2-phy' name.
This causes that the usb_add_hcd() function executes each phy operations
twice on the 'usb3-phy'. Once when the primary HCD is added, then once
again when the secondary HCD is added.
The issue affects the Marvell Armada 3700 platform at least, where a
custom name is used for the USB2 PHY:
$ git grep 'phy-names.*usb3' arch/arm64/boot/dts/marvell/armada-37xx.dtsi | tr '\t' ' '
arch/arm64/boot/dts/marvell/armada-37xx.dtsi: phy-names = "usb3-phy", "usb2-utmi-otg-phy";
Extend the usb_phy_roothub_alloc_usb3_phy() function to skip adding the
'usb3-phy' to the 'phy_roothub' list of the secondary HCD when 'usb2-phy'
is not specified in the device tree to avoid the double use.
Fixes: 53a2d95df836 ("usb: core: add phy notify connect and disconnect")
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
---
drivers/usb/core/phy.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
index 4bba1c2757406a35bf19eb7984a2807212374d18..4d966cc9cdc9510147041df7875ad9e48a9ea3af 100644
--- a/drivers/usb/core/phy.c
+++ b/drivers/usb/core/phy.c
@@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc);
struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
{
struct usb_phy_roothub *phy_roothub;
- int num_phys;
+ int num_phys, usb2_phy_index;
if (!IS_ENABLED(CONFIG_GENERIC_PHY))
return NULL;
@@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
if (num_phys <= 0)
return NULL;
+ /*
+ * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added
+ * all PHYs to the primary HCD's phy_roothub already, so skip
+ * adding 'usb3-phy' here to avoid double use of that.
+ */
+ usb2_phy_index = of_property_match_string(dev->of_node, "phy-names",
+ "usb2-phy");
+ if (usb2_phy_index < 0)
+ return NULL;
+
phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL);
if (!phy_roothub)
return ERR_PTR(-ENOMEM);
---
base-commit: f50200dd44125e445a6164e88c217472fa79cdbc
change-id: 20260329-usb-avoid-usb3-phy-double-use-7e498373073c
Best regards,
--
Gabor Juhos <j4g8y7@gmail.com>
^ permalink raw reply related
* [PATCH 0/2] arm64: dts: marvell: armada-37xx: USB3 PHY cleanup
From: Gabor Juhos @ 2026-03-30 15:25 UTC (permalink / raw)
To: Gregory Clement, Andrew Lunn, Sebastian Hesselbarth, Robert Marko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Greg Kroah-Hartman, Stanley Chang
Cc: linux-arm-kernel, devicetree, linux-kernel, Gabor Juhos
There are two small patches in the series. The first helps to avoid
triggering a bug in the USB core code, whereas the second one is a
small cleanup to align PHY definitions of the USB3 node with other
platforms.
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
---
Gabor Juhos (2):
arm64: dts: marvell: armada-37xx: use 'usb2-phy' in USB3 controller's phy-names
arm64: dts: marvell: armada-37xx: swap PHYs' order in USB3 controller node
arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi | 2 +-
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
---
base-commit: 2ff6cc999a04bcb094b8cbba68a9251f03a5c876
change-id: 20260330-armada-37xx-usb3-phy-cleanup-922a5472794a
Best regards,
--
Gabor Juhos <j4g8y7@gmail.com>
^ permalink raw reply
* [PATCH 2/2] arm64: dts: marvell: armada-37xx: swap PHYs' order in USB3 controller node
From: Gabor Juhos @ 2026-03-30 15:25 UTC (permalink / raw)
To: Gregory Clement, Andrew Lunn, Sebastian Hesselbarth, Robert Marko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Greg Kroah-Hartman, Stanley Chang
Cc: linux-arm-kernel, devicetree, linux-kernel, Gabor Juhos
In-Reply-To: <20260330-armada-37xx-usb3-phy-cleanup-v1-0-34d77f1a1784@gmail.com>
It seems that the Armada 3700 is the only platform where the USB3 specific
PHY is defined before the USB2 specific one in the device tree:
$ git grep -E 'phy-names[ \t]*=[ \t]*"usb3-phy"[ \t]*,' next-20260327 -- *.dts *.dtsi | tr '\t' ' '
next-20260327:arch/arm64/boot/dts/marvell/armada-37xx.dtsi: phy-names = "usb3-phy", "usb2-utmi-otg-phy";
In contrary to this, there are 93 other platforms/boards where 'usb2-phy'
is defined first:
$ git grep -E 'phy-names[ \t]*=[ \t]*"usb2-phy"[ \t]*,' next-20260327 -- *.dts *.dtsi | wc -l
93
Swap the order of the USB3 and USB2 PHYs to follow the common pattern
used on other platforms.
No functional changes intended.
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
---
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index 7470d504a41081b32bee45368028189a13ea7087..360fc24fdde2204540ac415852146d3020be87c0 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -371,8 +371,8 @@ usb3: usb@58000 {
reg = <0x58000 0x4000>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sb_periph_clk 12>;
- phys = <&comphy0 0>, <&usb2_utmi_otg_phy>;
- phy-names = "usb3-phy", "usb2-phy";
+ phys = <&usb2_utmi_otg_phy>, <&comphy0 0>;
+ phy-names = "usb2-phy", "usb3-phy";
status = "disabled";
};
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] arm64: dts: marvell: armada-37xx: use 'usb2-phy' in USB3 controller's phy-names
From: Gabor Juhos @ 2026-03-30 15:25 UTC (permalink / raw)
To: Gregory Clement, Andrew Lunn, Sebastian Hesselbarth, Robert Marko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Greg Kroah-Hartman, Stanley Chang
Cc: linux-arm-kernel, devicetree, linux-kernel, Gabor Juhos
In-Reply-To: <20260330-armada-37xx-usb3-phy-cleanup-v1-0-34d77f1a1784@gmail.com>
Instead of the generic 'usb2-phy' name, the Armada 37xx device trees
are using a custom 'usb2-utmi-otg-phy' name for the USB2 PHY in the USB3
controller node. Since commit 53a2d95df836 ("usb: core: add phy notify
connect and disconnect"), this triggers a bug [1] in the USB core which
causes double use of the USB3 PHY.
Change the PHY name to 'usb2-phy' in the SoC and in the uDPU specific
dtsi files in order to avoid triggering the bug and also to keep the
names in line with the ones used by other platforms.
Link: https://lore.kernel.org/r/20260330-usb-avoid-usb3-phy-double-use-v1-1-d2113aecb535@gmail.com # [1]
Fixes: 53a2d95df836 ("usb: core: add phy notify connect and disconnect")
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
---
arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi | 2 +-
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi b/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi
index cd856c0aba71e6f6fd3db8fb386ad607912e7577..12deacb741ccbea684d905f246f26a4399fc8cd8 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi
@@ -161,7 +161,7 @@ ð1 {
&usb3 {
status = "okay";
phys = <&usb2_utmi_otg_phy>;
- phy-names = "usb2-utmi-otg-phy";
+ phy-names = "usb2-phy";
};
&uart0 {
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index 44c47409f8793ae1266303607812ef481edbfbc5..7470d504a41081b32bee45368028189a13ea7087 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -372,7 +372,7 @@ usb3: usb@58000 {
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sb_periph_clk 12>;
phys = <&comphy0 0>, <&usb2_utmi_otg_phy>;
- phy-names = "usb3-phy", "usb2-utmi-otg-phy";
+ phy-names = "usb3-phy", "usb2-phy";
status = "disabled";
};
--
2.53.0
^ permalink raw reply related
* [PATCH] KVM: arm64: Don't populate TPIDR_EL2 in finalise_el2()
From: Will Deacon @ 2026-03-30 15:29 UTC (permalink / raw)
To: kvmarm
Cc: linux-arm-kernel, Will Deacon, Oliver Upton, Marc Zyngier,
Catalin Marinas, Mark Rutland
When running with VHE, TPIDR_EL2 is only used for the percpu offset once
the ARM64_HAS_VIRT_HOST_EXTN capability has been detected, at which
point cpu_copy_el2regs() will populate its contents from TPIDR_EL1.
Remove the redundant initialisation of TPIDR_EL2 from finalise_el2().
Cc: Oliver Upton <oupton@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
---
I spotted this by inspection as part of an ill-fated (abandoned) attempt
at repurposing tpidr_elx for something else.
arch/arm64/kernel/hyp-stub.S | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S
index 085bc9972f6b..8b8614c0b9a5 100644
--- a/arch/arm64/kernel/hyp-stub.S
+++ b/arch/arm64/kernel/hyp-stub.S
@@ -105,11 +105,9 @@ SYM_CODE_START_LOCAL(__finalise_el2)
msr_hcr_el2 x0
isb
- // Use the EL1 allocated stack, per-cpu offset
+ // Use the EL1 allocated stack
mrs x0, sp_el1
mov sp, x0
- mrs x0, tpidr_el1
- msr tpidr_el2, x0
// FP configuration, vectors
mrs_s x0, SYS_CPACR_EL12
--
2.53.0.1018.g2bb0e51243-goog
^ permalink raw reply related
* Re: [PATCH v5 3/7] pinctrl: extract pinctrl_generic_to_map() from pinctrl_generic_pins_function_dt_node_to_map()
From: Conor Dooley @ 2026-03-30 15:33 UTC (permalink / raw)
To: Frank Li
Cc: Peter Rosin, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rafał Miłecki, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-kernel, linux-gpio,
devicetree, imx, linux-arm-kernel, Haibo Chen, Conor Dooley
In-Reply-To: <20260327-pinctrl-mux-v5-3-d4aec9d62c62@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 932 bytes --]
On Fri, Mar 27, 2026 at 05:34:00PM -0400, Frank Li wrote:
> Refactor pinctrl_generic_pins_function_dt_subnode_to_map() by separating DT
> parsing logic from map creation. Introduce a new helper
> pinctrl_generic_to_map() to handle mapping to kernel data structures, while
> keeping DT property parsing in the subnode function.
>
> Improve code structure and enables easier reuse for platforms using
> different DT properties (e.g. pinmux) without modifying the
> dt_node_to_map-style callback API. Avoid unnecessary coupling to
> pinctrl_generic_pins_function_dt_node_to_map(), which provides
> functionality not needed when the phandle target is unambiguous.
>
> Maximize code reuse and provide a cleaner extension point for future
> pinctrl drivers.
>
> Suggested-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH] media: nxp: imx8-isi: fix memory leaks in probe error paths and remove
From: Laurent Pinchart @ 2026-03-30 15:41 UTC (permalink / raw)
To: Frank Li
Cc: David Carlier, mchehab, s.hauer, festevam, jacopo, aisheng.dong,
guoniu.zhou, linux-media, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <acqM06YySijL1VpU@lizhi-Precision-Tower-5810>
On Mon, Mar 30, 2026 at 10:46:43AM -0400, Frank Li wrote:
> On Fri, Mar 27, 2026 at 10:27:11PM +0000, David Carlier wrote:
> > mxc_isi_probe() allocates isi->pipes with kzalloc_objs() but never
> > frees it on any probe failure path or in mxc_isi_remove(), leaking the
> > allocation on every failed probe and every normal unbind.
> >
> > Additionally, when mxc_isi_pipe_init() fails partway through the
> > channel loop or when mxc_isi_v4l2_init() fails, the already initialized
> > pipes are not cleaned up — their media entities and mutexes are leaked.
> >
> > Fix both by adding kfree(isi->pipes) to all probe error paths and to
> > mxc_isi_remove(), and cleaning up already-initialized pipes in the
> > err_xbar error path.
> >
> > Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISP Channel driver")
> > Signed-off-by: David Carlier <devnexen@gmail.com>
> > ---
>
> I think provide a helper function, devm_kzalloc_objs(), or using old
> devm_kzalloc is better fix method.
I quite agree. Kees said he's planning to introduce devm_kzalloc_objs().
I assume this will come in the next kernel version. I think we can wait
for that to fix this leak as it's a really minor issue.
> > .../platform/nxp/imx8-isi/imx8-isi-core.c | 24 +++++++++++++++----
> > 1 file changed, 19 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
> > index 4bf8570e1b9e..ab32c5b6ac9c 100644
> > --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
> > +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
> > @@ -490,33 +490,43 @@ static int mxc_isi_probe(struct platform_device *pdev)
> > return -ENOMEM;
> >
> > isi->num_clks = devm_clk_bulk_get_all(dev, &isi->clks);
> > - if (isi->num_clks < 0)
> > + if (isi->num_clks < 0) {
> > + kfree(isi->pipes);
> > return dev_err_probe(dev, isi->num_clks, "Failed to get clocks\n");
> > + }
> >
> >
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [PATCH V2 1/5] dmaengine: xilinx_dma: Fix MCDMA descriptor fields for MM2S vs S2MM
From: Frank Li @ 2026-03-30 15:46 UTC (permalink / raw)
To: Srinivas Neeli
Cc: Vinod Koul, git, Frank Li, Michal Simek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Suraj Gupta,
Radhey Shyam Pandey, Thomas Gessler, Folker Schwesinger,
Tomi Valkeinen, Kees Cook, Abin Joseph, dmaengine, devicetree,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260313062533.421249-2-srinivas.neeli@amd.com>
On Fri, Mar 13, 2026 at 11:55:29AM +0530, Srinivas Neeli wrote:
> The MCDMA BD format differs between MM2S and S2MM directions, but the
Can you use DMA_DEV_TO_MEM and DMA_MEM_TO_DEV instead of MM2S and S2MM?
or memory to slave, at least first place need extend term MM2S(memory to
slave).
> driver was using generic 'status' and 'sideband_status' fields for both.
> This could lead to incorrect residue calculations when the hardware
> updates direction-specific fields.
driver was using generic 'status' and 'sideband_status' fields for both,
which lead ... (use Affirmative Tone)
>
> Refactor the descriptor structure to use unions with direction-specific
> field names (mm2s_status/s2mm_status, etc.). This ensures the driver
Ensure .. (needn't this)
Frank
> accesses the correct hardware fields based on channel direction and
> matches the hardware documentation.
>
> Fixes: 6ccd692bfb7f ("dmaengine: xilinx_dma: Add Xilinx AXI MCDMA Engine driver support")
> Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
> ---
> drivers/dma/xilinx/xilinx_dma.c | 29 ++++++++++++++++++++++-------
> 1 file changed, 22 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
> index b53292e02448..4a83492f2435 100644
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -275,8 +275,10 @@ struct xilinx_axidma_desc_hw {
> * @buf_addr_msb: MSB of Buffer address @0x0C
> * @rsvd: Reserved field @0x10
> * @control: Control Information field @0x14
> - * @status: Status field @0x18
> - * @sideband_status: Status of sideband signals @0x1C
> + * @mm2s_ctrl_sideband: Sideband control info for mm2s @0x18
> + * @s2mm_status: Status field for s2mm @0x18
> + * @mm2s_status: Status field for mm2s @0x1C
> + * @s2mm_sideband_status: Sideband status for s2mm @0x1C
> * @app: APP Fields @0x20 - 0x30
> */
> struct xilinx_aximcdma_desc_hw {
> @@ -286,8 +288,14 @@ struct xilinx_aximcdma_desc_hw {
> u32 buf_addr_msb;
> u32 rsvd;
> u32 control;
> - u32 status;
> - u32 sideband_status;
> + union {
> + u32 mm2s_ctrl_sideband;
> + u32 s2mm_status;
> + };
> + union {
> + u32 mm2s_status;
> + u32 s2mm_sideband_status;
> + };
> u32 app[XILINX_DMA_NUM_APP_WORDS];
> } __aligned(64);
>
> @@ -1013,9 +1021,16 @@ static u32 xilinx_dma_get_residue(struct xilinx_dma_chan *chan,
> struct xilinx_aximcdma_tx_segment,
> node);
> aximcdma_hw = &aximcdma_seg->hw;
> - residue +=
> - (aximcdma_hw->control - aximcdma_hw->status) &
> - chan->xdev->max_buffer_len;
> + if (chan->direction == DMA_DEV_TO_MEM)
> + residue +=
> + (aximcdma_hw->control -
> + aximcdma_hw->s2mm_status) &
> + chan->xdev->max_buffer_len;
> + else
> + residue +=
> + (aximcdma_hw->control -
> + aximcdma_hw->mm2s_status) &
> + chan->xdev->max_buffer_len;
> }
> }
>
> --
> 2.43.0
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox