mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms
@ 2026-10-05  9:07 Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0 Suzuki K Poulose
                   ` (22 more replies)
  0 siblings, 23 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

This series is a trimmed down version of the Arm CCA KVM support, previously
posted here [0]. Like in the v17, we have tried to split the entire series
into the following chunks.

 1) Base RMM RMI support under drivers/firmware/arm_rmm -> [1]
 2) Linux Host support for handling GPFs - [2]
 3) NEW: Enlighten KVM arm64 about the different VM types and use call
    backs for the VM type, rather than spilling the is_this_type_of_vm()
    everywhere. Adds VCPU and Stage2 MMU related callbacks with support
    for the existing VM types. There are other places where we may be
    able to abstract, but those need careful performance evaluations
    to make sure they are fit (e.g., vcpu_run)

   With that in place we generalise the predicate "kvm_vm_is_protected()"
   to cover all  "Confidential" VMs (which includes Protected VM and Realms),
   allowing us to handle common themes without having to do things like :

     if (kvm_vm_is_protected() || kvm_vm_is_realm())

   Also replaces the code with precise check for a given VM type to
   avoiding combination of if (). e.g,, kvm_vm_is_unprotected_pkvm(kvm).
   The checks under arch/arm64/kvm/{nvhe,pkvm} still retain the vm_is_protected()
   check as pVMs are the only possible protected VMs there.
   
 4) Bare minimal Realm VM support without the actual functionality to
    run a Realm. This would help the maintainers to review the series in
    smaller chunks. This doesn't depend on [1] and can be independently
    merged, without being "functional".
    This series includes vcpu operations and the s2 vm operations, which
    do need the RMI driver backend to be meaningful. But the KVM handler
    is in the right shape. The remaining changes would be added once the
    RMI firmware library lands. Also covers the SET_ONE_REG/GET_ONE_REG

 5) Core implementation of the RMI driver for KVM and actual enablement of the
    Realm support. This depends on (1), (2) and the guest-memfd-in-place
    conversion series v12 from Ackerley. This is available here at the integration
    branch [3]


NOTE: There is a conflict with Fuad's patch to drop the WARN for unknown vm
ioctls, that is queued in ~fixes~ in v7.3-rc5. I have dropped the
WARN_ON_ONCE() to match the code after resolution.

  https://lore.kernel.org/all/20260914093838.1082637-1-fuad.tabba@linux.dev 

This series is comprised of (3) and (4) above.

The integration branch has been tested with the following components:

  tf-RMM:   main branch (commit 5e6e2acd) compliant to RMM-v2.0-beta3 [4]
  kvmtool: git@git.gitlab.arm.com:linux-arm/kvmtool-cca.git tag:cca-kvm-v20

[0] Arm CCA KVM Support v16 : https://lore.kernel.org/all/20260803134403.80630-1-steven.price@arm.com
[1] Linux firmware RMI v22 https://git.gitlab.arm.com/linux-arm/linux-cca/ cca/cca-host/fw_rmm/v22
[2] Linux GPF Host  https://git.gitlab.arm.com/linux-arm/linux-cca/ cca/cca-host/host-gpf/v19
[3] https://git.gitlab.arm.com/linux-arm/linux-cca/ cca/cca-host/kvm-v222integration
[4] https://support.arm.com/documentation/den0137/2-0bet3/

Changes since v21:
 https://lore.kernel.org/all/20261001210703.1597150-1-suzuki.poulose@arm.com
 - Keep the kvm_arch struct packed, by moving psci_version, closer to vm_flavor and also moving the vm_s2_ops, closer to vm_flavor.
 - Drop the kern_hyp_va() for vcpu_is_protected() in nVHE, instead ban nVHE
   code from using vcpu_is_protected() (by using BUILD_BUG_ON()) and convert
   all users to vcpu_is_protected_pkvm()
 - Drop kvm_vm_is_unprotected_pkvm() in favor of open coded check against VM_PKVM
 - Drop WARN_ON_ONCE() in the kvm_vm_ioctl_allowed() to match Fuad's fix merged in v7.3-rc5
 - Move '&' to the KVM_*_OPS macros
 - Nuke __unmap_stage2_range() and define per-VM callback for stage2_unmap_range, removing the KVM_PGT_FN() hack for the call, and also for the others
 - Drop NULL check for vm_s2_ops callbacks and always define everything.
 - Add no_age_gfn() which plugs in for pVMs and Realms for the vm_age_*gfn callbacks

Changes since v20:
 https://lore.kernel.org/all/20260924160504.853911-1-suzuki.poulose@arm.com

 - Allow KVM_ARM_CAP_DEVICE_CTRL for Realms to allow SMCCC filtering
 - New patch: Deny vcpu features not compatible with protected VMs
 - Remove suprious ":w" in commit description (Thanks Catalin)

Changes since v19:
 https://lore.kernel.org/all/20260920212845.707-1-suzuki.poulose@arm.com

 - Collect Reviews from Jonathan, and Tested-by from Gavin - Thank you both!
 - Add a new patch prepare the the vcpu_set_pauth_traps for the addition of
   VCPU callbacks. Could have merged with the existing one, but that becomes
   quite too much to follow.
 - Add another new preparatory patch to replace vcpu->kvm => new local variable
   kvm in kvm_handle_guest_abort(), making it also easier for abstracting memory
   abort handling.
 - Fix vcpu_is_protected for nvhe hyp code, by using kern_hyp_va() for vcpu->kvm
 - Use READ_ONCE() to read the host kvm->arch.vm_flavor in EL2 pKVM code
 - Move the replacement of !kvm_vm_is_protected() in kvm_arch_vcpu_put() with
   kvm_vm_is_unprotected_pkvm() to this patch
 - Drop ',' after the end marker in vm_flavor enums
 - Wrap kvm_arm_vmid_clear_active() in 'vcpu_put_mmu()' to compliment the
   vcpu_prepare_mmu and call it conditional on !pKVM.
 - Update the time loading constraints comment to clarify that it only applies
   for VHE.
 - Sort the vcpu ops by vm_flavor enum value
 - Add blank line in kvm_arch_flush_remote_tlbs()
 - Drop local variable ret and directly return from if...else for kvm_vm_mem_abort

Changes since v18:
  https://lore.kernel.org/all/20260915160141.3543048-1-suzuki.poulose@arm.com

 - Patch count down by 3, after merging different patches together, see more below
 - Retain NULL vm_offset for pVMs and move the counter offset flag initialisation
   to kvm_timer_init_vm() - Marc
 - Merge widening the scope of kvm_vm_is_protected() to the patch where the
   flavors are introduced(Marc) and also dropped Fuad's reviewed-by, as the
   patch is now bigger.
 - Merge "Use kvm_vm_is_unprotected_pkvm" for !kvm_vm_is_protected to patch
   where flavor is introuced.
 - Merge "vgic-v3" mandate and preventing vgic-v2 mappings to a single patch,
   where kvm_vm_hyp_is_distrusting() introduced - Fuad
 - Drop kvm_vm_hyp_is_pkvm(), reverting to is_protected_kvm_enabled()
 - Use is_protected_kvm_enabeld() for pKVM guest flavor checks.
 - s/PKVM/pKVM for commit descriptions too
 - Make sure the vm_mem_abort callback is !NULL at init time.
 - Bail out early for !pKVM && !Realm VMs in kvm_vm_ioctl_allowed(). Use
   kvm_vm_hyp_is_distrusting()
 - WARN_ON_ONCE(!kvm) for kvm_vm_ioctl_allowed() as it must be only called with
   a valid kvm instance and only from kvm_arch_vm_ioctl()
 - Rename kvm_arch_vm_{ext,ioctl}_allowed => kvm_vm_{ext,ioctl}_allowed - Fuad
 - Move kvm_realm_ext_allowed() to asm/kvm_rmi.h - Fuad
 - Don't expose PMCR_EL0 to the userspace until we support PMU


Changes since v17:
https://lore.kernel.org/all/20260908162223.1683432-1-suzuki.poulose@arm.com 

 - Add a patch to fix pKVM handling of SYS_CNTVCT/CNTPCT to override the counter
   offset (Patch1)
 - Restrict Realms to VGIC v3 only - New patch
 - Add kvm_vm_is_unprotected() to replace is_protected_kvm_enabled() &&
   !kvm_vm_is_protected() - New patch
 - Use macro to initialize the per-flavor vcpu, s2_vm ops
 - Add a wrapper to initialise vcpu and s2_vm ops with a BUILD_BUG_ON()
   for the array size checks against VM flavour types
 - Drop forward decalaration of the vcpu, s2_vm operations that spoiled the
   fun ;-)
 - Remove irrelevant comment about the order of timer loading for !VHE
 - Use the explicti kvm_call_hyp_nvhe for pKVM specific ops
 - Don't call nvhe_vcpu_put from pkvm_vcpu_put, open code them
 - Drop cpu argument for vcpu_load() callback. We set the cpu
   before the callbacks are invoked
 - Drop kvm_vm_is_confidential(), instead widen the scope of kvm_vm_is_protected()
   to cover pVMs and Realms. Add an explicit helper kvm_vm_is_protected_pkvm()
   for the cases where we need to check for a "pVM on pKVM"
 - Add kvm_vm_hyp_is_pkvm() for checking if the VM is running on pKVM.
   covers both unprotected and pvms. But really uses is_protected_kvm_enabled()
   under the hood
 - Add kvm_vm_hyp_is_distrusting() to cover pKVM guests (both protected and
   unprotected) and Realms. Use this for preventing the vgic v2 mapping into
   Stage2 for a guest
 - Drop superfluous !kvm check from kvm_vm_ioctl_enable_cap() - Sashiko
 - Drop KVM_CAP_CREATE_IRQCHIP, as we don't support VGIC_V2 for Realms
 - Filter out the vm_ioctls that are based on blocked cap.
 - Repurpose the pkvm plumbing for filtering the caps and ioctl to generic
   and plumb the Realm support in
 - s/PKVM/pKVM for the comments
 - Drop type argument for pkvm_init_host_vm and also drop protected variable,
   now that we have the vm_flavor to check.
 - Use kvm_vm_hyp_is_pkvm() to replace is_protected_kvm_enabled() with valid
   kvm instance
 - CCA: Merge the GET/SET REG handling patches into a single patch
 - CCA: Reword the commit description for SVE VL access handling
 - Reordered the patches to group the Realm realted to changes to the rear end

Jean-Philippe Brucker (2):
  KVM: arm64: CCA: Expose SVE VL register before VCPU finalization
  KVM: arm64: CCA: Control user register access for Realms

Steven Price (4):
  KVM: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h
  KVM: arm64: CCA: Introduce Realms
  KVM: arm64: CCA: WARN on injected undef exceptions
  KVM: arm64: CCA: Support timers in realm RECs

Suzuki K Poulose (17):
  KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0
  KVM: arm64: Disable Steal time accounting for protected guests
  KVM: arm64: Include kvm_emulate.h in kvm/arm_psci.h
  KVM: arm64: Track the type of VM in kvm_arch
  KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host
  KVM: arm64: Refactor the vcpu_load to allow for VM specific callbacks
  KVM: arm64: Add vcpu load/put call backs for flavors
  KVM: arm64: Prevent unsupported vcpu features for VM types
  KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range
  KVM: arm64: Add VM specific callback for S2 MMU operations
  KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort()
  KVM: arm64: Abstract out memory abort handling
  KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms
  KVM: arm64: CCA: Add a new mode for supporting Realm guests
  KVM: arm64: CCA: Add VCPU load/put for Realms
  KVM: arm64: CCA: Add bare minimal S2 operations for Realm
  KVM: arm64: CCA: Don't expose unsupported capabilities for realm
    guests

 .../admin-guide/kernel-parameters.txt         |   3 +
 arch/arm64/include/asm/kvm_emulate.h          |  16 +
 arch/arm64/include/asm/kvm_host.h             |  90 ++++-
 arch/arm64/include/asm/kvm_pgtable.h          |  10 +-
 arch/arm64/include/asm/kvm_pkvm.h             |  25 +-
 arch/arm64/include/asm/kvm_rmi.h              |  85 ++++
 arch/arm64/include/asm/virt.h                 |   1 +
 arch/arm64/kvm/Makefile                       |   2 +-
 arch/arm64/kvm/arch_timer.c                   |  34 +-
 arch/arm64/kvm/arm.c                          | 367 ++++++++++++++----
 arch/arm64/kvm/guest.c                        |  73 +++-
 arch/arm64/kvm/hyp/include/nvhe/pkvm.h        |   2 +-
 arch/arm64/kvm/hyp/nvhe/pkvm.c                |   6 +-
 arch/arm64/kvm/hyp/nvhe/switch.c              |   4 +-
 arch/arm64/kvm/hyp/nvhe/timer-sr.c            |   2 +-
 arch/arm64/kvm/hyp/pgtable.c                  |   1 +
 arch/arm64/kvm/hypercalls.c                   |   4 +-
 arch/arm64/kvm/inject_fault.c                 |   1 +
 arch/arm64/kvm/mmio.c                         |   1 +
 arch/arm64/kvm/mmu.c                          | 273 +++++++++----
 arch/arm64/kvm/pkvm.c                         |   6 +-
 arch/arm64/kvm/pvtime.c                       |  14 +-
 arch/arm64/kvm/rmi.c                          |  18 +
 arch/arm64/kvm/sys_regs.c                     |  28 +-
 arch/arm64/kvm/vgic/vgic-init.c               |   2 +
 include/kvm/arm_psci.h                        |   2 +
 26 files changed, 858 insertions(+), 212 deletions(-)
 create mode 100644 arch/arm64/include/asm/kvm_rmi.h
 create mode 100644 arch/arm64/kvm/rmi.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  0:02   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 02/23] KVM: arm64: Disable Steal time accounting for protected guests Suzuki K Poulose
                   ` (21 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Protected VMs doesn't allow setting offsets for virtual and physical
counters, as the offset is always fixed to 0. The VM ioctl is filtered
out based on the cap. However we don't prevent the userspace from trying
to write to the CNTVCT/CNTPCT registers. This would lead to KVM triggering
a WARN() in timer_set_offset() as the vm_offset pointer is set to NULL.

Fix this by always "fixing" the timer offsets to 0 and marking that the
timer offset is set in the kvm->arch.flags at KVM init time for protected
VMs. This prevents the access to the VM specific vm_offset at low cost.
A userspace writing to the CNT*CT_EL0 would observe success, without
any real effect. This is cleaner over spilling "*_is_protected()"
checks and "matches" what we really do in practise. i.e., always run
with "fixed counter offset of 0".

Reported by Sashiko

Link: https://lore.kernel.org/all/20260908164641.416911F00A3A@smtp.kernel.org
Fixes: f7d05ee84a6a ("KVM: arm64: Prevent host from managing timer offsets for protected VMs")
Suggested-by: Marc Zyngier <maz@kernel.org>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 Changes since v19:
  - Fix typos in commit description and explain why we choose the approach.
  - Improve comment in the code
 Changes since v18:
  - Retain NULL vm_offset for protected VMs to avoid host tampering with the
    offset.
  - Moved the flag setting into kvm_timer_init_vm(), where it should have been
    in the first place
---
 arch/arm64/kvm/arch_timer.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
index 6ac3321f4c575..a45845f4ae4ea 100644
--- a/arch/arm64/kvm/arch_timer.c
+++ b/arch/arm64/kvm/arch_timer.c
@@ -1110,8 +1110,7 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
 		timer_context_init(vcpu, i);
 
 	/* Synchronize offsets across timers of a VM if not already provided */
-	if (!vcpu_is_protected(vcpu) &&
-	    !test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
+	if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
 		timer_set_offset(vcpu_vtimer(vcpu), kvm_phys_timer_read());
 		timer_set_offset(vcpu_ptimer(vcpu), 0);
 	}
@@ -1133,6 +1132,15 @@ void kvm_timer_init_vm(struct kvm *kvm)
 	 */
 	for (int i = 0; i < NR_KVM_TIMERS; i++)
 		kvm->arch.timer_data.ppi[i] = get_vgic_ppi(kvm, default_ppi[i]);
+
+	/*
+	 * Protected VMs don't allow the userspace to set counter offsets,
+	 * either set via counter register writes or the dedicated ioctls.
+	 * Pretend the offset has already been set and rely on the default
+	 * offset being 0.
+	 */
+	if (kvm_vm_is_protected(kvm))
+		set_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &kvm->arch.flags);
 }
 
 void kvm_timer_cpu_up(void)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 02/23] KVM: arm64: Disable Steal time accounting for protected guests
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0 Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  0:03   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 03/23] KVM: arm64: Include kvm_emulate.h in kvm/arm_psci.h Suzuki K Poulose
                   ` (20 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose, Fuad Tabba

PVTIME support is advertised by KVM_CAP_STEAL_TIME, which doesn't take into
account the kvm instance. Even with that, a VMM could skip the CAP check
and proceed to configure the PVTIME as we don't do further check on the
DEVICE_CTRL. Tighten this up by passing the KVM instance around wherever
possible and catch things early.

Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 +-
 arch/arm64/kvm/arm.c              |  2 +-
 arch/arm64/kvm/pvtime.c           | 14 +++++++-------
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 27fe0cd5b2d7a..286489a69dff5 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1346,7 +1346,7 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
 gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu);
 void kvm_update_stolen_time(struct kvm_vcpu *vcpu);
 
-bool kvm_arm_pvtime_supported(void);
+bool kvm_arm_pvtime_supported(struct kvm *kvm);
 int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
 			    struct kvm_device_attr *attr);
 int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 8b080804bc90b..db36815630790 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -447,7 +447,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 		r = system_supports_mte();
 		break;
 	case KVM_CAP_STEAL_TIME:
-		r = kvm_arm_pvtime_supported();
+		r = kvm_arm_pvtime_supported(kvm);
 		break;
 	case KVM_CAP_ARM_EL1_32BIT:
 		r = cpus_have_final_cap(ARM64_HAS_32BIT_EL1);
diff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c
index 4ceabaa4c30bd..579e0a4720ad2 100644
--- a/arch/arm64/kvm/pvtime.c
+++ b/arch/arm64/kvm/pvtime.c
@@ -67,9 +67,9 @@ gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)
 	return base;
 }
 
-bool kvm_arm_pvtime_supported(void)
+bool kvm_arm_pvtime_supported(struct kvm *kvm)
 {
-	return !!sched_info_on();
+	return !!sched_info_on() && (!kvm || !kvm_vm_is_protected(kvm));
 }
 
 int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
@@ -81,8 +81,8 @@ int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
 	int ret = 0;
 	int idx;
 
-	if (!kvm_arm_pvtime_supported() ||
-	    attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
+	if (!kvm_arm_pvtime_supported(kvm) ||
+	    (attr->attr != KVM_ARM_VCPU_PVTIME_IPA))
 		return -ENXIO;
 
 	if (get_user(ipa, user))
@@ -110,8 +110,8 @@ int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
 	u64 __user *user = (u64 __user *)attr->addr;
 	u64 ipa;
 
-	if (!kvm_arm_pvtime_supported() ||
-	    attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
+	if (!kvm_arm_pvtime_supported(vcpu->kvm) ||
+	    (attr->attr != KVM_ARM_VCPU_PVTIME_IPA))
 		return -ENXIO;
 
 	ipa = vcpu->arch.steal.base;
@@ -126,7 +126,7 @@ int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
 {
 	switch (attr->attr) {
 	case KVM_ARM_VCPU_PVTIME_IPA:
-		if (kvm_arm_pvtime_supported())
+		if (kvm_arm_pvtime_supported(vcpu->kvm))
 			return 0;
 	}
 	return -ENXIO;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 03/23] KVM: arm64: Include kvm_emulate.h in kvm/arm_psci.h
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0 Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 02/23] KVM: arm64: Disable Steal time accounting for protected guests Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 04/23] KVM: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h Suzuki K Poulose
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose, Fuad Tabba

Fix a potential build error (like below, when asm/kvm_emulate.h gets
included after the kvm/arm_psci.h) by including the missing header file
in kvm/arm_psci.h:

./include/kvm/arm_psci.h: In function ‘kvm_psci_version’:
./include/kvm/arm_psci.h:29:13: error: implicit declaration of function
   ‘vcpu_has_feature’; did you mean ‘cpu_have_feature’? [-Werror=implicit-function-declaration]
   29 |         if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PSCI_0_2)) {
	         |             ^~~~~~~~~~~~~~~~
			       |             cpu_have_feature

Reviewed-by: Gavin Shan <gshan@redhat.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 include/kvm/arm_psci.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index f86a006d67136..06c20612e9e7d 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -10,6 +10,8 @@
 #include <linux/kvm_host.h>
 #include <uapi/linux/psci.h>
 
+#include <asm/kvm_emulate.h>
+
 #define KVM_ARM_PSCI_0_1	PSCI_VERSION(0, 1)
 #define KVM_ARM_PSCI_0_2	PSCI_VERSION(0, 2)
 #define KVM_ARM_PSCI_1_0	PSCI_VERSION(1, 0)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 04/23] KVM: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (2 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 03/23] KVM: arm64: Include kvm_emulate.h in kvm/arm_psci.h Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch Suzuki K Poulose
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Fuad Tabba, Suzuki K Poulose

From: Steven Price <steven.price@arm.com>

To avoid future include cycles, drop the linux/kvm_host.h include in
kvm_pgtable.h and include the lightweight headers required for the types
and inline helpers used there. Additionally provide a forward
declaration for struct kvm_s2_mmu as it's only used as a pointer in this
file.

Both pgtable.c and kvm_pkvm.h relied on the indirect inclusion of
kvm_host.h, so make that explicit.

Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v19:
 - Include asm/memory.h, asm/page.h , linux/bitfield.h in asm/kvm_pgtable.h
   (Sashiko)
---
 arch/arm64/include/asm/kvm_pgtable.h | 10 +++++++++-
 arch/arm64/include/asm/kvm_pkvm.h    |  2 +-
 arch/arm64/kvm/hyp/pgtable.c         |  1 +
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
index 41a8687938eb6..a211a5d87bf96 100644
--- a/arch/arm64/include/asm/kvm_pgtable.h
+++ b/arch/arm64/include/asm/kvm_pgtable.h
@@ -7,10 +7,18 @@
 #ifndef __ARM64_KVM_PGTABLE_H__
 #define __ARM64_KVM_PGTABLE_H__
 
+#include <linux/bitfield.h>
 #include <linux/bits.h>
-#include <linux/kvm_host.h>
+#include <linux/kvm_types.h>
+#include <linux/rbtree_types.h>
+#include <linux/rcupdate.h>
 #include <linux/types.h>
 
+#include <asm/memory.h>
+#include <asm/page.h>
+
+struct kvm_s2_mmu;
+
 #define KVM_PGTABLE_FIRST_LEVEL		-1
 #define KVM_PGTABLE_LAST_LEVEL		3
 
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index beea00e693a0a..54a618d887fa4 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -7,9 +7,9 @@
 #define __ARM64_KVM_PKVM_H__
 
 #include <linux/arm_ffa.h>
+#include <linux/kvm_host.h>
 #include <linux/memblock.h>
 #include <linux/scatterlist.h>
-#include <asm/kvm_host.h>
 #include <asm/kvm_pgtable.h>
 
 /* Maximum number of VMs that can co-exist under pKVM. */
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index b74dd5ce1efd3..f48253b9d88b5 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -8,6 +8,7 @@
  */
 
 #include <linux/bitfield.h>
+#include <linux/kvm_host.h>
 #include <asm/kvm_pgtable.h>
 #include <asm/stage2_pgtable.h>
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (3 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 04/23] KVM: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:55   ` Gavin Shan
  2026-10-06  8:33   ` Marc Zyngier
  2026-10-05  9:07 ` [PATCH v22 06/23] KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host Suzuki K Poulose
                   ` (17 subsequent siblings)
  22 siblings, 2 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

KVM arm64 has different types of VMs with all the different modes in which
the hypervisor code can be run. e.g., VHE, nVHE, pKVM etc. Then there is
protected VM and normal VMs with pKVM. We might soon add other types,
e.g., Arm CCA Realm. So in an effort to make the handling of these
different types of VMs a bit more friendly to the eyes, add a VM flavor to
the kvm_arch and we could then add handlers for different operations based
on the VM type.

Keep the flavor initialisation at the beginning to allow for the detection
early enough and fail out on any unsupported requests.

With that, add wrappers for checking the "type" of a VM and replace the
existing users with the new wrappers.

Given we already have the construct of "kvm_vm_is_protected" in the core
KVM code, use that for all confidential compute guests including Realms
that we are about to add. Adds __VM_PROTECTED marker vm flavor to draw the
boundary for "protected VMs". In later patches, we would add Realm VMs,
 which would also be classified as protected.

Add a explicit helper to detect if a given VM is a "protected" VM under pKVM.
Change the existing users that precisely want to check the VM type. These
include :
  - kvm_arch_prepare_memory_region - For preventing memslot changes after
    pVM creation.

All the others are retained as a wider check for confidential guest VMs.
These are:
 - kvm_vm_ioctl_set_counter_offset - For disallowing timer offset
   configuration
 - io_mem_abort for dabt handling without valid syndrome information

Both of which are true for Realms too.

Realms support is restricted to VHE host and thus "kvm_vm_is_protected()"
checks in the pkvm hyp specific code doesn't need to change, as the only
protected guests it deals with is "protected pKVM" guests. To tighten this
init_pkvm_hyp_vm() restricts the hyp copy of the vm_flavor to the ones it
supports.

vcpu_is_protected() usage from nVHE hyp code is tricky, as we need to
convert the vcpu->kvm to the HYP VA before checking the flavor. This
involves kern_hyp_va() usage in asm/kvm_host.h. To avoid build breaks,
include asm/kvm_mmu.h to arm64/kvm/mmio.c.

While at it move the psci_version around to keep the structure packed.

Suggested-by: Marc Zyngier <maz@kernel.org>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v21:
 - Drop kern_hyp_va() and restrict nvhe code to always use vcpu_is_protected_pkvm()
 - Drop kvm_vm_is_unprotected_pkvm() and open code the check
 - Move psci_version field in kvm_arch around to keep the structure packed
---
 arch/arm64/include/asm/kvm_host.h      | 42 +++++++++++++++++++++++---
 arch/arm64/include/asm/kvm_pkvm.h      |  4 +--
 arch/arm64/kvm/arm.c                   | 33 ++++++++++++++++----
 arch/arm64/kvm/hyp/include/nvhe/pkvm.h |  2 +-
 arch/arm64/kvm/hyp/nvhe/pkvm.c         |  6 +++-
 arch/arm64/kvm/hyp/nvhe/switch.c       |  4 +--
 arch/arm64/kvm/hyp/nvhe/timer-sr.c     |  2 +-
 arch/arm64/kvm/mmio.c                  |  1 +
 arch/arm64/kvm/mmu.c                   |  2 +-
 arch/arm64/kvm/pkvm.c                  |  6 ++--
 10 files changed, 79 insertions(+), 23 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 286489a69dff5..dedb5df15a803 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -257,7 +257,6 @@ struct kvm_protected_vm {
 	pkvm_handle_t handle;
 	struct kvm_hyp_memcache teardown_mc;
 	struct kvm_hyp_memcache stage2_teardown_mc;
-	bool is_protected;
 	bool is_created;
 
 	/*
@@ -306,9 +305,22 @@ enum fgt_group_id {
 	__NR_FGT_GROUP_IDS__
 };
 
+enum kvm_arm_vm_flavor {
+	VM_NVHE,
+	VM_VHE,
+	VM_PKVM,		/* Normal guests on pKVM */
+	MARKER(__VM_PROTECTED),
+	VM_PROTECTED_PKVM,	/* Protected VM */
+	VM_FLAVOR_MAX
+};
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
+	enum kvm_arm_vm_flavor vm_flavor;
+	/* Mandated version of PSCI */
+	u32 psci_version;
+
 	/*
 	 * Fine-Grained UNDEF, mimicking the FGT layout defined by the
 	 * architecture. We track them globally, as we present the
@@ -332,9 +344,6 @@ struct kvm_arch {
 	/* Timers */
 	struct arch_timer_vm_data timer_data;
 
-	/* Mandated version of PSCI */
-	u32 psci_version;
-
 	/* Protects VM-scoped configuration data */
 	struct mutex config_lock;
 
@@ -1504,9 +1513,32 @@ struct kvm *kvm_arch_alloc_vm(void);
 
 #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE
 
-#define kvm_vm_is_protected(kvm)	(is_protected_kvm_enabled() && (kvm)->arch.pkvm.is_protected)
+#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor >= __VM_PROTECTED)
 
+#ifdef __KVM_NVHE_HYPERVISOR__
+/*
+ * Accessing vcpu->kvm from nVHE hyp stub is tricky, as we need to convert the
+ * pointer to the hyp VA. With pKVM, the nVHE code runs with the hyp_vcpu,
+ * which is populated correctly. Always vcpu_is_protected_pkvm(), which is
+ * gated on is_protected_kvm_enabled().
+ */
+#define vcpu_is_protected(vcpu)		BUILD_BUG_ON(1)
+#else
 #define vcpu_is_protected(vcpu)		kvm_vm_is_protected((vcpu)->kvm)
+#endif
+
+#define kvm_vm_is_protected_pkvm(kvm)					\
+	(is_protected_kvm_enabled() && ((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM))
+/*
+ * Rely on is_protected_kvm_enabled() check in kvm_vm_is_protected_pkvm() to
+ * make sure the vcpu->kvm is always valid VA in the context
+ */
+#define vcpu_is_protected_pkvm(vcpu)					\
+	({								\
+		struct kvm *__kvm = READ_ONCE((vcpu)->kvm);		\
+									\
+		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
+	})
 
 int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);
 bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 54a618d887fa4..2addc37c500e1 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, unsigned long type);
+int pkvm_init_host_vm(struct kvm *kvm);
 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);
@@ -49,7 +49,7 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
 	case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
 		return false;
 	default:
-		return !kvm || !kvm_vm_is_protected(kvm);
+		return !kvm || (kvm->arch.vm_flavor == VM_PKVM);
 	}
 }
 
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index db36815630790..bcec14c587119 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -214,6 +214,26 @@ static int kvm_arm_default_max_vcpus(void)
 	return vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
 }
 
+static int kvm_init_vm_flavor(struct kvm *kvm, unsigned long type)
+{
+	bool protected = type & KVM_VM_TYPE_ARM_PROTECTED;
+
+	if (is_protected_kvm_enabled()) {
+		if (protected)
+			kvm->arch.vm_flavor = VM_PROTECTED_PKVM;
+		else
+			kvm->arch.vm_flavor = VM_PKVM;
+	} else if (protected) {
+		return -EINVAL;
+	} else if (has_vhe()) {
+		kvm->arch.vm_flavor = VM_VHE;
+	} else {
+		kvm->arch.vm_flavor = VM_NVHE;
+	}
+
+	return 0;
+}
+
 /**
  * kvm_arch_init_vm - initializes a VM data structure
  * @kvm:	pointer to the KVM struct
@@ -236,6 +256,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	mutex_unlock(&kvm->lock);
 #endif
 
+	ret = kvm_init_vm_flavor(kvm, type);
+	if (ret)
+		return ret;
+
 	kvm_init_nested(kvm);
 
 	ret = kvm_share_hyp(kvm, kvm + 1);
@@ -257,12 +281,9 @@ 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, type);
+		ret = pkvm_init_host_vm(kvm);
 		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);
@@ -751,7 +772,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 		kvm_call_hyp_nvhe(__pkvm_vcpu_put);
 
 		/* __pkvm_vcpu_put implies a sync of the state */
-		if (!kvm_vm_is_protected(vcpu->kvm))
+		if (vcpu->kvm->arch.vm_flavor == VM_PKVM)
 			vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
 	}
 
@@ -985,7 +1006,7 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
 
 	if (is_protected_kvm_enabled()) {
 		/* Start with the vcpu in a dirty state */
-		if (!kvm_vm_is_protected(vcpu->kvm))
+		if (vcpu->kvm->arch.vm_flavor == VM_PKVM)
 			vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
 		ret = pkvm_create_hyp_vm(kvm);
 		if (ret)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index c904647d2f760..0acd78f4bc223 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -57,7 +57,7 @@ pkvm_hyp_vcpu_to_hyp_vm(struct pkvm_hyp_vcpu *hyp_vcpu)
 
 static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu)
 {
-	return vcpu_is_protected(&hyp_vcpu->vcpu);
+	return vcpu_is_protected_pkvm(&hyp_vcpu->vcpu);
 }
 
 static inline bool pkvm_hyp_vm_is_protected(struct pkvm_hyp_vm *hyp_vm)
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 459bd9eb7e4bc..ed51762aa4b5d 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -432,7 +432,11 @@ static void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm,
 
 	hyp_vm->host_kvm = host_kvm;
 	hyp_vm->kvm.created_vcpus = nr_vcpus;
-	hyp_vm->kvm.arch.pkvm.is_protected = READ_ONCE(host_kvm->arch.pkvm.is_protected);
+	if (READ_ONCE(host_kvm->arch.vm_flavor) == VM_PROTECTED_PKVM)
+		hyp_vm->kvm.arch.vm_flavor = VM_PROTECTED_PKVM;
+	else
+		hyp_vm->kvm.arch.vm_flavor = VM_PKVM;
+
 	hyp_vm->kvm.arch.flags = 0;
 	pkvm_init_features_from_host(hyp_vm, host_kvm);
 
diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c
index 7318e3e6a5f36..2a3e6d83682d5 100644
--- a/arch/arm64/kvm/hyp/nvhe/switch.c
+++ b/arch/arm64/kvm/hyp/nvhe/switch.c
@@ -217,7 +217,7 @@ static const exit_handler_fn pvm_exit_handlers[] = {
 
 static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
 {
-	if (unlikely(vcpu_is_protected(vcpu)))
+	if (unlikely(vcpu_is_protected_pkvm(vcpu)))
 		return pvm_exit_handlers;
 
 	return hyp_exit_handlers;
@@ -238,7 +238,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
 	 * it.  The check below is based on the one in
 	 * kvm_arch_vcpu_ioctl_run().
 	 */
-	if (unlikely(vcpu_is_protected(vcpu) && vcpu_mode_is_32bit(vcpu))) {
+	if (unlikely(vcpu_is_protected_pkvm(vcpu) && vcpu_mode_is_32bit(vcpu))) {
 		/*
 		 * As we have caught the guest red-handed, decide that it isn't
 		 * fit for purpose anymore by making the vcpu invalid. The VMM
diff --git a/arch/arm64/kvm/hyp/nvhe/timer-sr.c b/arch/arm64/kvm/hyp/nvhe/timer-sr.c
index 9930657169133..17bd1ff621ab6 100644
--- a/arch/arm64/kvm/hyp/nvhe/timer-sr.c
+++ b/arch/arm64/kvm/hyp/nvhe/timer-sr.c
@@ -48,7 +48,7 @@ void __timer_enable_traps(struct kvm_vcpu *vcpu)
 	 * or running a protected VM (we don't offset anything in this case).
 	 */
 	clr = CNTHCTL_EL1PCEN;
-	if (vcpu_is_protected(vcpu) ||
+	if (vcpu_is_protected_pkvm(vcpu) ||
 	    !timer_get_offset(vcpu_ptimer(vcpu)))
 		set |= CNTHCTL_EL1PCTEN;
 	else
diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c
index d1c3a352d5a22..ab1d2fef9a522 100644
--- a/arch/arm64/kvm/mmio.c
+++ b/arch/arm64/kvm/mmio.c
@@ -6,6 +6,7 @@
 
 #include <linux/kvm_host.h>
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_mmu.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 9ba86450fe4af..0f4e8b71fa85d 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2624,7 +2624,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
 	hva_t hva, reg_end;
 	int ret = 0;
 
-	if (kvm_vm_is_protected(kvm)) {
+	if (kvm_vm_is_protected_pkvm(kvm)) {
 		/* Cannot modify memslots once a pVM has run. */
 		if (pkvm_hyp_vm_is_created(kvm) &&
 		    (change == KVM_MR_DELETE || change == KVM_MR_MOVE)) {
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 8e4c6e4bec123..8e9176a700926 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -229,10 +229,9 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm)
 	mutex_unlock(&kvm->arch.config_lock);
 }
 
-int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)
+int pkvm_init_host_vm(struct kvm *kvm)
 {
 	int ret;
-	bool protected = type & KVM_VM_TYPE_ARM_PROTECTED;
 
 	/* Reserve the VM in hyp and obtain a hyp handle for the VM. */
 	ret = kvm_call_hyp_nvhe(__pkvm_reserve_vm);
@@ -240,8 +239,7 @@ int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)
 		return ret;
 
 	kvm->arch.pkvm.handle = ret;
-	kvm->arch.pkvm.is_protected = protected;
-	if (protected) {
+	if (kvm_vm_is_protected(kvm)) {
 		pr_warn_once("kvm: protected VMs are experimental and for development only, tainting kernel\n");
 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
 	}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 06/23] KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (4 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  0:29   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 07/23] KVM: arm64: Refactor the vcpu_load to allow for VM specific callbacks Suzuki K Poulose
                   ` (16 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

vcpu_set_pauth_traps() bails out and does nothing for pKVM hosts.
Clean this up by moving the is_protected_kvm_enabled() check to the
caller, in preparation for adding VM specific vcpu load/put callbacks.

While at it, do an early return if the vcpu doesn't have ptrauth.

No functional changes

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v19:
 - New patch, since addition of this to the Refactoring patch makes it a bit
   more bigger and harder to review
---
 arch/arm64/kvm/arm.c | 52 +++++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 25 deletions(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index bcec14c587119..50b84065ead1a 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -631,33 +631,34 @@ void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu)
 
 static void vcpu_set_pauth_traps(struct kvm_vcpu *vcpu)
 {
-	if (vcpu_has_ptrauth(vcpu) && !is_protected_kvm_enabled()) {
-		/*
-		 * Either we're running an L2 guest, and the API/APK bits come
-		 * from L1's HCR_EL2, or API/APK are both set.
-		 */
-		if (unlikely(is_nested_ctxt(vcpu))) {
-			u64 val;
+	if (!vcpu_has_ptrauth(vcpu))
+		return;
 
-			val = __vcpu_sys_reg(vcpu, HCR_EL2);
-			val &= (HCR_API | HCR_APK);
-			vcpu->arch.hcr_el2 &= ~(HCR_API | HCR_APK);
-			vcpu->arch.hcr_el2 |= val;
-		} else {
-			vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK);
-		}
+	/*
+	 * Either we're running an L2 guest, and the API/APK bits come
+	 * from L1's HCR_EL2, or API/APK are both set.
+	 */
+	if (unlikely(is_nested_ctxt(vcpu))) {
+		u64 val;
 
-		/*
-		 * Save the host keys if there is any chance for the guest
-		 * to use pauth, as the entry code will reload the guest
-		 * keys in that case.
-		 */
-		if (vcpu->arch.hcr_el2 & (HCR_API | HCR_APK)) {
-			struct kvm_cpu_context *ctxt;
+		val = __vcpu_sys_reg(vcpu, HCR_EL2);
+		val &= (HCR_API | HCR_APK);
+		vcpu->arch.hcr_el2 &= ~(HCR_API | HCR_APK);
+		vcpu->arch.hcr_el2 |= val;
+	} else {
+		vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK);
+	}
 
-			ctxt = this_cpu_ptr_hyp_sym(kvm_hyp_ctxt);
-			ptrauth_save_keys(ctxt);
-		}
+	/*
+	 * Save the host keys if there is any chance for the guest
+	 * to use pauth, as the entry code will reload the guest
+	 * keys in that case.
+	 */
+	if (vcpu->arch.hcr_el2 & (HCR_API | HCR_APK)) {
+		struct kvm_cpu_context *ctxt;
+
+		ctxt = this_cpu_ptr_hyp_sym(kvm_hyp_ctxt);
+		ptrauth_save_keys(ctxt);
 	}
 }
 
@@ -749,7 +750,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	else
 		vcpu->arch.hcr_el2 |= HCR_TWI;
 
-	vcpu_set_pauth_traps(vcpu);
+	if (!is_protected_kvm_enabled())
+		vcpu_set_pauth_traps(vcpu);
 
 	if (is_protected_kvm_enabled()) {
 		kvm_call_hyp_nvhe(__pkvm_vcpu_load,
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 07/23] KVM: arm64: Refactor the vcpu_load to allow for VM specific callbacks
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (5 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 06/23] KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 08/23] KVM: arm64: Add vcpu load/put call backs for flavors Suzuki K Poulose
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

To keep the VCPU load/put handling cleaner with the different kinds of VM
types, we are about to introduce VM specific callbacks to do just the right
thing. In preparation for that, make some refactoring to add the change
easier by mainly feature specific configurations to individual wrappers,
so that different callbacks could reuse the helpers.

Adds vcpu_prepare_mmu()/vcpu_put_mmu() wrappers to load/put MMU related
configurations for !pKVM guests. Additionally makes it explicit that
kvm_arm_vmid_clear_active() is not required for pKVM host.

Add vcpu_load_pvtime(), vcpu_set_wfx_traps() wrappers for handling the
corresponding configurations.

While at it, also make it clear that the timer loading constraints only
apply for the VHE.

No functional changes intended. Based on a work by Marc Zyngier.

Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v19:
 - Add vcpu_put_mmu() to pair with the vcpu_prepare_mmu() and make the
   call conditional on !is_protected_kvm_enabled(). Preparing the path
   for per-flavor callbacks
 - Update the timer load constraints comment to reflect that it only applies
   for VHE
 - Wrap kvm_arm_vmid_clear_active() to vcpu_put_mmu() to compliement
   with the vcpu_prepare_mmu(). Also only clear the VMID for non-pKVM
   guests
---
 arch/arm64/kvm/arm.c | 59 ++++++++++++++++++++++++++++++--------------
 1 file changed, 40 insertions(+), 19 deletions(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 50b84065ead1a..24800809b4a96 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -684,14 +684,11 @@ static bool kvm_vcpu_should_clear_twe(struct kvm_vcpu *vcpu)
 	return single_task_running();
 }
 
-void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void vcpu_prepare_mmu(struct kvm_vcpu *vcpu)
 {
 	struct kvm_s2_mmu *mmu;
 	int *last_ran;
 
-	if (is_protected_kvm_enabled())
-		goto nommu;
-
 	if (vcpu_has_nv(vcpu))
 		kvm_vcpu_load_hw_mmu(vcpu);
 
@@ -721,34 +718,56 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 		kvm_call_hyp(__kvm_flush_cpu_context, mmu);
 		*last_ran = vcpu->vcpu_idx;
 	}
+}
 
-nommu:
+static void vcpu_put_mmu(struct kvm_vcpu *vcpu)
+{
+	kvm_arm_vmid_clear_active();
+}
+
+static void vcpu_set_wfx_traps(struct kvm_vcpu *vcpu)
+{
+	if (kvm_vcpu_should_clear_twe(vcpu))
+		vcpu->arch.hcr_el2 &= ~HCR_TWE;
+	else
+		vcpu->arch.hcr_el2 |= HCR_TWE;
+
+	if (kvm_vcpu_should_clear_twi(vcpu))
+		vcpu->arch.hcr_el2 &= ~HCR_TWI;
+	else
+		vcpu->arch.hcr_el2 |= HCR_TWI;
+}
+
+static void vcpu_load_pvtime(struct kvm_vcpu *vcpu)
+{
+	if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
+		kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
+}
+
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
 	vcpu->cpu = cpu;
 
+	if (!is_protected_kvm_enabled())
+		vcpu_prepare_mmu(vcpu);
 	/*
-	 * The timer must be loaded before the vgic to correctly set up physical
-	 * interrupt deactivation in nested state (e.g. timer interrupt).
+	 * For VHE, the timer must be loaded before the vgic to correctly
+	 * set up physical interrupt deactivation in nested state (e.g. timer
+	 * interrupt).
 	 */
 	kvm_timer_vcpu_load(vcpu);
 	kvm_vgic_load(vcpu);
 	kvm_vcpu_load_debug(vcpu);
 	kvm_vcpu_load_fgt(vcpu);
+
 	if (has_vhe())
 		kvm_vcpu_load_vhe(vcpu);
+
 	kvm_arch_vcpu_load_fp(vcpu);
 	kvm_vcpu_pmu_restore_guest(vcpu);
-	if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
-		kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
 
-	if (kvm_vcpu_should_clear_twe(vcpu))
-		vcpu->arch.hcr_el2 &= ~HCR_TWE;
-	else
-		vcpu->arch.hcr_el2 |= HCR_TWE;
-
-	if (kvm_vcpu_should_clear_twi(vcpu))
-		vcpu->arch.hcr_el2 &= ~HCR_TWI;
-	else
-		vcpu->arch.hcr_el2 |= HCR_TWI;
+	vcpu_load_pvtime(vcpu);
+	vcpu_set_wfx_traps(vcpu);
 
 	if (!is_protected_kvm_enabled())
 		vcpu_set_pauth_traps(vcpu);
@@ -787,7 +806,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 	kvm_vcpu_pmu_restore_host(vcpu);
 	if (vcpu_has_nv(vcpu))
 		kvm_vcpu_put_hw_mmu(vcpu);
-	kvm_arm_vmid_clear_active();
+
+	if (!is_protected_kvm_enabled())
+		vcpu_put_mmu(vcpu);
 
 	vcpu_clear_on_unsupported_cpu(vcpu);
 	vcpu->cpu = -1;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 08/23] KVM: arm64: Add vcpu load/put call backs for flavors
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (6 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 07/23] KVM: arm64: Refactor the vcpu_load to allow for VM specific callbacks Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  2:15   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types Suzuki K Poulose
                   ` (14 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Add VM flavor specific handlers for VCPU load/put, in an effort to make it
easier to follow the code. pauth traps were removed from VMs running PKVM
as it is a no-op for them.

Based on a patch by Marc Zyngier

Suggested-by: Marc Zyngier <maz@kernel.org>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v21:
 - Add '&' into the KVM_VCPU_OPS() macro
---
 arch/arm64/include/asm/kvm_host.h |   6 ++
 arch/arm64/kvm/arm.c              | 138 +++++++++++++++++++++++-------
 2 files changed, 114 insertions(+), 30 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index dedb5df15a803..839f5e9c7c65e 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -150,6 +150,11 @@ struct kvm_vmid {
 	atomic64_t id;
 };
 
+struct kvm_vcpu_ops {
+	void (*vcpu_load)(struct kvm_vcpu *vcpu);
+	void (*vcpu_put)(struct kvm_vcpu *vcpu);
+};
+
 struct kvm_s2_mmu {
 	struct kvm_vmid vmid;
 
@@ -855,6 +860,7 @@ struct vncr_tlb;
 
 struct kvm_vcpu_arch {
 	struct kvm_cpu_context ctxt;
+	const struct kvm_vcpu_ops *vcpu_ops;
 
 	/*
 	 * Guest floating point state
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 24800809b4a96..f31d31fa27ad9 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -93,6 +93,7 @@ static const struct kvm_ioctl_cap_map vm_ioctl_caps[] = {
 	{ KVM_ARM_PREFERRED_TARGET, KVM_CAP_ARM_BASIC },
 };
 
+static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu);
 /*
  * Set *ext to the capability.
  * Return 0 if found, or -EINVAL if no IOCTL matches.
@@ -569,6 +570,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 	mutex_unlock(&vcpu->mutex);
 #endif
 
+	kvm_init_vcpu_ops(vcpu);
+
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
 
@@ -744,12 +747,9 @@ static void vcpu_load_pvtime(struct kvm_vcpu *vcpu)
 		kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
 }
 
-void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+static void vhe_vcpu_load(struct kvm_vcpu *vcpu)
 {
-	vcpu->cpu = cpu;
-
-	if (!is_protected_kvm_enabled())
-		vcpu_prepare_mmu(vcpu);
+	vcpu_prepare_mmu(vcpu);
 	/*
 	 * For VHE, the timer must be loaded before the vgic to correctly
 	 * set up physical interrupt deactivation in nested state (e.g. timer
@@ -759,26 +759,53 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	kvm_vgic_load(vcpu);
 	kvm_vcpu_load_debug(vcpu);
 	kvm_vcpu_load_fgt(vcpu);
+	kvm_vcpu_load_vhe(vcpu);
+	kvm_arch_vcpu_load_fp(vcpu);
+	kvm_vcpu_pmu_restore_guest(vcpu);
+
+	vcpu_load_pvtime(vcpu);
+	vcpu_set_wfx_traps(vcpu);
+	vcpu_set_pauth_traps(vcpu);
+}
 
-	if (has_vhe())
-		kvm_vcpu_load_vhe(vcpu);
+static void nvhe_vcpu_load(struct kvm_vcpu *vcpu)
+{
+	vcpu_prepare_mmu(vcpu);
+	kvm_timer_vcpu_load(vcpu);
+	kvm_vgic_load(vcpu);
+	kvm_vcpu_load_debug(vcpu);
+	kvm_vcpu_load_fgt(vcpu);
+	kvm_arch_vcpu_load_fp(vcpu);
+	kvm_vcpu_pmu_restore_guest(vcpu);
+
+	vcpu_load_pvtime(vcpu);
+	vcpu_set_wfx_traps(vcpu);
+	vcpu_set_pauth_traps(vcpu);
+}
 
+static void pkvm_vcpu_load(struct kvm_vcpu *vcpu)
+{
+	kvm_timer_vcpu_load(vcpu);
+	kvm_vgic_load(vcpu);
+	kvm_vcpu_load_debug(vcpu);
+	kvm_vcpu_load_fgt(vcpu);
 	kvm_arch_vcpu_load_fp(vcpu);
 	kvm_vcpu_pmu_restore_guest(vcpu);
 
 	vcpu_load_pvtime(vcpu);
 	vcpu_set_wfx_traps(vcpu);
 
-	if (!is_protected_kvm_enabled())
-		vcpu_set_pauth_traps(vcpu);
+	kvm_call_hyp_nvhe(__pkvm_vcpu_load,
+			  vcpu->kvm->arch.pkvm.handle,
+			  vcpu->vcpu_idx, vcpu->arch.hcr_el2);
+	kvm_call_hyp_nvhe(__vgic_v3_restore_vmcr_aprs,
+			  &vcpu->arch.vgic_cpu.vgic_v3);
+}
 
-	if (is_protected_kvm_enabled()) {
-		kvm_call_hyp_nvhe(__pkvm_vcpu_load,
-				  vcpu->kvm->arch.pkvm.handle,
-				  vcpu->vcpu_idx, vcpu->arch.hcr_el2);
-		kvm_call_hyp(__vgic_v3_restore_vmcr_aprs,
-			     &vcpu->arch.vgic_cpu.vgic_v3);
-	}
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	vcpu->cpu = cpu;
+	vcpu->arch.vcpu_ops->vcpu_load(vcpu);
 
 	if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus))
 		vcpu_set_on_unsupported_cpu(vcpu);
@@ -786,30 +813,50 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	vcpu->arch.pid = pid_nr(vcpu->pid);
 }
 
-void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
+static void vhe_vcpu_put(struct kvm_vcpu *vcpu)
 {
-	if (is_protected_kvm_enabled()) {
-		kvm_call_hyp(__vgic_v3_save_aprs, &vcpu->arch.vgic_cpu.vgic_v3);
-		kvm_call_hyp_nvhe(__pkvm_vcpu_put);
-
-		/* __pkvm_vcpu_put implies a sync of the state */
-		if (vcpu->kvm->arch.vm_flavor == VM_PKVM)
-			vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
-	}
-
 	kvm_vcpu_put_debug(vcpu);
 	kvm_arch_vcpu_put_fp(vcpu);
-	if (has_vhe())
-		kvm_vcpu_put_vhe(vcpu);
+	kvm_vcpu_put_vhe(vcpu);
 	kvm_timer_vcpu_put(vcpu);
 	kvm_vgic_put(vcpu);
 	kvm_vcpu_pmu_restore_host(vcpu);
+
 	if (vcpu_has_nv(vcpu))
 		kvm_vcpu_put_hw_mmu(vcpu);
 
-	if (!is_protected_kvm_enabled())
-		vcpu_put_mmu(vcpu);
+	vcpu_put_mmu(vcpu);
+}
 
+static void nvhe_vcpu_put(struct kvm_vcpu *vcpu)
+{
+	kvm_vcpu_put_debug(vcpu);
+	kvm_arch_vcpu_put_fp(vcpu);
+	kvm_timer_vcpu_put(vcpu);
+	kvm_vgic_put(vcpu);
+	kvm_vcpu_pmu_restore_host(vcpu);
+	vcpu_put_mmu(vcpu);
+}
+
+static void pkvm_vcpu_put(struct kvm_vcpu *vcpu)
+{
+	kvm_call_hyp_nvhe(__vgic_v3_save_aprs, &vcpu->arch.vgic_cpu.vgic_v3);
+	kvm_call_hyp_nvhe(__pkvm_vcpu_put);
+
+	/* __pkvm_vcpu_put implies a sync of the state */
+	if (vcpu->kvm->arch.vm_flavor == VM_PKVM)
+		vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
+
+	kvm_vcpu_put_debug(vcpu);
+	kvm_arch_vcpu_put_fp(vcpu);
+	kvm_timer_vcpu_put(vcpu);
+	kvm_vgic_put(vcpu);
+	kvm_vcpu_pmu_restore_host(vcpu);
+}
+
+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
+{
+	vcpu->arch.vcpu_ops->vcpu_put(vcpu);
 	vcpu_clear_on_unsupported_cpu(vcpu);
 	vcpu->cpu = -1;
 }
@@ -2149,6 +2196,37 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
 	}
 }
 
+static const struct kvm_vcpu_ops vhe_vcpu_ops = {
+	.vcpu_load = vhe_vcpu_load,
+	.vcpu_put = vhe_vcpu_put,
+};
+
+static const struct kvm_vcpu_ops nvhe_vcpu_ops = {
+	.vcpu_load = nvhe_vcpu_load,
+	.vcpu_put = nvhe_vcpu_put,
+};
+
+static const struct kvm_vcpu_ops pkvm_vcpu_ops = {
+	.vcpu_load = pkvm_vcpu_load,
+	.vcpu_put = pkvm_vcpu_put,
+};
+
+#define KVM_VCPU_OPS(flavor, ops)		\
+	[(flavor)] = &(ops)
+
+static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {
+	KVM_VCPU_OPS(VM_NVHE, nvhe_vcpu_ops),
+	KVM_VCPU_OPS(VM_VHE, vhe_vcpu_ops),
+	KVM_VCPU_OPS(VM_PKVM, pkvm_vcpu_ops),
+	KVM_VCPU_OPS(VM_PROTECTED_PKVM, pkvm_vcpu_ops),
+};
+
+static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)
+{
+	BUILD_BUG_ON(ARRAY_SIZE(arm64_vcpu_ops) != VM_FLAVOR_MAX);
+	vcpu->arch.vcpu_ops = arm64_vcpu_ops[vcpu->kvm->arch.vm_flavor];
+}
+
 static unsigned long nvhe_percpu_size(void)
 {
 	return (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_end) -
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (7 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 08/23] KVM: arm64: Add vcpu load/put call backs for flavors Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  2:24   ` Gavin Shan
                     ` (2 more replies)
  2026-10-05  9:07 ` [PATCH v22 10/23] KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range Suzuki K Poulose
                   ` (13 subsequent siblings)
  22 siblings, 3 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Prevent unsupported VCPU features for the protected VCPUs. Realms and pVMs
not support 32bit EL1 or NV yet. pKVM doesn't rely on the host vcpu
features and it clears the unsupported features while hyp_vcpu is
initialised. Block the features early in the vcpu init if we detect
incompatible features.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/arm.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index f31d31fa27ad9..9c2ef7ca6a961 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1668,11 +1668,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
 	return -EINVAL;
 }
 
-static unsigned long system_supported_vcpu_features(void)
+static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
 {
 	unsigned long features = KVM_VCPU_VALID_FEATURES;
 
-	if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
+	if (vcpu_is_protected(vcpu) ||
+	    !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
 		clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
 
 	if (!kvm_supports_guest_pmuv3()) {
@@ -1688,7 +1689,8 @@ static unsigned long system_supported_vcpu_features(void)
 		clear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features);
 	}
 
-	if (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
+	if (vcpu_is_protected(vcpu) ||
+	    !cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
 		clear_bit(KVM_ARM_VCPU_HAS_EL2, &features);
 
 	return features;
@@ -1708,7 +1710,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
 			return -ENOENT;
 	}
 
-	if (features & ~system_supported_vcpu_features())
+	if (features & ~system_supported_vcpu_features(vcpu))
 		return -EINVAL;
 
 	/*
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 10/23] KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (8 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  2:37   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations Suzuki K Poulose
                   ` (12 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

In preparation for adding VM specific backends for stage2 operations,
nuke __unmap_stage2_range() and fold the logic into
kvm_stage2_unmap_range(). Also, make kvm_unmap_gfn_range(), the only other
user of the __unmap_stage2_range() call the kvm_stage2_unmap_range(). Also,
while at it fix the comment to make it clear that mmu_lock must always be
held while unmapping. The code already mandates that and the two call paths
do have the write mmu_lock held.

Later we would replace the logic in kvm_stage2_unmap_range() with VM
specific backends.

No functional changes intended.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Change since v21:
 - Nuke __unmap_stage2_range and consolidate the stage2_unmap_range logic
   into kvm_stage2_unmap_range(), in preparation for removing the KVM_PGT_FN()
   for stage2_unmap
---
 arch/arm64/kvm/mmu.c | 38 ++++++++++++++++----------------------
 1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 0f4e8b71fa85d..4ae3bb6baef1a 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -314,36 +314,30 @@ static void invalidate_icache_guest_page(void *va, size_t size)
  * does.
  */
 /**
- * __unmap_stage2_range -- Clear stage2 page table entries to unmap a range
+ * kvm_stage2_unmap_range -- Clear stage2 page table entries to unmap a range
  * @mmu:   The KVM stage-2 MMU pointer
  * @start: The intermediate physical base address of the range to unmap
  * @size:  The size of the area to unmap
  * @may_block: Whether or not we are permitted to block
  *
  * Clear a range of stage-2 mappings, lowering the various ref-counts.  Must
- * be called while holding mmu_lock (unless for freeing the stage2 pgd before
- * destroying the VM), otherwise another faulting VCPU may come in and mess
- * with things behind our backs.
+ * be called while holding mmu_lock otherwise another faulting VCPU may
+ * come in and mess with things behind our backs.
  */
-static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64 size,
-				 bool may_block)
-{
-	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
-	phys_addr_t end = start + size;
-
-	lockdep_assert_held_write(&kvm->mmu_lock);
-	WARN_ON(size & ~PAGE_MASK);
-	WARN_ON(stage2_apply_range(mmu, start, end, KVM_PGT_FN(kvm_pgtable_stage2_unmap),
-				   may_block));
-}
-
 void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
 			    u64 size, bool may_block)
 {
-	if (kvm_vm_is_protected(kvm_s2_mmu_to_kvm(mmu)))
+	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+
+	if (kvm_vm_is_protected(kvm))
 		return;
 
-	__unmap_stage2_range(mmu, start, size, may_block);
+	lockdep_assert_held_write(&kvm->mmu_lock);
+	WARN_ON(size & ~PAGE_MASK);
+
+	WARN_ON(stage2_apply_range(mmu, start, start + size,
+				   KVM_PGT_FN(kvm_pgtable_stage2_unmap),
+				   may_block));
 }
 
 void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
@@ -2436,12 +2430,12 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 
 bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
 {
-	if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
+	if (!kvm->arch.mmu.pgt)
 		return false;
 
-	__unmap_stage2_range(&kvm->arch.mmu, range->start << PAGE_SHIFT,
-			     (range->end - range->start) << PAGE_SHIFT,
-			     range->may_block);
+	kvm_stage2_unmap_range(&kvm->arch.mmu, range->start << PAGE_SHIFT,
+			       (range->end - range->start) << PAGE_SHIFT,
+			       range->may_block);
 
 	kvm_nested_s2_unmap(kvm, range->may_block);
 	return false;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (9 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 10/23] KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:00   ` Gavin Shan
  2026-10-06  9:24   ` Marc Zyngier
  2026-10-05  9:07 ` [PATCH v22 12/23] KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort() Suzuki K Poulose
                   ` (11 subsequent siblings)
  22 siblings, 2 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Add VM type specific S2 MMU operation backends which can be initialized per
VM flavor, to keep the handling cleaner.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Change since v21:
 - Define all vm_s2_ops call back. All calls are mandatory.
 - Define callback for each flavor, disjointing the non-protetcted pKVM and
   normal KVM (VHE & nVHE) and remove the KVM_PGT_FN() hacks.
 - Dropped Reviews due to the changes.
 - Add "no_age_gfn" and "no_stage2_unmap_range" for pKVM callbacks, no_age_*
   to be also reused by Realms later.
 - Move kvm_vm_s2_ops field to keep the structure packed
---
 arch/arm64/include/asm/kvm_host.h |  14 +++
 arch/arm64/kvm/mmu.c              | 173 +++++++++++++++++++++++++-----
 2 files changed, 160 insertions(+), 27 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 839f5e9c7c65e..0778c308ce597 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -155,6 +155,19 @@ struct kvm_vcpu_ops {
 	void (*vcpu_put)(struct kvm_vcpu *vcpu);
 };
 
+struct kvm_gfn_range;
+
+struct kvm_vm_s2_ops {
+	bool (*vm_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
+	bool (*vm_test_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
+	int (*vm_flush_remote_tlbs)(struct kvm *kvm);
+	int (*vm_flush_remote_tlbs_range)(struct kvm *kvm, gfn_t gfn,
+					  u64 nr_pages);
+	void (*vm_stage2_unmap_range)(struct kvm_s2_mmu *mmu,
+				      phys_addr_t start, u64 size,
+				      bool may_block);
+};
+
 struct kvm_s2_mmu {
 	struct kvm_vmid vmid;
 
@@ -321,6 +334,7 @@ enum kvm_arm_vm_flavor {
 
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
+	const struct kvm_vm_s2_ops *vm_s2_ops;
 
 	enum kvm_arm_vm_flavor vm_flavor;
 	/* Mandated version of PSCI */
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 4ae3bb6baef1a..c046c76833876 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -37,6 +37,8 @@ static unsigned long __ro_after_init io_map_base;
 
 #define KVM_PGT_FN(fn)		(!is_protected_kvm_enabled() ? fn : p ## fn)
 
+static int kvm_vm_init_vm_s2_ops(struct kvm *kvm);
+
 static phys_addr_t __stage2_range_addr_end(phys_addr_t addr, phys_addr_t end,
 					   phys_addr_t size)
 {
@@ -166,6 +168,18 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
 	return memslot->dirty_bitmap && !(memslot->flags & KVM_MEM_READONLY);
 }
 
+static int pkvm_flush_remote_tlbs(struct kvm *kvm)
+{
+	kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
+	return 0;
+}
+
+static int kvm_vm_flush_remote_tlbs(struct kvm *kvm)
+{
+	kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
+	return 0;
+}
+
 /**
  * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8
  * @kvm:	pointer to kvm structure.
@@ -174,26 +188,31 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
  */
 int kvm_arch_flush_remote_tlbs(struct kvm *kvm)
 {
-	if (is_protected_kvm_enabled())
-		kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
-	else
-		kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
-	return 0;
+	return kvm->arch.vm_s2_ops->vm_flush_remote_tlbs(kvm);
 }
 
-int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
-				      gfn_t gfn, u64 nr_pages)
+static int pkvm_flush_remote_tlbs_range(struct kvm *kvm,
+					gfn_t gfn, u64 nr_pages)
+{
+	return pkvm_flush_remote_tlbs(kvm);
+}
+
+static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,
+					 gfn_t gfn, u64 nr_pages)
 {
 	u64 size = nr_pages << PAGE_SHIFT;
 	u64 addr = gfn << PAGE_SHIFT;
 
-	if (is_protected_kvm_enabled())
-		kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
-	else
-		kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
+	kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
 	return 0;
 }
 
+int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
+				     gfn_t gfn, u64 nr_pages)
+{
+	return kvm->arch.vm_s2_ops->vm_flush_remote_tlbs_range(kvm, gfn, nr_pages);
+}
+
 static void *stage2_memcache_zalloc_page(void *arg)
 {
 	struct kvm_mmu_memory_cache *mc = arg;
@@ -289,6 +308,27 @@ static void invalidate_icache_guest_page(void *va, size_t size)
 	__invalidate_icache_guest_page(va, size);
 }
 
+static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
+				      phys_addr_t start,
+				      u64 size, bool may_block)
+{
+	WARN_ON(stage2_apply_range(mmu, start, start + size,
+				   kvm_pgtable_stage2_unmap, may_block));
+}
+
+static void pkvm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
+				    phys_addr_t start,
+				    u64 size, bool may_block)
+{
+	WARN_ON(stage2_apply_range(mmu, start, start + size,
+				   pkvm_pgtable_stage2_unmap, may_block));
+}
+
+static void no_stage2_unmap_range(struct kvm_s2_mmu *mmu,
+				  phys_addr_t start, u64 size, bool may_block)
+{
+}
+
 /*
  * Unmapping vs dcache management:
  *
@@ -329,15 +369,10 @@ void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
 {
 	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
 
-	if (kvm_vm_is_protected(kvm))
-		return;
-
 	lockdep_assert_held_write(&kvm->mmu_lock);
 	WARN_ON(size & ~PAGE_MASK);
 
-	WARN_ON(stage2_apply_range(mmu, start, start + size,
-				   KVM_PGT_FN(kvm_pgtable_stage2_unmap),
-				   may_block));
+	kvm->arch.vm_s2_ops->vm_stage2_unmap_range(mmu, start, size, may_block);
 }
 
 void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
@@ -977,6 +1012,12 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
 	int cpu, err;
 	struct kvm_pgtable *pgt;
 
+	/* Initialize the VM ops for the VM instance for the first time */
+	if (mmu == &kvm->arch.mmu) {
+		err = kvm_vm_init_vm_s2_ops(kvm);
+		if (err)
+			return err;
+	}
 	/*
 	 * If we already have our page tables in place, and that the
 	 * MMU context is the canonical one, we have a bug somewhere,
@@ -2441,34 +2482,68 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
 	return false;
 }
 
-bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+static bool kvm_vm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
 {
 	u64 size = (range->end - range->start) << PAGE_SHIFT;
 
-	if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
-		return false;
-
-	return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
+	return kvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
 						   range->start << PAGE_SHIFT,
 						   size, true);
+}
+
+static bool pkvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	u64 size = (range->end - range->start) << PAGE_SHIFT;
+
+	return pkvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
+						    range->start << PAGE_SHIFT,
+						    size, true);
+}
+
+static bool no_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	/* The hypervisor doesn't support aging */
+	return false;
+}
+
+bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	if (!kvm->arch.mmu.pgt)
+		return false;
+
+	return kvm->arch.vm_s2_ops->vm_age_gfn(kvm, range);
 	/*
 	 * TODO: Handle nested_mmu structures here using the reverse mapping in
 	 * a later version of patch series.
 	 */
 }
 
-bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+static bool kvm_vm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
 {
 	u64 size = (range->end - range->start) << PAGE_SHIFT;
 
-	if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
-		return false;
-
-	return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
+	return kvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
 						   range->start << PAGE_SHIFT,
 						   size, false);
 }
 
+static bool pkvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	u64 size = (range->end - range->start) << PAGE_SHIFT;
+
+	return pkvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
+						    range->start << PAGE_SHIFT,
+						    size, false);
+}
+
+bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	if (!kvm->arch.mmu.pgt)
+		return false;
+
+	return kvm->arch.vm_s2_ops->vm_test_age_gfn(kvm, range);
+}
+
 phys_addr_t kvm_mmu_get_httbr(void)
 {
 	return __pa(hyp_pgtable->pgd);
@@ -2790,3 +2865,47 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)
 
 	trace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);
 }
+
+static const struct kvm_vm_s2_ops protected_pkvm_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= pkvm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= pkvm_flush_remote_tlbs_range,
+	.vm_age_gfn			= no_age_gfn,
+	.vm_test_age_gfn		= no_age_gfn,
+	.vm_stage2_unmap_range		= no_stage2_unmap_range,
+};
+
+static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= pkvm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= pkvm_flush_remote_tlbs_range,
+	.vm_age_gfn			= pkvm_age_gfn,
+	.vm_test_age_gfn		= pkvm_test_age_gfn,
+	.vm_stage2_unmap_range		= pkvm_stage2_unmap_range,
+};
+
+static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= kvm_vm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= kvm_vm_flush_remote_tlbs_range,
+	.vm_age_gfn			= kvm_vm_age_gfn,
+	.vm_test_age_gfn		= kvm_vm_test_age_gfn,
+	.vm_stage2_unmap_range		= kvm_vm_stage2_unmap_range,
+};
+
+#define KVM_VM_S2_OPS(flavor, ops)		\
+		[flavor] = &(ops)
+
+static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {
+	KVM_VM_S2_OPS(VM_VHE, kvm_default_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_NVHE, kvm_default_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_PKVM, pkvm_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_PROTECTED_PKVM, protected_pkvm_vm_s2_ops),
+};
+
+static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)
+{
+	BUILD_BUG_ON(ARRAY_SIZE(arm64_vm_s2_ops) != VM_FLAVOR_MAX);
+
+	kvm->arch.vm_s2_ops = arm64_vm_s2_ops[kvm->arch.vm_flavor];
+	if (WARN_ON(!kvm->arch.vm_s2_ops))
+		return -EINVAL;
+	return 0;
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 12/23] KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort()
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (10 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:02   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling Suzuki K Poulose
                   ` (10 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose, Fuad Tabba

kvm_handle_guest_abort() repeatedly obtains the VM from vcpu->kvm.

Introduce a local kvm pointer and use it throughout the function. While
touching the code, fix the missing whitespace in the
kvm_is_nested_s2_mmu() call.

Suggested-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/mmu.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index c046c76833876..9cc7fcde4dd0a 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2284,6 +2284,7 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
  */
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 {
+	struct kvm *kvm = vcpu->kvm;
 	struct kvm_s2_trans nested_trans, *nested = NULL;
 	unsigned long esr;
 	phys_addr_t fault_ipa; /* The address we faulted on */
@@ -2304,7 +2305,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 	 * with an SEA.
 	 */
 	ipa = fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
-	if (KVM_BUG_ON(ipa == INVALID_GPA, vcpu->kvm))
+	if (KVM_BUG_ON(ipa == INVALID_GPA, kvm))
 		return -EFAULT;
 
 	is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
@@ -2339,7 +2340,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		return -EFAULT;
 	}
 
-	idx = srcu_read_lock(&vcpu->kvm->srcu);
+	idx = srcu_read_lock(&kvm->srcu);
 
 	/*
 	 * We may have faulted on a shadow stage 2 page table if we are
@@ -2354,7 +2355,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 	 * nothing to walk and we treat it as a 1:1 before going through the
 	 * canonical translation.
 	 */
-	if (kvm_is_nested_s2_mmu(vcpu->kvm,vcpu->arch.hw_mmu) &&
+	if (kvm_is_nested_s2_mmu(kvm, vcpu->arch.hw_mmu) &&
 	    vcpu->arch.hw_mmu->nested_stage2_enabled) {
 		u32 esr;
 
@@ -2382,7 +2383,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 	}
 
 	gfn = ipa >> PAGE_SHIFT;
-	memslot = gfn_to_memslot(vcpu->kvm, gfn);
+	memslot = gfn_to_memslot(kvm, gfn);
 	hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
 	write_fault = kvm_is_write_fault(vcpu);
 	if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
@@ -2446,7 +2447,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		.hva		= hva,
 	};
 
-	if (kvm_vm_is_protected(vcpu->kvm)) {
+	if (kvm_vm_is_protected(kvm)) {
 		ret = pkvm_mem_abort(&s2fd);
 	} else {
 		VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
@@ -2465,7 +2466,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 	if (ret == -ENOEXEC)
 		ret = kvm_inject_sea_iabt(vcpu, kvm_vcpu_get_hfar(vcpu));
 out_unlock:
-	srcu_read_unlock(&vcpu->kvm->srcu, idx);
+	srcu_read_unlock(&kvm->srcu, idx);
 	return ret;
 }
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (11 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 12/23] KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort() Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:07   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 14/23] KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms Suzuki K Poulose
                   ` (9 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Move the memory abort handling under VM specific s2 operation.

Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v21:
 - Rename protected_vm_mem_abort => protected_pkvm_mem_abort for consistency
   with the other callbacks.
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/kvm/mmu.c              | 33 ++++++++++++++++++-------------
 2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 0778c308ce597..7cb343583953d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -156,6 +156,7 @@ struct kvm_vcpu_ops {
 };
 
 struct kvm_gfn_range;
+struct kvm_s2_fault_desc;
 
 struct kvm_vm_s2_ops {
 	bool (*vm_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
@@ -166,6 +167,7 @@ struct kvm_vm_s2_ops {
 	void (*vm_stage2_unmap_range)(struct kvm_s2_mmu *mmu,
 				      phys_addr_t start, u64 size,
 				      bool may_block);
+	int (*vm_mem_abort)(const struct kvm_s2_fault_desc *s2fd);
 };
 
 struct kvm_s2_mmu {
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 9cc7fcde4dd0a..ccbe2ffeda40b 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1740,7 +1740,7 @@ struct kvm_s2_fault_vma_info {
 	bool		map_non_cacheable;
 };
 
-static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
+static int protected_pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 {
 	unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
 	struct kvm_vcpu *vcpu = s2fd->vcpu;
@@ -2178,6 +2178,20 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 	return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache);
 }
 
+static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
+{
+	struct kvm_vcpu *vcpu = s2fd->vcpu;
+
+	VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
+			!kvm_is_write_fault(vcpu) &&
+			!kvm_vcpu_trap_is_exec_fault(vcpu));
+
+	if (kvm_slot_has_gmem(s2fd->memslot))
+		return gmem_abort(s2fd);
+	else
+		return user_mem_abort(s2fd);
+}
+
 /* Resolve the access fault by making the page young again. */
 static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
 {
@@ -2447,19 +2461,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		.hva		= hva,
 	};
 
-	if (kvm_vm_is_protected(kvm)) {
-		ret = pkvm_mem_abort(&s2fd);
-	} else {
-		VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
-				!write_fault &&
-				!kvm_vcpu_trap_is_exec_fault(vcpu));
-
-		if (kvm_slot_has_gmem(memslot))
-			ret = gmem_abort(&s2fd);
-		else
-			ret = user_mem_abort(&s2fd);
-	}
-
+	ret = kvm->arch.vm_s2_ops->vm_mem_abort(&s2fd);
 	if (ret == 0)
 		ret = 1;
 out:
@@ -2873,6 +2875,7 @@ static const struct kvm_vm_s2_ops protected_pkvm_vm_s2_ops = {
 	.vm_age_gfn			= no_age_gfn,
 	.vm_test_age_gfn		= no_age_gfn,
 	.vm_stage2_unmap_range		= no_stage2_unmap_range,
+	.vm_mem_abort			= protected_pkvm_mem_abort,
 };
 
 static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {
@@ -2881,6 +2884,7 @@ static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {
 	.vm_age_gfn			= pkvm_age_gfn,
 	.vm_test_age_gfn		= pkvm_test_age_gfn,
 	.vm_stage2_unmap_range		= pkvm_stage2_unmap_range,
+	.vm_mem_abort			= kvm_vm_mem_abort,
 };
 
 static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
@@ -2889,6 +2893,7 @@ static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
 	.vm_age_gfn			= kvm_vm_age_gfn,
 	.vm_test_age_gfn		= kvm_vm_test_age_gfn,
 	.vm_stage2_unmap_range		= kvm_vm_stage2_unmap_range,
+	.vm_mem_abort			= kvm_vm_mem_abort,
 };
 
 #define KVM_VM_S2_OPS(flavor, ops)		\
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 14/23] KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (12 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:10   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 15/23] KVM: arm64: CCA: Add a new mode for supporting Realm guests Suzuki K Poulose
                   ` (8 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose, Fuad Tabba

pKVM does not trust the host. Realm VMs follow a similar trust model, with
the Realm Management Monitor owning the protected state instead of the
host. Add a helper to identify VMs that run under a host-distrusting
hypervisor.

Use this for blocking ioremap of vgic-v2 into stage2 and prevent creation
of VGIC other than v3.

Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Tested-by: Gavin Shan <gshan@redhat.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/include/asm/kvm_host.h | 4 ++++
 arch/arm64/kvm/mmu.c              | 2 +-
 arch/arm64/kvm/vgic/vgic-init.c   | 2 ++
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7cb343583953d..56861b75411f1 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -328,6 +328,8 @@ enum fgt_group_id {
 enum kvm_arm_vm_flavor {
 	VM_NVHE,
 	VM_VHE,
+	/* VMs running on a hyp that doesn't trust */
+	MARKER(__VM_DISTRUSTING_HYP),
 	VM_PKVM,		/* Normal guests on pKVM */
 	MARKER(__VM_PROTECTED),
 	VM_PROTECTED_PKVM,	/* Protected VM */
@@ -1562,6 +1564,8 @@ struct kvm *kvm_arch_alloc_vm(void);
 		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
 	})
 
+#define kvm_vm_hyp_is_distrusting(kvm)	((kvm)->arch.vm_flavor >= __VM_DISTRUSTING_HYP)
+
 int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);
 bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
 
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index ccbe2ffeda40b..909ea3f545e83 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1249,7 +1249,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
 				     KVM_PGTABLE_PROT_R |
 				     (writable ? KVM_PGTABLE_PROT_W : 0);
 
-	if (is_protected_kvm_enabled())
+	if (kvm_vm_hyp_is_distrusting(kvm))
 		return -EPERM;
 
 	size += offset_in_page(guest_ipa);
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 4012df6002ea6..874025513afcc 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -84,6 +84,8 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
 		!kvm_vgic_global_state.can_emulate_gicv2)
 		return -ENODEV;
 
+	if (kvm_vm_hyp_is_distrusting(kvm) && type != KVM_DEV_TYPE_ARM_VGIC_V3)
+		return -ENODEV;
 	/*
 	 * Ensure mutual exclusion with vCPU creation and any vCPU ioctls by:
 	 *
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 15/23] KVM: arm64: CCA: Add a new mode for supporting Realm guests
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (13 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 14/23] KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:11   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms Suzuki K Poulose
                   ` (7 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Add an explicit mode to support Arm CCA guests.

Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 Documentation/admin-guide/kernel-parameters.txt | 3 +++
 arch/arm64/include/asm/kvm_host.h               | 1 +
 arch/arm64/kvm/arm.c                            | 5 +++++
 3 files changed, 9 insertions(+)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd24..1afe3df3b923e 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3256,6 +3256,9 @@ Kernel parameters
 			nested: VHE-based mode with support for nested
 				virtualization. Requires at least ARMv8.4
 				hardware (with FEAT_NV2).
+			rmm: Support for running confidential guests in Realm
+			     world using RMM, as defined by Arm Confidential
+			     Compute Architecture (CCA)
 
 			Defaults to VHE/nVHE based on hardware support. Setting
 			mode to "protected" will disable kexec and hibernation
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 56861b75411f1..dfa9d4ec61a76 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -69,6 +69,7 @@ enum kvm_mode {
 	KVM_MODE_DEFAULT,
 	KVM_MODE_PROTECTED,
 	KVM_MODE_NV,
+	KVM_MODE_RMM,
 	KVM_MODE_NONE,
 };
 #ifdef CONFIG_KVM
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9c2ef7ca6a961..6c81fec35ecec 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -3279,6 +3279,11 @@ static int __init early_kvm_mode_cfg(char *arg)
 		return 0;
 	}
 
+	if (strcmp(arg, "rmm") == 0 && !WARN_ON(!is_kernel_in_hyp_mode())) {
+		kvm_mode = KVM_MODE_RMM;
+		return 0;
+	}
+
 	return -EINVAL;
 }
 early_param("kvm-arm.mode", early_kvm_mode_cfg);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (14 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 15/23] KVM: arm64: CCA: Add a new mode for supporting Realm guests Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:16   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 17/23] KVM: arm64: CCA: Add bare minimal S2 operations for Realm Suzuki K Poulose
                   ` (6 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

RMM controls the VCPU settings and most are hidden from the KVM, except
for the VGIC and timer bits.

A later patch would add syncing the VCPU state into the Realm REC related
SMC parameters.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/arm.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 6c81fec35ecec..294cea4cc8271 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -802,6 +802,13 @@ static void pkvm_vcpu_load(struct kvm_vcpu *vcpu)
 			  &vcpu->arch.vgic_cpu.vgic_v3);
 }
 
+static void realm_vcpu_load(struct kvm_vcpu *vcpu)
+{
+	kvm_timer_vcpu_load(vcpu);
+	kvm_vgic_load(vcpu);
+	vcpu_set_wfx_traps(vcpu);
+}
+
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
 	vcpu->cpu = cpu;
@@ -854,6 +861,12 @@ static void pkvm_vcpu_put(struct kvm_vcpu *vcpu)
 	kvm_vcpu_pmu_restore_host(vcpu);
 }
 
+static void realm_vcpu_put(struct kvm_vcpu *vcpu)
+{
+	kvm_timer_vcpu_put(vcpu);
+	kvm_vgic_put(vcpu);
+}
+
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
 	vcpu->arch.vcpu_ops->vcpu_put(vcpu);
@@ -2213,6 +2226,11 @@ static const struct kvm_vcpu_ops pkvm_vcpu_ops = {
 	.vcpu_put = pkvm_vcpu_put,
 };
 
+static const struct kvm_vcpu_ops realm_vcpu_ops = {
+	.vcpu_load = realm_vcpu_load,
+	.vcpu_put = realm_vcpu_put,
+};
+
 #define KVM_VCPU_OPS(flavor, ops)		\
 	[(flavor)] = &(ops)
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 17/23] KVM: arm64: CCA: Add bare minimal S2 operations for Realm
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (15 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:18   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms Suzuki K Poulose
                   ` (5 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Add bare minimal MMU operation hooks for Realms. The mem_abort handling
is chosen as the default KVM variant. However this cannot be reached for
Realms yet and we would need real RMI command support to make it fully
functional. Similarly, unmapping stage2 requires RMI command support.
Until then reuse follow protected pKVM hook and ignore unmapping.
This will be addressed in the later series.

RMM takes care of the TLB flushing as required, when the Stage2 is
modified. So host doesn't need to do anything explicitly. RMM doesn't
support access flags for the stage2, even for the shared IPA.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v21:
 - Reuse no_age_gfn for Realms. Also use no_stage2_unmap_range for
   now, until we get proper RMI backed driver.
---
 arch/arm64/kvm/mmu.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 909ea3f545e83..58bc6a85f48b4 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -180,6 +180,12 @@ static int kvm_vm_flush_remote_tlbs(struct kvm *kvm)
 	return 0;
 }
 
+static int realm_flush_remote_tlbs(struct kvm *kvm)
+{
+	/* Nothing to do here, RMM does the job */
+	return 0;
+}
+
 /**
  * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8
  * @kvm:	pointer to kvm structure.
@@ -207,6 +213,13 @@ static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,
 	return 0;
 }
 
+static int realm_flush_remote_tlbs_range(struct kvm *kvm,
+					 gfn_t gfn, u64 nr_pages)
+{
+	/* Nothing to do here, RMM does the job */
+	return 0;
+}
+
 int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
 				     gfn_t gfn, u64 nr_pages)
 {
@@ -2896,6 +2909,16 @@ static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
 	.vm_mem_abort			= kvm_vm_mem_abort,
 };
 
+static const struct kvm_vm_s2_ops realm_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= realm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= realm_flush_remote_tlbs_range,
+	.vm_age_gfn			= no_age_gfn,
+	.vm_test_age_gfn		= no_age_gfn,
+	/* Until we get proper support for unmap */
+	.vm_stage2_unmap_range		= no_stage2_unmap_range,
+	.vm_mem_abort			= kvm_vm_mem_abort,
+};
+
 #define KVM_VM_S2_OPS(flavor, ops)		\
 		[flavor] = &(ops)
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (16 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 17/23] KVM: arm64: CCA: Add bare minimal S2 operations for Realm Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:42   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 19/23] KVM: arm64: CCA: Don't expose unsupported capabilities for realm guests Suzuki K Poulose
                   ` (4 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

From: Steven Price <steven.price@arm.com>

Add foundational work for supporting Realms.
 - Add a new VM flavor.
 - At KVM init, check if the KVM can support Realms (though not functional
   yet) and will be advertised by static key kvm_rmi_is_available. This
   will be turned on in a later patches, once we have all the bits and
   pieces ready. For now check if we are blessed with KVM_MODE_RMM.
 - Add realm specific tracking in kvm_arch. Since Realm and protected pKVM
   states are mutually exclusive, move them into a union.

Please note that we cannot create Realm VMs yet. This requires further
changes to the UABI and core RMI driver support, which will come later.

Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Steven Price <steven.price@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/include/asm/kvm_emulate.h | 16 ++++++++
 arch/arm64/include/asm/kvm_host.h    | 19 ++++++---
 arch/arm64/include/asm/kvm_rmi.h     | 61 ++++++++++++++++++++++++++++
 arch/arm64/include/asm/virt.h        |  1 +
 arch/arm64/kvm/Makefile              |  2 +-
 arch/arm64/kvm/arm.c                 |  6 +++
 arch/arm64/kvm/mmu.c                 |  1 +
 arch/arm64/kvm/rmi.c                 | 18 ++++++++
 8 files changed, 118 insertions(+), 6 deletions(-)
 create mode 100644 arch/arm64/include/asm/kvm_rmi.h
 create mode 100644 arch/arm64/kvm/rmi.c

diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index a3c1928bdf743..d360a8b05b8bf 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -793,4 +793,20 @@ static inline void kvm_reset_vcpu_psci(struct kvm_vcpu *vcpu,
 	vcpu_set_reg(vcpu, 0, reset_state->r0);
 }
 
+static inline enum realm_state kvm_realm_state(struct kvm *kvm)
+{
+	return READ_ONCE(kvm->arch.realm.state);
+}
+
+static inline void kvm_set_realm_state(struct kvm *kvm,
+				       enum realm_state new_state)
+{
+	WRITE_ONCE(kvm->arch.realm.state, new_state);
+}
+
+static inline bool kvm_realm_is_created(struct kvm *kvm)
+{
+	return kvm_vm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
+}
+
 #endif /* __ARM64_KVM_EMULATE_H__ */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index dfa9d4ec61a76..3debffef638a4 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -27,6 +27,7 @@
 #include <asm/fpsimd.h>
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_rmi.h>
 #include <asm/vncr_mapping.h>
 
 #define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -334,6 +335,7 @@ enum kvm_arm_vm_flavor {
 	VM_PKVM,		/* Normal guests on pKVM */
 	MARKER(__VM_PROTECTED),
 	VM_PROTECTED_PKVM,	/* Protected VM */
+	VM_REALM,		/* CCA */
 	VM_FLAVOR_MAX
 };
 
@@ -450,11 +452,14 @@ struct kvm_arch {
 	/* Count the number of VNCR_EL2 TLBs */
 	atomic_t vncr_tlb_count;
 
-	/*
-	 * For an untrusted host VM, 'pkvm.handle' is used to lookup
-	 * the associated pKVM instance in the hypervisor.
-	 */
-	struct kvm_protected_vm pkvm;
+	union {
+		/*
+		 * For an untrusted host VM, 'pkvm.handle' is used to lookup
+		 * the associated pKVM instance in the hypervisor.
+		 */
+		struct kvm_protected_vm pkvm;
+		struct realm realm;
+	};
 
 #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
 	/* Nested virtualization info */
@@ -1565,6 +1570,10 @@ struct kvm *kvm_arch_alloc_vm(void);
 		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
 	})
 
+
+#define kvm_vm_is_realm(kvm)		((kvm)->arch.vm_flavor == VM_REALM)
+#define vcpu_is_rec(vcpu)		kvm_vm_is_realm((vcpu)->kvm)
+
 #define kvm_vm_hyp_is_distrusting(kvm)	((kvm)->arch.vm_flavor >= __VM_DISTRUSTING_HYP)
 
 int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
new file mode 100644
index 0000000000000..44f5c75a27b5b
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_rmi.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#ifndef __ASM_KVM_RMI_H
+#define __ASM_KVM_RMI_H
+
+/**
+ * enum realm_state - State of a Realm
+ *
+ * Mirrors the RMM's Realm lifecycle states where they are meaningful to KVM,
+ * with REALM_STATE_DYING being a KVM-internal state used to prevent further
+ * requests while teardown is in progress. KVM does not track REALM_SYSTEM_OFF
+ * or REALM_ZOMBIE separately as they naturally lead to teardown.
+ */
+enum realm_state {
+	/**
+	 * @REALM_STATE_NONE:
+	 *      Realm has not yet been created. rmi_realm_create() has not
+	 *      yet been called.
+	 */
+	REALM_STATE_NONE,
+	/**
+	 * @REALM_STATE_NEW:
+	 *      Realm is under construction, rmi_realm_create() has been
+	 *      called, but it is not yet activated. Pages may be populated.
+	 */
+	REALM_STATE_NEW,
+	/**
+	 * @REALM_STATE_ACTIVE:
+	 *      Realm has been created and is eligible for execution with
+	 *      rmi_rec_enter(). Pages may no longer be populated with
+	 *      rmi_data_create().
+	 */
+	REALM_STATE_ACTIVE,
+	/**
+	 * @REALM_STATE_DYING:
+	 *      Realm is in the process of being destroyed or has already been
+	 *      destroyed.
+	 */
+	REALM_STATE_DYING,
+	/**
+	 * @REALM_STATE_DEAD:
+	 *      Realm has been destroyed.
+	 */
+	REALM_STATE_DEAD
+};
+
+/**
+ * struct realm - Additional per VM data for a Realm
+ *
+ * @state: The lifetime state machine for the realm
+ */
+struct realm {
+	enum realm_state state;
+};
+
+void kvm_init_rmi(void);
+
+#endif /* __ASM_KVM_RMI_H */
diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index b546703c3ab9a..92cec42952f42 100644
--- a/arch/arm64/include/asm/virt.h
+++ b/arch/arm64/include/asm/virt.h
@@ -87,6 +87,7 @@ void __hyp_reset_vectors(void);
 bool is_kvm_arm_initialised(void);
 
 DECLARE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);
+DECLARE_STATIC_KEY_FALSE(kvm_rmi_is_available);
 
 static inline bool is_pkvm_initialized(void)
 {
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 59612d2f277c1..ed3cf30eb06e7 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -16,7 +16,7 @@ CFLAGS_handle_exit.o += -Wno-override-init
 kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
 	 inject_fault.o va_layout.o handle_exit.o config.o \
 	 guest.o debug.o reset.o sys_regs.o stacktrace.o \
-	 vgic-sys-reg-v3.o fpsimd.o pkvm.o \
+	 vgic-sys-reg-v3.o fpsimd.o pkvm.o rmi.o \
 	 arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
 	 vgic/vgic.o vgic/vgic-init.o \
 	 vgic/vgic-irqfd.o vgic/vgic-v2.o \
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 294cea4cc8271..37ef2ba4e43e2 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -42,6 +42,7 @@
 #include <asm/kvm_nested.h>
 #include <asm/kvm_pkvm.h>
 #include <asm/kvm_ptrauth.h>
+#include <asm/kvm_rmi.h>
 #include <asm/sections.h>
 #include <asm/stacktrace/nvhe.h>
 
@@ -112,6 +113,8 @@ long kvm_get_cap_for_kvm_ioctl(unsigned int ioctl, long *ext)
 	return -EINVAL;
 }
 
+DEFINE_STATIC_KEY_FALSE(kvm_rmi_is_available);
+
 DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);
 
 DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_base);
@@ -2239,6 +2242,7 @@ static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {
 	KVM_VCPU_OPS(VM_VHE, vhe_vcpu_ops),
 	KVM_VCPU_OPS(VM_PKVM, pkvm_vcpu_ops),
 	KVM_VCPU_OPS(VM_PROTECTED_PKVM, pkvm_vcpu_ops),
+	KVM_VCPU_OPS(VM_REALM, realm_vcpu_ops),
 };
 
 static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)
@@ -3192,6 +3196,8 @@ static __init int kvm_arm_init(void)
 
 	in_hyp_mode = is_kernel_in_hyp_mode();
 
+	kvm_init_rmi();
+
 	if (cpus_have_final_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ||
 	    cpus_have_final_cap(ARM64_WORKAROUND_1508412))
 		kvm_info("Guests without required CPU erratum workarounds can deadlock system!\n" \
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 58bc6a85f48b4..697f1ba6667a9 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -2927,6 +2927,7 @@ static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {
 	KVM_VM_S2_OPS(VM_NVHE, kvm_default_vm_s2_ops),
 	KVM_VM_S2_OPS(VM_PKVM, pkvm_vm_s2_ops),
 	KVM_VM_S2_OPS(VM_PROTECTED_PKVM, protected_pkvm_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_REALM, realm_vm_s2_ops),
 };
 
 static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
new file mode 100644
index 0000000000000..5ecc8b3498698
--- /dev/null
+++ b/arch/arm64/kvm/rmi.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#include <linux/kvm_host.h>
+
+#include <asm/virt.h>
+
+void kvm_init_rmi(void)
+{
+	if (kvm_get_mode() != KVM_MODE_RMM)
+		return;
+
+	/* TODO: Check if the RMI is available */
+
+	/* Future patch will enable static branch kvm_rmi_is_available */
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 19/23] KVM: arm64: CCA: Don't expose unsupported capabilities for realm guests
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (17 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  4:58   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 20/23] KVM: arm64: CCA: WARN on injected undef exceptions Suzuki K Poulose
                   ` (3 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

Limit the capabilities that are allowed for Realm VMs. Similarly block
the vm_ioctls backed by the capabilities.

Repurpose the kvm_pkvm_ioctl_allowed() to support both pKVM and Realm
ioctls. Rename the helper to kvm_vm_ioctl_allowed() and move it
into arch/arm64/kvm/arm.c. Also add a generic kvm_vm_ext_allowed()
to handle pKVM and Realm capability filtering and route them accordingly.

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v21:
 - Drop WARN_ON_ONCE() from kvm_vm_ioctl_allowed to synchronise with
   what is in -rc5 and make the conflict resolution easier.
---
 arch/arm64/include/asm/kvm_pkvm.h | 19 ------------
 arch/arm64/include/asm/kvm_rmi.h  | 24 +++++++++++++++
 arch/arm64/kvm/arm.c              | 49 +++++++++++++++++++++++++++++--
 3 files changed, 70 insertions(+), 22 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 2addc37c500e1..b833abb1be9c7 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -53,25 +53,6 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
 	}
 }
 
-/*
- * Check whether the KVM VM IOCTL is allowed in pKVM.
- *
- * Certain features are allowed only for non-protected VMs in pKVM, which is why
- * this takes the VM (kvm) as a parameter.
- */
-static inline bool kvm_pkvm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
-{
-	long ext;
-	int r;
-
-	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
-
-	if (WARN_ON_ONCE(r < 0))
-		return false;
-
-	return kvm_pkvm_ext_allowed(kvm, ext);
-}
-
 extern struct memblock_region kvm_nvhe_sym(hyp_memory)[];
 extern unsigned int kvm_nvhe_sym(hyp_memblock_nr);
 
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
index 44f5c75a27b5b..ea1450e0c8619 100644
--- a/arch/arm64/include/asm/kvm_rmi.h
+++ b/arch/arm64/include/asm/kvm_rmi.h
@@ -6,6 +6,8 @@
 #ifndef __ASM_KVM_RMI_H
 #define __ASM_KVM_RMI_H
 
+#include <linux/kvm.h>
+
 /**
  * enum realm_state - State of a Realm
  *
@@ -58,4 +60,26 @@ struct realm {
 
 void kvm_init_rmi(void);
 
+static inline bool kvm_realm_ext_allowed(long ext)
+{
+	switch (ext) {
+	case KVM_CAP_IRQCHIP:
+	case KVM_CAP_ARM_PSCI:
+	case KVM_CAP_ARM_PSCI_0_2:
+	case KVM_CAP_DEVICE_CTRL:
+	case KVM_CAP_NR_VCPUS:
+	case KVM_CAP_MAX_VCPUS:
+	case KVM_CAP_MAX_VCPU_ID:
+	case KVM_CAP_MSI_DEVID:
+	case KVM_CAP_ARM_VM_IPA_SIZE:
+	case KVM_CAP_ARM_SVE:
+	case KVM_CAP_ONE_REG:
+	case KVM_CAP_ARM_PTRAUTH_ADDRESS:
+	case KVM_CAP_ARM_PTRAUTH_GENERIC:
+	case KVM_CAP_SYNC_MMU:
+		return true;
+	}
+	return false;
+}
+
 #endif /* __ASM_KVM_RMI_H */
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 37ef2ba4e43e2..fe707a0c47308 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -136,6 +136,49 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
 }
 
+static inline bool kvm_vm_ext_allowed(struct kvm *kvm, long ext)
+{
+	/*
+	 * We could be called with kvm as NULL, so can't use kvm_vm_* for pKVM
+	 * flavors
+	 */
+	if (is_protected_kvm_enabled())
+		return kvm_pkvm_ext_allowed(kvm, ext);
+	else if (kvm && kvm_vm_is_realm(kvm))
+		return kvm_realm_ext_allowed(ext);
+	else
+		return true;
+}
+
+/*
+ * Check whether the KVM VM IOCTL is allowed. For pKVM and Realm VMs, certain
+ * ioctls are not allowed. Further, certain features are allowed only for
+ * non-protected VMs in pKVM.
+ */
+static inline bool kvm_vm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
+{
+	long ext;
+	int r;
+
+	/*
+	 * We are guaranteed to be called with a valid kvm instance, as the
+	 * only caller is kvm_arch_vm_ioctl(). Catch any deviations, as we
+	 * rely on the kvm instance below.
+	 */
+	if (WARN_ON_ONCE(!kvm))
+		return false;
+
+	/* Cover both pKVM host and Realm VMs */
+	if (!kvm_vm_hyp_is_distrusting(kvm))
+		return true;
+
+	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
+	if (r < 0)
+		return false;
+
+	return kvm_vm_ext_allowed(kvm, ext);
+}
+
 int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 			    struct kvm_enable_cap *cap)
 {
@@ -144,7 +187,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 	if (cap->flags)
 		return -EINVAL;
 
-	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, cap->cap))
+	if (!kvm_vm_ext_allowed(kvm, cap->cap))
 		return -EINVAL;
 
 	switch (cap->cap) {
@@ -403,7 +446,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 {
 	int r;
 
-	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
+	if (!kvm_vm_ext_allowed(kvm, ext))
 		return 0;
 
 	switch (ext) {
@@ -2146,7 +2189,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
 	void __user *argp = (void __user *)arg;
 	struct kvm_device_attr attr;
 
-	if (is_protected_kvm_enabled() && !kvm_pkvm_ioctl_allowed(kvm, ioctl))
+	if (!kvm_vm_ioctl_allowed(kvm, ioctl))
 		return -EINVAL;
 
 	switch (ioctl) {
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 20/23] KVM: arm64: CCA: WARN on injected undef exceptions
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (18 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 19/23] KVM: arm64: CCA: Don't expose unsupported capabilities for realm guests Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  3:49   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 21/23] KVM: arm64: CCA: Support timers in realm RECs Suzuki K Poulose
                   ` (2 subsequent siblings)
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

From: Steven Price <steven.price@arm.com>

The RMM doesn't allow injection of a undefined exception into a realm
guest. Add a WARN to catch if this ever happens.

Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/inject_fault.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index d6c4fc16f8795..d61a3ff04fabb 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -317,6 +317,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
  */
 void kvm_inject_undefined(struct kvm_vcpu *vcpu)
 {
+	KVM_BUG(vcpu_is_rec(vcpu), vcpu->kvm, "Unexpected undefined exception injection to REC");
 	if (vcpu_el1_is_32bit(vcpu))
 		inject_undef32(vcpu);
 	else
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 21/23] KVM: arm64: CCA: Support timers in realm RECs
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (19 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 20/23] KVM: arm64: CCA: WARN on injected undef exceptions Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  5:44   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization Suzuki K Poulose
  2026-10-05  9:07 ` [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms Suzuki K Poulose
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Suzuki K Poulose

From: Steven Price <steven.price@arm.com>

The RMM keeps track of the timer while the realm REC is running, but on
exit to the normal world KVM is responsible for handling the timers.

A later patch adds the support for propagating the timer values from the
exit data structure and making sure the values are in sync for KVM.

Also, RMM doesn't support injecting virtual interrupts backed by Physical
interrupts. So, use the existing software resampling mechanims for Realm
timer interrupts.

Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/arch_timer.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
index a45845f4ae4ea..428709f499205 100644
--- a/arch/arm64/kvm/arch_timer.c
+++ b/arch/arm64/kvm/arch_timer.c
@@ -56,11 +56,25 @@ static unsigned long kvm_arch_timer_get_irq_flags(void)
 	return kvm_vgic_global_state.no_hw_deactivation ? VGIC_IRQ_SW_RESAMPLE : 0;
 }
 
+static unsigned long kvm_realm_timer_get_irq_flags(void)
+{
+	/*
+	 * RMI_REC_ENTER rejects LRs with the HW bit set, so use the existing
+	 * software resampling mechanism for Realm timer interrupts.
+	 */
+	return VGIC_IRQ_SW_RESAMPLE;
+}
+
 static const struct irq_ops arch_timer_irq_ops = {
 	.get_flags	 = kvm_arch_timer_get_irq_flags,
 	.get_input_level = kvm_arch_timer_get_input_level,
 };
 
+static const struct irq_ops realm_timer_irq_ops = {
+	.get_flags	 = kvm_realm_timer_get_irq_flags,
+	.get_input_level = kvm_arch_timer_get_input_level,
+};
+
 static const struct irq_ops arch_timer_irq_ops_vgic_v5 = {
 	.get_input_level = kvm_arch_timer_get_input_level,
 	.queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock,
@@ -1617,8 +1631,12 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
 
 	get_timer_map(vcpu, &map);
 
-	ops = vgic_is_v5(vcpu->kvm) ? &arch_timer_irq_ops_vgic_v5 :
-				      &arch_timer_irq_ops;
+	if (vcpu_is_rec(vcpu))
+		ops = &realm_timer_irq_ops;
+	else if (vgic_is_v5(vcpu->kvm))
+		ops = &arch_timer_irq_ops_vgic_v5;
+	else
+		ops = &arch_timer_irq_ops;
 
 	for (int i = 0; i < nr_timers(vcpu); i++)
 		kvm_vgic_set_irq_ops(vcpu, timer_irq(vcpu_get_timer(vcpu, i)), ops);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (20 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 21/23] KVM: arm64: CCA: Support timers in realm RECs Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  5:35   ` Gavin Shan
  2026-10-05  9:07 ` [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms Suzuki K Poulose
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker, Suzuki K Poulose

From: Jean-Philippe Brucker <jean-philippe@linaro.org>

Userspace must configure the SVE vector length before the Realm is created
(as it is part of the parameter for Realm creation), but the Realm VCPUs
cannot be finalized until after the Realm Descriptor has been created.

KVM_GET_REG_LIST currently rejects the unfinalized VCPUs, which prevents
the userspace from discovering and configuring the VLs for  the Realm.

Allow KVM_GET_REG_LIST for unfinalized RECs and make the SVE register
enumeration handle the unfinalized case explicitly. i.e., only expose
KVM_REG_ARM64_SVE_VLS before SVE is finalized.

One adverse side effect of this change is that a KVM_GET_REG_LIST call that
only probes for the array size will now succeed even if SVE is not
finalized, but that seems harmless since the following KVM_GET_REG_LIST
with the full array will fail.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/arm.c   | 15 ++++++++++++++-
 arch/arm64/kvm/guest.c | 10 +++++-----
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index fe707a0c47308..d99e7818f5894 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -2004,6 +2004,19 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 	return __kvm_arm_vcpu_set_events(vcpu, events);
 }
 
+/*
+ * Realm VCPUs can be finalized only after the Realm descriptor is created.
+ * But in order to seal the SVE VL, we need to allow the userspace to read/write
+ * to the SVE_VL, before everything is finalized.
+ * Allow the register list for RECs before the VCPUs are finalized.
+ */
+static bool kvm_arm_vcpu_reg_list_allowed(struct kvm_vcpu *vcpu)
+{
+	if (kvm_arm_vcpu_is_finalized(vcpu))
+		return true;
+	return vcpu_is_rec(vcpu);
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -2059,7 +2072,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 			break;
 
 		r = -EPERM;
-		if (!kvm_arm_vcpu_is_finalized(vcpu))
+		if (!kvm_arm_vcpu_reg_list_allowed(vcpu))
 			break;
 
 		r = -EFAULT;
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index b01d6622b8720..c3ca369882273 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -598,8 +598,8 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
 	if (!vcpu_has_sve(vcpu))
 		return 0;
 
-	/* Policed by KVM_GET_REG_LIST: */
-	WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
+	if (!kvm_arm_vcpu_sve_finalized(vcpu))
+		return 1; /* KVM_REG_ARM64_SVE_VLS */
 
 	return slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */)
 		+ 1; /* KVM_REG_ARM64_SVE_VLS */
@@ -616,9 +616,6 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
 	if (!vcpu_has_sve(vcpu))
 		return 0;
 
-	/* Policed by KVM_GET_REG_LIST: */
-	WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
-
 	/*
 	 * Enumerate this first, so that userspace can save/restore in
 	 * the order reported by KVM_GET_REG_LIST:
@@ -628,6 +625,9 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
 		return -EFAULT;
 	++num_regs;
 
+	if (!kvm_arm_vcpu_sve_finalized(vcpu))
+		return num_regs;
+
 	for (i = 0; i < slices; i++) {
 		for (n = 0; n < SVE_NUM_ZREGS; n++) {
 			reg = KVM_REG_ARM64_SVE_ZREG(n, i);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms
  2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
                   ` (21 preceding siblings ...)
  2026-10-05  9:07 ` [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization Suzuki K Poulose
@ 2026-10-05  9:07 ` Suzuki K Poulose
  2026-10-06  5:47   ` Gavin Shan
  22 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-05  9:07 UTC (permalink / raw)
  To: kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, gshan, joey.gouly, tabba,
	yuzenghui, linux-coco, gankulkarni, sdonthineni, alpergun,
	fj0570is, WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker, Suzuki K Poulose

From: Jean-Philippe Brucker <jean-philippe@linaro.org>

The RMM restricts the access to the register states that the host can
read/modify for a given Realm.

e.g., At VCPU creation, can modify GPRS (x0-x30) and PC.
      While servicing SMCCC calls via RSI_HOST_CALL or servicing PSCI
      requests.
      MMIO emulation in the unprotected space.

Additionally we use the sysreg configuration to advertise/configure the
following Realm parameters, which are required before the Realm Descriptor
is created:
 - SVE Vector Length
 - Number of HW Breakpoints/Watchpoints
 - PMU Counters.

Thus KVM also additionally allows access to ID_AA64DFR0_EL1 and SVE_VLS for
the configuration of Realm creation parameters. We don't support PMUs for
the Realm VMs yet, so PMCR is not exposed.

The RMM makes similar restrictions for reading of the guest's registers
(this is *confidential* compute after all), however we don't impose the
restriction here. This allows the VMM to read (stale) values from the
registers which might be useful to read back the initial values even if
the RMM doesn't provide the latest version. For migration of a realm VM,
a new interface will be needed so that the VMM can receive an
(encrypted) blob of the VM's state.

Reflect the above in KVM_GET_REG_LIST, KVM_SET_ONE_REG calls.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Co-developed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Steven Price <steven.price@arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 arch/arm64/kvm/guest.c      | 63 +++++++++++++++++++++++++++++++++++++
 arch/arm64/kvm/hypercalls.c |  4 +--
 arch/arm64/kvm/sys_regs.c   | 28 +++++++++++++----
 3 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index c3ca369882273..ffe3f5ce4b3cf 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -73,6 +73,25 @@ static u64 core_reg_offset_from_id(u64 id)
 	return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
 }
 
+static bool kvm_realm_validate_core_reg(u64 off)
+{
+	/*
+	 * Note that GPRs can only sometimes be controlled by the VMM.
+	 * For PSCI only X0-X6 are used, higher registers are ignored (restored
+	 * from the REC).
+	 * For HOST_CALL all of X0-X30 are copied to the RsiHostCall structure.
+	 * For emulated MMIO X0 is always used.
+	 * PC can only be set before the realm is activated.
+	 */
+	switch (off) {
+	case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
+	     KVM_REG_ARM_CORE_REG(regs.regs[30]):
+	case KVM_REG_ARM_CORE_REG(regs.pc):
+		return true;
+	}
+	return false;
+}
+
 static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
 {
 	int size;
@@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,
 		u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
 		int size = core_reg_size_from_offset(vcpu, i);
 
+		if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
+			continue;
+
 		if (size < 0)
 			continue;
 
@@ -598,6 +620,9 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
 	if (!vcpu_has_sve(vcpu))
 		return 0;
 
+	if (kvm_vm_is_realm(vcpu->kvm))
+		return 1; /* KVM_REG_ARM64_SVE_VLS */
+
 	if (!kvm_arm_vcpu_sve_finalized(vcpu))
 		return 1; /* KVM_REG_ARM64_SVE_VLS */
 
@@ -625,6 +650,10 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
 		return -EFAULT;
 	++num_regs;
 
+	/* For Realms only support SVE_VLS */
+	if (kvm_vm_is_realm(vcpu->kvm))
+		return num_regs;
+
 	if (!kvm_arm_vcpu_sve_finalized(vcpu))
 		return num_regs;
 
@@ -705,6 +734,11 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
 		return -EINVAL;
 
+	/*
+	 * We don't filter out the register reads for Realms, like we do for
+	 * the user writes. We expose junk data for the VMM instead of
+	 * denying the requests.
+	 */
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
 	case KVM_REG_ARM_FW:
@@ -716,12 +750,41 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return kvm_arm_sys_reg_get_reg(vcpu, reg);
 }
 
+#define KVM_REG_ARM_ID_AA64DFR0_EL1	ARM64_SYS_REG(3, 0, 0, 5, 0)
+/*
+ * The RMI ABI only enables setting some GPRs and PC. The selection of GPRs
+ * that are available depends on the Realm state and the reason for the last
+ * exit.  All other registers are reset to architectural or otherwise defined
+ * reset values by the RMM, except for a few configuration fields that
+ * correspond to Realm parameters.
+ */
+static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,
+				   const struct kvm_one_reg *reg)
+{
+	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) {
+		u64 off = core_reg_offset_from_id(reg->id);
+
+		return kvm_realm_validate_core_reg(off);
+	}
+
+	switch (reg->id) {
+	case KVM_REG_ARM_ID_AA64DFR0_EL1:
+	case KVM_REG_ARM64_SVE_VLS:
+		return true;
+	}
+
+	return false;
+}
+
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	/* We currently use nothing arch-specific in upper 32 bits */
 	if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
 		return -EINVAL;
 
+	if (kvm_vm_is_realm(vcpu->kvm) && !validate_realm_set_reg(vcpu, reg))
+		return -EINVAL;
+
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
 	case KVM_REG_ARM_FW:
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
index b11b8821c9fbc..2b1e6fdeb4d5c 100644
--- a/arch/arm64/kvm/hypercalls.c
+++ b/arch/arm64/kvm/hypercalls.c
@@ -414,14 +414,14 @@ void kvm_arm_teardown_hypercalls(struct kvm *kvm)
 
 int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
 {
-	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
+	return vcpu_is_rec(vcpu) ? 0 : ARRAY_SIZE(kvm_arm_fw_reg_ids);
 }
 
 int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
+	for (i = 0; i < kvm_arm_get_fw_num_regs(vcpu); i++) {
 		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
 			return -EFAULT;
 	}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 44aae52c473d7..47ebde943a09e 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -5638,18 +5638,18 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
 				    sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
 }
 
-static unsigned int num_demux_regs(void)
+static inline unsigned int num_demux_regs(struct kvm_vcpu *vcpu)
 {
-	return CSSELR_MAX;
+	return vcpu_is_rec(vcpu) ? 0 : CSSELR_MAX;
 }
 
-static int write_demux_regids(u64 __user *uindices)
+static int write_demux_regids(struct kvm_vcpu *vcpu, u64 __user *uindices)
 {
 	u64 val = KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
 	unsigned int i;
 
 	val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
-	for (i = 0; i < CSSELR_MAX; i++) {
+	for (i = 0; i < num_demux_regs(vcpu); i++) {
 		if (put_user(val | i, uindices))
 			return -EFAULT;
 		uindices++;
@@ -5693,11 +5693,27 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
 	return true;
 }
 
+static inline bool kvm_realm_sys_reg_hidden_user(const struct kvm_vcpu *vcpu,
+						 u64 reg)
+{
+	if (!vcpu_is_rec(vcpu))
+		return false;
+
+	switch (reg) {
+	case SYS_ID_AA64DFR0_EL1:
+		return false;
+	}
+	return true;
+}
+
 static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
 			    const struct sys_reg_desc *rd,
 			    u64 __user **uind,
 			    unsigned int *total)
 {
+	if (kvm_realm_sys_reg_hidden_user(vcpu, reg_to_encoding(rd)))
+		return 0;
+
 	/*
 	 * Ignore registers we trap but don't save,
 	 * and for which no custom user accessor is provided.
@@ -5735,7 +5751,7 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
 
 unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
 {
-	return num_demux_regs()
+	return num_demux_regs(vcpu)
 		+ walk_sys_regs(vcpu, (u64 __user *)NULL);
 }
 
@@ -5748,7 +5764,7 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 		return err;
 	uindices += err;
 
-	return write_demux_regids(uindices);
+	return write_demux_regids(vcpu, uindices);
 }
 
 #define KVM_ARM_FEATURE_ID_RANGE_INDEX(r)			\
-- 
2.43.0


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0
  2026-10-05  9:07 ` [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0 Suzuki K Poulose
@ 2026-10-06  0:02   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  0:02 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Protected VMs doesn't allow setting offsets for virtual and physical
> counters, as the offset is always fixed to 0. The VM ioctl is filtered
> out based on the cap. However we don't prevent the userspace from trying
> to write to the CNTVCT/CNTPCT registers. This would lead to KVM triggering
> a WARN() in timer_set_offset() as the vm_offset pointer is set to NULL.
> 
> Fix this by always "fixing" the timer offsets to 0 and marking that the
> timer offset is set in the kvm->arch.flags at KVM init time for protected
> VMs. This prevents the access to the VM specific vm_offset at low cost.
> A userspace writing to the CNT*CT_EL0 would observe success, without
> any real effect. This is cleaner over spilling "*_is_protected()"
> checks and "matches" what we really do in practise. i.e., always run
> with "fixed counter offset of 0".
> 
> Reported by Sashiko
> 
> Link: https://lore.kernel.org/all/20260908164641.416911F00A3A@smtp.kernel.org
> Fixes: f7d05ee84a6a ("KVM: arm64: Prevent host from managing timer offsets for protected VMs")
> Suggested-by: Marc Zyngier <maz@kernel.org>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   Changes since v19:
>    - Fix typos in commit description and explain why we choose the approach.
>    - Improve comment in the code
>   Changes since v18:
>    - Retain NULL vm_offset for protected VMs to avoid host tampering with the
>      offset.
>    - Moved the flag setting into kvm_timer_init_vm(), where it should have been
>      in the first place
> ---
>   arch/arm64/kvm/arch_timer.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 02/23] KVM: arm64: Disable Steal time accounting for protected guests
  2026-10-05  9:07 ` [PATCH v22 02/23] KVM: arm64: Disable Steal time accounting for protected guests Suzuki K Poulose
@ 2026-10-06  0:03   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  0:03 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Fuad Tabba

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> PVTIME support is advertised by KVM_CAP_STEAL_TIME, which doesn't take into
> account the kvm instance. Even with that, a VMM could skip the CAP check
> and proceed to configure the PVTIME as we don't do further check on the
> DEVICE_CTRL. Tighten this up by passing the KVM instance around wherever
> possible and catch things early.
> 
> Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/include/asm/kvm_host.h |  2 +-
>   arch/arm64/kvm/arm.c              |  2 +-
>   arch/arm64/kvm/pvtime.c           | 14 +++++++-------
>   3 files changed, 9 insertions(+), 9 deletions(-)
> 
Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 06/23] KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host
  2026-10-05  9:07 ` [PATCH v22 06/23] KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host Suzuki K Poulose
@ 2026-10-06  0:29   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  0:29 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> vcpu_set_pauth_traps() bails out and does nothing for pKVM hosts.
> Clean this up by moving the is_protected_kvm_enabled() check to the
> caller, in preparation for adding VM specific vcpu load/put callbacks.
> 
> While at it, do an early return if the vcpu doesn't have ptrauth.
> 
> No functional changes
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v19:
>   - New patch, since addition of this to the Refactoring patch makes it a bit
>     more bigger and harder to review
> ---
>   arch/arm64/kvm/arm.c | 52 +++++++++++++++++++++++---------------------
>   1 file changed, 27 insertions(+), 25 deletions(-)
> 

Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 08/23] KVM: arm64: Add vcpu load/put call backs for flavors
  2026-10-05  9:07 ` [PATCH v22 08/23] KVM: arm64: Add vcpu load/put call backs for flavors Suzuki K Poulose
@ 2026-10-06  2:15   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  2:15 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Add VM flavor specific handlers for VCPU load/put, in an effort to make it
> easier to follow the code. pauth traps were removed from VMs running PKVM
> as it is a no-op for them.
> 
> Based on a patch by Marc Zyngier
> 
> Suggested-by: Marc Zyngier <maz@kernel.org>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v21:
>   - Add '&' into the KVM_VCPU_OPS() macro
> ---
>   arch/arm64/include/asm/kvm_host.h |   6 ++
>   arch/arm64/kvm/arm.c              | 138 +++++++++++++++++++++++-------
>   2 files changed, 114 insertions(+), 30 deletions(-)
> 
Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types
  2026-10-05  9:07 ` [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types Suzuki K Poulose
@ 2026-10-06  2:24   ` Gavin Shan
  2026-10-06  2:25   ` Gavin Shan
  2026-10-06  8:50   ` Marc Zyngier
  2 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  2:24 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Prevent unsupported VCPU features for the protected VCPUs. Realms and pVMs
> not support 32bit EL1 or NV yet. pKVM doesn't rely on the host vcpu
> features and it clears the unsupported features while hyp_vcpu is
> initialised. Block the features early in the vcpu init if we detect
> incompatible features.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/arm.c | 10 ++++++----
>   1 file changed, 6 insertions(+), 4 deletions(-)
> 

One nitpick below. In either way:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index f31d31fa27ad9..9c2ef7ca6a961 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -1668,11 +1668,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
>   	return -EINVAL;
>   }
>   
> -static unsigned long system_supported_vcpu_features(void)
> +static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
>   {
>   	unsigned long features = KVM_VCPU_VALID_FEATURES;
>   
> -	if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
> +	if (vcpu_is_protected(vcpu) ||
> +	    !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
>   		clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
>   
>   	if (!kvm_supports_guest_pmuv3()) {
> @@ -1688,7 +1689,8 @@ static unsigned long system_supported_vcpu_features(void)
>   		clear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features);
>   	}
>   
> -	if (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
> +	if (vcpu_is_protected(vcpu) ||
> +	    !cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
>   		clear_bit(KVM_ARM_VCPU_HAS_EL2, &features);
>   
>   	return features;
> @@ -1708,7 +1710,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
>   			return -ENOENT;
>   	}
>   
> -	if (features & ~system_supported_vcpu_features())
> +	if (features & ~system_supported_vcpu_features(vcpu))
>   		return -EINVAL;
>  

The following check at the beginning of kvm_vcpu_init_check_features() is redundant to
the check here. We can drop that in this patch or a preparatory patch.

static int kvm_vcpu_init_check_features(...)
{
        /*
         * This check can be dropped since the same check has been done
	* by the followup "if (features & ~system_supported_vcpu_features(vcpu))"
         */
        if (features & ~KVM_VCPU_VALID_FEATURES)
                 return -ENOENT;
}

  
>   	/*

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types
  2026-10-05  9:07 ` [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types Suzuki K Poulose
  2026-10-06  2:24   ` Gavin Shan
@ 2026-10-06  2:25   ` Gavin Shan
  2026-10-06  5:16     ` Suzuki K Poulose
  2026-10-06  8:50   ` Marc Zyngier
  2 siblings, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  2:25 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Prevent unsupported VCPU features for the protected VCPUs. Realms and pVMs
> not support 32bit EL1 or NV yet. pKVM doesn't rely on the host vcpu
> features and it clears the unsupported features while hyp_vcpu is
> initialised. Block the features early in the vcpu init if we detect
> incompatible features.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/arm.c | 10 ++++++----
>   1 file changed, 6 insertions(+), 4 deletions(-)
> 

One nitpick below. In either way:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index f31d31fa27ad9..9c2ef7ca6a961 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -1668,11 +1668,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
>   	return -EINVAL;
>   }
>   
> -static unsigned long system_supported_vcpu_features(void)
> +static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
>   {
>   	unsigned long features = KVM_VCPU_VALID_FEATURES;
>   
> -	if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
> +	if (vcpu_is_protected(vcpu) ||
> +	    !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
>   		clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
>   
>   	if (!kvm_supports_guest_pmuv3()) {
> @@ -1688,7 +1689,8 @@ static unsigned long system_supported_vcpu_features(void)
>   		clear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features);
>   	}
>   
> -	if (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
> +	if (vcpu_is_protected(vcpu) ||
> +	    !cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
>   		clear_bit(KVM_ARM_VCPU_HAS_EL2, &features);
>   
>   	return features;
> @@ -1708,7 +1710,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
>   			return -ENOENT;
>   	}
>   
> -	if (features & ~system_supported_vcpu_features())
> +	if (features & ~system_supported_vcpu_features(vcpu))
>   		return -EINVAL;
>  

The following check at the beginning of kvm_vcpu_init_check_features() is redundant to
the check here. We can drop that in this patch or a preparatory patch.

static int kvm_vcpu_init_check_features(...)
{
        /*
         * This check can be dropped since the same check has been done
	* by the followup "if (features & ~system_supported_vcpu_features(vcpu))"
         */
        if (features & ~KVM_VCPU_VALID_FEATURES)
                 return -ENOENT;
}

  
>   	/*

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 10/23] KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range
  2026-10-05  9:07 ` [PATCH v22 10/23] KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range Suzuki K Poulose
@ 2026-10-06  2:37   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  2:37 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> In preparation for adding VM specific backends for stage2 operations,
> nuke __unmap_stage2_range() and fold the logic into
> kvm_stage2_unmap_range(). Also, make kvm_unmap_gfn_range(), the only other
> user of the __unmap_stage2_range() call the kvm_stage2_unmap_range(). Also,
> while at it fix the comment to make it clear that mmu_lock must always be
> held while unmapping. The code already mandates that and the two call paths
> do have the write mmu_lock held.
> 
> Later we would replace the logic in kvm_stage2_unmap_range() with VM
> specific backends.
> 
> No functional changes intended.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Change since v21:
>   - Nuke __unmap_stage2_range and consolidate the stage2_unmap_range logic
>     into kvm_stage2_unmap_range(), in preparation for removing the KVM_PGT_FN()
>     for stage2_unmap
> ---
>   arch/arm64/kvm/mmu.c | 38 ++++++++++++++++----------------------
>   1 file changed, 16 insertions(+), 22 deletions(-)
> 

Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations
  2026-10-05  9:07 ` [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations Suzuki K Poulose
@ 2026-10-06  3:00   ` Gavin Shan
  2026-10-06  5:22     ` Suzuki K Poulose
  2026-10-06  9:24   ` Marc Zyngier
  1 sibling, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:00 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Add VM type specific S2 MMU operation backends which can be initialized per
> VM flavor, to keep the handling cleaner.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Change since v21:
>   - Define all vm_s2_ops call back. All calls are mandatory.
>   - Define callback for each flavor, disjointing the non-protetcted pKVM and
>     normal KVM (VHE & nVHE) and remove the KVM_PGT_FN() hacks.
>   - Dropped Reviews due to the changes.
>   - Add "no_age_gfn" and "no_stage2_unmap_range" for pKVM callbacks, no_age_*
>     to be also reused by Realms later.
>   - Move kvm_vm_s2_ops field to keep the structure packed
> ---
>   arch/arm64/include/asm/kvm_host.h |  14 +++
>   arch/arm64/kvm/mmu.c              | 173 +++++++++++++++++++++++++-----
>   2 files changed, 160 insertions(+), 27 deletions(-)
> 

With the following nitpicks addressed:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 839f5e9c7c65e..0778c308ce597 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -155,6 +155,19 @@ struct kvm_vcpu_ops {
>   	void (*vcpu_put)(struct kvm_vcpu *vcpu);
>   };
>   
> +struct kvm_gfn_range;
> +
> +struct kvm_vm_s2_ops {
> +	bool (*vm_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
> +	bool (*vm_test_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
> +	int (*vm_flush_remote_tlbs)(struct kvm *kvm);
> +	int (*vm_flush_remote_tlbs_range)(struct kvm *kvm, gfn_t gfn,
> +					  u64 nr_pages);
> +	void (*vm_stage2_unmap_range)(struct kvm_s2_mmu *mmu,
> +				      phys_addr_t start, u64 size,
> +				      bool may_block);
> +};
> +
>   struct kvm_s2_mmu {
>   	struct kvm_vmid vmid;
>   
> @@ -321,6 +334,7 @@ enum kvm_arm_vm_flavor {
>   
>   struct kvm_arch {
>   	struct kvm_s2_mmu mmu;
> +	const struct kvm_vm_s2_ops *vm_s2_ops;
>   
>   	enum kvm_arm_vm_flavor vm_flavor;
>   	/* Mandated version of PSCI */
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 4ae3bb6baef1a..c046c76833876 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -37,6 +37,8 @@ static unsigned long __ro_after_init io_map_base;
>   
>   #define KVM_PGT_FN(fn)		(!is_protected_kvm_enabled() ? fn : p ## fn)
>   
> +static int kvm_vm_init_vm_s2_ops(struct kvm *kvm);
> +
>   static phys_addr_t __stage2_range_addr_end(phys_addr_t addr, phys_addr_t end,
>   					   phys_addr_t size)
>   {
> @@ -166,6 +168,18 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
>   	return memslot->dirty_bitmap && !(memslot->flags & KVM_MEM_READONLY);
>   }
>   
> +static int pkvm_flush_remote_tlbs(struct kvm *kvm)
> +{
> +	kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
> +	return 0;
> +}
> +
> +static int kvm_vm_flush_remote_tlbs(struct kvm *kvm)
> +{
> +	kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
> +	return 0;
> +}
> +
>   /**
>    * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8
>    * @kvm:	pointer to kvm structure.
> @@ -174,26 +188,31 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
>    */
>   int kvm_arch_flush_remote_tlbs(struct kvm *kvm)
>   {
> -	if (is_protected_kvm_enabled())
> -		kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
> -	else
> -		kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
> -	return 0;
> +	return kvm->arch.vm_s2_ops->vm_flush_remote_tlbs(kvm);
>   }
>   
> -int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
> -				      gfn_t gfn, u64 nr_pages)
> +static int pkvm_flush_remote_tlbs_range(struct kvm *kvm,
> +					gfn_t gfn, u64 nr_pages)
> +{
> +	return pkvm_flush_remote_tlbs(kvm);

No need to have another function call, which causes unnecessary overhead?

	kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
	return 0;

> +}
> +
> +static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,
> +					 gfn_t gfn, u64 nr_pages)
>   {
>   	u64 size = nr_pages << PAGE_SHIFT;
>   	u64 addr = gfn << PAGE_SHIFT;
>   
> -	if (is_protected_kvm_enabled())
> -		kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
> -	else
> -		kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
> +	kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
>   	return 0;
>   }
>  

Since we're here, the local variable 'addr' and 'size' can be dropped by:

	kvm_tlb_flush_vmid_range(&kvm->arch.mmu,
                                  gfn << PAGE_SHIFT,
                                  nr_pages << PAGE_SHIFT);

  
> +int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
> +				     gfn_t gfn, u64 nr_pages)
> +{
> +	return kvm->arch.vm_s2_ops->vm_flush_remote_tlbs_range(kvm, gfn, nr_pages);
> +}
> +
>   static void *stage2_memcache_zalloc_page(void *arg)
>   {
>   	struct kvm_mmu_memory_cache *mc = arg;
> @@ -289,6 +308,27 @@ static void invalidate_icache_guest_page(void *va, size_t size)
>   	__invalidate_icache_guest_page(va, size);
>   }
>   
> +static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
> +				      phys_addr_t start,
> +				      u64 size, bool may_block)
> +{
> +	WARN_ON(stage2_apply_range(mmu, start, start + size,
> +				   kvm_pgtable_stage2_unmap, may_block));
> +}
> +
> +static void pkvm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
> +				    phys_addr_t start,
> +				    u64 size, bool may_block)
> +{
> +	WARN_ON(stage2_apply_range(mmu, start, start + size,
> +				   pkvm_pgtable_stage2_unmap, may_block));
> +}
> +
> +static void no_stage2_unmap_range(struct kvm_s2_mmu *mmu,
> +				  phys_addr_t start, u64 size, bool may_block)
> +{
> +}
> +
>   /*
>    * Unmapping vs dcache management:
>    *
> @@ -329,15 +369,10 @@ void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
>   {
>   	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
>   
> -	if (kvm_vm_is_protected(kvm))
> -		return;
> -
>   	lockdep_assert_held_write(&kvm->mmu_lock);
>   	WARN_ON(size & ~PAGE_MASK);
>   
> -	WARN_ON(stage2_apply_range(mmu, start, start + size,
> -				   KVM_PGT_FN(kvm_pgtable_stage2_unmap),
> -				   may_block));
> +	kvm->arch.vm_s2_ops->vm_stage2_unmap_range(mmu, start, size, may_block);
>   }
>   
>   void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
> @@ -977,6 +1012,12 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
>   	int cpu, err;
>   	struct kvm_pgtable *pgt;
>   
> +	/* Initialize the VM ops for the VM instance for the first time */
> +	if (mmu == &kvm->arch.mmu) {
> +		err = kvm_vm_init_vm_s2_ops(kvm);
> +		if (err)
> +			return err;
> +	}
>   	/*
>   	 * If we already have our page tables in place, and that the
>   	 * MMU context is the canonical one, we have a bug somewhere,
> @@ -2441,34 +2482,68 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
>   	return false;
>   }
>   
> -bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +static bool kvm_vm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
>   {
>   	u64 size = (range->end - range->start) << PAGE_SHIFT;
>   
> -	if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
> -		return false;
> -
> -	return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
> +	return kvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
>   						   range->start << PAGE_SHIFT,
>   						   size, true);
> +}
> +
> +static bool pkvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +{
> +	u64 size = (range->end - range->start) << PAGE_SHIFT;
> +
> +	return pkvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
> +						    range->start << PAGE_SHIFT,
> +						    size, true);
> +}
> +
> +static bool no_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +{
> +	/* The hypervisor doesn't support aging */
> +	return false;
> +}
> +
> +bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +{
> +	if (!kvm->arch.mmu.pgt)
> +		return false;
> +
> +	return kvm->arch.vm_s2_ops->vm_age_gfn(kvm, range);
>   	/*
>   	 * TODO: Handle nested_mmu structures here using the reverse mapping in
>   	 * a later version of patch series.
>   	 */
>   }
>   
> -bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +static bool kvm_vm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
>   {
>   	u64 size = (range->end - range->start) << PAGE_SHIFT;
>   
> -	if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm))
> -		return false;
> -
> -	return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
> +	return kvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
>   						   range->start << PAGE_SHIFT,
>   						   size, false);
>   }
>   
> +static bool pkvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +{
> +	u64 size = (range->end - range->start) << PAGE_SHIFT;
> +
> +	return pkvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
> +						    range->start << PAGE_SHIFT,
> +						    size, false);
> +}
> +
> +bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> +{
> +	if (!kvm->arch.mmu.pgt)
> +		return false;
> +
> +	return kvm->arch.vm_s2_ops->vm_test_age_gfn(kvm, range);
> +}
> +
>   phys_addr_t kvm_mmu_get_httbr(void)
>   {
>   	return __pa(hyp_pgtable->pgd);
> @@ -2790,3 +2865,47 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)
>   
>   	trace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);
>   }
> +
> +static const struct kvm_vm_s2_ops protected_pkvm_vm_s2_ops = {
> +	.vm_flush_remote_tlbs		= pkvm_flush_remote_tlbs,
> +	.vm_flush_remote_tlbs_range	= pkvm_flush_remote_tlbs_range,
> +	.vm_age_gfn			= no_age_gfn,
> +	.vm_test_age_gfn		= no_age_gfn,
> +	.vm_stage2_unmap_range		= no_stage2_unmap_range,
> +};
> +
> +static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {
> +	.vm_flush_remote_tlbs		= pkvm_flush_remote_tlbs,
> +	.vm_flush_remote_tlbs_range	= pkvm_flush_remote_tlbs_range,
> +	.vm_age_gfn			= pkvm_age_gfn,
> +	.vm_test_age_gfn		= pkvm_test_age_gfn,
> +	.vm_stage2_unmap_range		= pkvm_stage2_unmap_range,
> +};
> +
> +static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
> +	.vm_flush_remote_tlbs		= kvm_vm_flush_remote_tlbs,
> +	.vm_flush_remote_tlbs_range	= kvm_vm_flush_remote_tlbs_range,
> +	.vm_age_gfn			= kvm_vm_age_gfn,
> +	.vm_test_age_gfn		= kvm_vm_test_age_gfn,
> +	.vm_stage2_unmap_range		= kvm_vm_stage2_unmap_range,
> +};
> +
> +#define KVM_VM_S2_OPS(flavor, ops)		\
> +		[flavor] = &(ops)

s/[flavor]/[(flavor)]

> +
> +static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {
> +	KVM_VM_S2_OPS(VM_VHE, kvm_default_vm_s2_ops),
> +	KVM_VM_S2_OPS(VM_NVHE, kvm_default_vm_s2_ops),
> +	KVM_VM_S2_OPS(VM_PKVM, pkvm_vm_s2_ops),
> +	KVM_VM_S2_OPS(VM_PROTECTED_PKVM, protected_pkvm_vm_s2_ops),
> +};
> +
> +static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)
> +{
> +	BUILD_BUG_ON(ARRAY_SIZE(arm64_vm_s2_ops) != VM_FLAVOR_MAX);
> +
> +	kvm->arch.vm_s2_ops = arm64_vm_s2_ops[kvm->arch.vm_flavor];
> +	if (WARN_ON(!kvm->arch.vm_s2_ops))
> +		return -EINVAL;
> +	return 0;
> +}

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 12/23] KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort()
  2026-10-05  9:07 ` [PATCH v22 12/23] KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort() Suzuki K Poulose
@ 2026-10-06  3:02   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:02 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Fuad Tabba

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> kvm_handle_guest_abort() repeatedly obtains the VM from vcpu->kvm.
> 
> Introduce a local kvm pointer and use it throughout the function. While
> touching the code, fix the missing whitespace in the
> kvm_is_nested_s2_mmu() call.
> 
> Suggested-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/mmu.c | 13 +++++++------
>   1 file changed, 7 insertions(+), 6 deletions(-)
> 

Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling
  2026-10-05  9:07 ` [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling Suzuki K Poulose
@ 2026-10-06  3:07   ` Gavin Shan
  2026-10-06  5:25     ` Suzuki K Poulose
  0 siblings, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:07 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Move the memory abort handling under VM specific s2 operation.
> 
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v21:
>   - Rename protected_vm_mem_abort => protected_pkvm_mem_abort for consistency
>     with the other callbacks.
> ---
>   arch/arm64/include/asm/kvm_host.h |  2 ++
>   arch/arm64/kvm/mmu.c              | 33 ++++++++++++++++++-------------
>   2 files changed, 21 insertions(+), 14 deletions(-)
> 

One nitpick below. In either way:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 0778c308ce597..7cb343583953d 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -156,6 +156,7 @@ struct kvm_vcpu_ops {
>   };
>   
>   struct kvm_gfn_range;
> +struct kvm_s2_fault_desc;
>   
>   struct kvm_vm_s2_ops {
>   	bool (*vm_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
> @@ -166,6 +167,7 @@ struct kvm_vm_s2_ops {
>   	void (*vm_stage2_unmap_range)(struct kvm_s2_mmu *mmu,
>   				      phys_addr_t start, u64 size,
>   				      bool may_block);
> +	int (*vm_mem_abort)(const struct kvm_s2_fault_desc *s2fd);
>   };
>   
>   struct kvm_s2_mmu {
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 9cc7fcde4dd0a..ccbe2ffeda40b 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1740,7 +1740,7 @@ struct kvm_s2_fault_vma_info {
>   	bool		map_non_cacheable;
>   };
>   
> -static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
> +static int protected_pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>   {
>   	unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
>   	struct kvm_vcpu *vcpu = s2fd->vcpu;
> @@ -2178,6 +2178,20 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>   	return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache);
>   }
>   
> +static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
> +{
> +	struct kvm_vcpu *vcpu = s2fd->vcpu;
> +
> +	VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
> +			!kvm_is_write_fault(vcpu) &&
> +			!kvm_vcpu_trap_is_exec_fault(vcpu));
> +
> +	if (kvm_slot_has_gmem(s2fd->memslot))
> +		return gmem_abort(s2fd);
> +	else
> +		return user_mem_abort(s2fd);
> +}

The "if...else" block is moved from kvm_handle_guest_abort(), but it might be
clearer if we have:

	if (kvm_slot_has_gmem(s2fd->memslot))
		return gmem_abort(s2fd);

	return user_mem_abort(s2fd);

> +
>   /* Resolve the access fault by making the page young again. */
>   static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
>   {
> @@ -2447,19 +2461,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>   		.hva		= hva,
>   	};
>   
> -	if (kvm_vm_is_protected(kvm)) {
> -		ret = pkvm_mem_abort(&s2fd);
> -	} else {
> -		VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
> -				!write_fault &&
> -				!kvm_vcpu_trap_is_exec_fault(vcpu));
> -
> -		if (kvm_slot_has_gmem(memslot))
> -			ret = gmem_abort(&s2fd);
> -		else
> -			ret = user_mem_abort(&s2fd);
> -	}
> -
> +	ret = kvm->arch.vm_s2_ops->vm_mem_abort(&s2fd);
>   	if (ret == 0)
>   		ret = 1;
>   out:
> @@ -2873,6 +2875,7 @@ static const struct kvm_vm_s2_ops protected_pkvm_vm_s2_ops = {
>   	.vm_age_gfn			= no_age_gfn,
>   	.vm_test_age_gfn		= no_age_gfn,
>   	.vm_stage2_unmap_range		= no_stage2_unmap_range,
> +	.vm_mem_abort			= protected_pkvm_mem_abort,
>   };
>   
>   static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {
> @@ -2881,6 +2884,7 @@ static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {
>   	.vm_age_gfn			= pkvm_age_gfn,
>   	.vm_test_age_gfn		= pkvm_test_age_gfn,
>   	.vm_stage2_unmap_range		= pkvm_stage2_unmap_range,
> +	.vm_mem_abort			= kvm_vm_mem_abort,
>   };
>   
>   static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
> @@ -2889,6 +2893,7 @@ static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
>   	.vm_age_gfn			= kvm_vm_age_gfn,
>   	.vm_test_age_gfn		= kvm_vm_test_age_gfn,
>   	.vm_stage2_unmap_range		= kvm_vm_stage2_unmap_range,
> +	.vm_mem_abort			= kvm_vm_mem_abort,
>   };
>   
>   #define KVM_VM_S2_OPS(flavor, ops)		\

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 14/23] KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms
  2026-10-05  9:07 ` [PATCH v22 14/23] KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms Suzuki K Poulose
@ 2026-10-06  3:10   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:10 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Fuad Tabba

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> pKVM does not trust the host. Realm VMs follow a similar trust model, with
> the Realm Management Monitor owning the protected state instead of the
> host. Add a helper to identify VMs that run under a host-distrusting
> hypervisor.
> 
> Use this for blocking ioremap of vgic-v2 into stage2 and prevent creation
> of VGIC other than v3.
> 
> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/include/asm/kvm_host.h | 4 ++++
>   arch/arm64/kvm/mmu.c              | 2 +-
>   arch/arm64/kvm/vgic/vgic-init.c   | 2 ++
>   3 files changed, 7 insertions(+), 1 deletion(-)
> 
Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 15/23] KVM: arm64: CCA: Add a new mode for supporting Realm guests
  2026-10-05  9:07 ` [PATCH v22 15/23] KVM: arm64: CCA: Add a new mode for supporting Realm guests Suzuki K Poulose
@ 2026-10-06  3:11   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:11 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Add an explicit mode to support Arm CCA guests.
> 
> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   Documentation/admin-guide/kernel-parameters.txt | 3 +++
>   arch/arm64/include/asm/kvm_host.h               | 1 +
>   arch/arm64/kvm/arm.c                            | 5 +++++
>   3 files changed, 9 insertions(+)
> 

Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms
  2026-10-05  9:07 ` [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms Suzuki K Poulose
@ 2026-10-06  3:16   ` Gavin Shan
  2026-10-06  5:09     ` Suzuki K Poulose
  0 siblings, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:16 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> RMM controls the VCPU settings and most are hidden from the KVM, except
> for the VGIC and timer bits.
> 
> A later patch would add syncing the VCPU state into the Realm REC related
> SMC parameters.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/arm.c | 18 ++++++++++++++++++
>   1 file changed, 18 insertions(+)
> 
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 6c81fec35ecec..294cea4cc8271 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -802,6 +802,13 @@ static void pkvm_vcpu_load(struct kvm_vcpu *vcpu)
>   			  &vcpu->arch.vgic_cpu.vgic_v3);
>   }
>   
> +static void realm_vcpu_load(struct kvm_vcpu *vcpu)
> +{
> +	kvm_timer_vcpu_load(vcpu);
> +	kvm_vgic_load(vcpu);
> +	vcpu_set_wfx_traps(vcpu);

Why we need to call vcpu_set_wfx_traps(), which updates 'vcpu->arch.hcr_el2'? I don't
see how 'vcpu->arch.hcr_el2' is affects realm in RMM. Could you please explain in the
commit log.

> +}
> +
>   void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
>   {
>   	vcpu->cpu = cpu;
> @@ -854,6 +861,12 @@ static void pkvm_vcpu_put(struct kvm_vcpu *vcpu)
>   	kvm_vcpu_pmu_restore_host(vcpu);
>   }
>   
> +static void realm_vcpu_put(struct kvm_vcpu *vcpu)
> +{
> +	kvm_timer_vcpu_put(vcpu);
> +	kvm_vgic_put(vcpu);
> +}
> +
>   void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
>   {
>   	vcpu->arch.vcpu_ops->vcpu_put(vcpu);
> @@ -2213,6 +2226,11 @@ static const struct kvm_vcpu_ops pkvm_vcpu_ops = {
>   	.vcpu_put = pkvm_vcpu_put,
>   };
>   
> +static const struct kvm_vcpu_ops realm_vcpu_ops = {
> +	.vcpu_load = realm_vcpu_load,
> +	.vcpu_put = realm_vcpu_put,
> +};
> +
>   #define KVM_VCPU_OPS(flavor, ops)		\
>   	[(flavor)] = &(ops)
>   

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 17/23] KVM: arm64: CCA: Add bare minimal S2 operations for Realm
  2026-10-05  9:07 ` [PATCH v22 17/23] KVM: arm64: CCA: Add bare minimal S2 operations for Realm Suzuki K Poulose
@ 2026-10-06  3:18   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:18 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Add bare minimal MMU operation hooks for Realms. The mem_abort handling
> is chosen as the default KVM variant. However this cannot be reached for
> Realms yet and we would need real RMI command support to make it fully
> functional. Similarly, unmapping stage2 requires RMI command support.
> Until then reuse follow protected pKVM hook and ignore unmapping.
> This will be addressed in the later series.
> 
> RMM takes care of the TLB flushing as required, when the Stage2 is
> modified. So host doesn't need to do anything explicitly. RMM doesn't
> support access flags for the stage2, even for the shared IPA.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v21:
>   - Reuse no_age_gfn for Realms. Also use no_stage2_unmap_range for
>     now, until we get proper RMI backed driver.
> ---
>   arch/arm64/kvm/mmu.c | 23 +++++++++++++++++++++++
>   1 file changed, 23 insertions(+)
> 

Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms
  2026-10-05  9:07 ` [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms Suzuki K Poulose
@ 2026-10-06  3:42   ` Gavin Shan
  2026-10-06  5:10     ` Suzuki K Poulose
  0 siblings, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:42 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
> 
> Add foundational work for supporting Realms.
>   - Add a new VM flavor.
>   - At KVM init, check if the KVM can support Realms (though not functional
>     yet) and will be advertised by static key kvm_rmi_is_available. This
>     will be turned on in a later patches, once we have all the bits and
>     pieces ready. For now check if we are blessed with KVM_MODE_RMM.
>   - Add realm specific tracking in kvm_arch. Since Realm and protected pKVM
>     states are mutually exclusive, move them into a union.
> 
> Please note that we cannot create Realm VMs yet. This requires further
> changes to the UABI and core RMI driver support, which will come later.
> 
> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/include/asm/kvm_emulate.h | 16 ++++++++
>   arch/arm64/include/asm/kvm_host.h    | 19 ++++++---
>   arch/arm64/include/asm/kvm_rmi.h     | 61 ++++++++++++++++++++++++++++
>   arch/arm64/include/asm/virt.h        |  1 +
>   arch/arm64/kvm/Makefile              |  2 +-
>   arch/arm64/kvm/arm.c                 |  6 +++
>   arch/arm64/kvm/mmu.c                 |  1 +
>   arch/arm64/kvm/rmi.c                 | 18 ++++++++
>   8 files changed, 118 insertions(+), 6 deletions(-)
>   create mode 100644 arch/arm64/include/asm/kvm_rmi.h
>   create mode 100644 arch/arm64/kvm/rmi.c
> 
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index a3c1928bdf743..d360a8b05b8bf 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -793,4 +793,20 @@ static inline void kvm_reset_vcpu_psci(struct kvm_vcpu *vcpu,
>   	vcpu_set_reg(vcpu, 0, reset_state->r0);
>   }
>   
> +static inline enum realm_state kvm_realm_state(struct kvm *kvm)
> +{
> +	return READ_ONCE(kvm->arch.realm.state);
> +}
> +
> +static inline void kvm_set_realm_state(struct kvm *kvm,
> +				       enum realm_state new_state)
> +{
> +	WRITE_ONCE(kvm->arch.realm.state, new_state);
> +}
> +
> +static inline bool kvm_realm_is_created(struct kvm *kvm)
> +{
> +	return kvm_vm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
> +}
> +
>   #endif /* __ARM64_KVM_EMULATE_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index dfa9d4ec61a76..3debffef638a4 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -27,6 +27,7 @@
>   #include <asm/fpsimd.h>
>   #include <asm/kvm.h>
>   #include <asm/kvm_asm.h>
> +#include <asm/kvm_rmi.h>
>   #include <asm/vncr_mapping.h>
>   
>   #define __KVM_HAVE_ARCH_INTC_INITIALIZED
> @@ -334,6 +335,7 @@ enum kvm_arm_vm_flavor {
>   	VM_PKVM,		/* Normal guests on pKVM */
>   	MARKER(__VM_PROTECTED),
>   	VM_PROTECTED_PKVM,	/* Protected VM */
> +	VM_REALM,		/* CCA */
>   	VM_FLAVOR_MAX
>   };
>   
> @@ -450,11 +452,14 @@ struct kvm_arch {
>   	/* Count the number of VNCR_EL2 TLBs */
>   	atomic_t vncr_tlb_count;
>   
> -	/*
> -	 * For an untrusted host VM, 'pkvm.handle' is used to lookup
> -	 * the associated pKVM instance in the hypervisor.
> -	 */
> -	struct kvm_protected_vm pkvm;
> +	union {
> +		/*
> +		 * For an untrusted host VM, 'pkvm.handle' is used to lookup
> +		 * the associated pKVM instance in the hypervisor.
> +		 */
> +		struct kvm_protected_vm pkvm;
> +		struct realm realm;
> +	};
>   
>   #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
>   	/* Nested virtualization info */
> @@ -1565,6 +1570,10 @@ struct kvm *kvm_arch_alloc_vm(void);
>   		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
>   	})
>   
> +
> +#define kvm_vm_is_realm(kvm)		((kvm)->arch.vm_flavor == VM_REALM)
> +#define vcpu_is_rec(vcpu)		kvm_vm_is_realm((vcpu)->kvm)
> +

vcpu_is_rec() isn't safely called in nVHE hyp stub. So do we need add something
similiar to what we had for vcpu_is_protected(vcpu) in PATCH[05]?

#ifdef __KVM_NVHE_HYPERVISOR__
/* vcpu_is_rec() isn't expected to called in nVHE hyp stub */
#define vcpu_is_rec(vcpu)	BUILD_BUG_ON(1)
#else
#define vcpu_is_rec(vcpu)	kvm_vm_is_realm((vcpu)->kvm)
#endif


>   #define kvm_vm_hyp_is_distrusting(kvm)	((kvm)->arch.vm_flavor >= __VM_DISTRUSTING_HYP)
>   
>   int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> new file mode 100644
> index 0000000000000..44f5c75a27b5b
> --- /dev/null
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023-2026 ARM Ltd.
> + */
> +
> +#ifndef __ASM_KVM_RMI_H
> +#define __ASM_KVM_RMI_H
> +
> +/**
> + * enum realm_state - State of a Realm
> + *
> + * Mirrors the RMM's Realm lifecycle states where they are meaningful to KVM,
> + * with REALM_STATE_DYING being a KVM-internal state used to prevent further
> + * requests while teardown is in progress. KVM does not track REALM_SYSTEM_OFF
> + * or REALM_ZOMBIE separately as they naturally lead to teardown.
> + */
> +enum realm_state {
> +	/**
> +	 * @REALM_STATE_NONE:
> +	 *      Realm has not yet been created. rmi_realm_create() has not
> +	 *      yet been called.
> +	 */
> +	REALM_STATE_NONE,
> +	/**
> +	 * @REALM_STATE_NEW:
> +	 *      Realm is under construction, rmi_realm_create() has been
> +	 *      called, but it is not yet activated. Pages may be populated.
                                                      ^^^^^
Granule instead of page is the term applicable to RMM. Lets use a generic
term 'memory' here. Also, lets mention the name of the function used to
populate the memory (granules), which is consistent to the comments for
REALM_STATE_ACTIVE.

	Memory may be populated by rmi_rtt_data_map_init().


> +	 */
> +	REALM_STATE_NEW,
> +	/**
> +	 * @REALM_STATE_ACTIVE:
> +	 *      Realm has been created and is eligible for execution with
> +	 *      rmi_rec_enter(). Pages may no longer be populated with
> +	 *      rmi_data_create().
> +	 */
> +	REALM_STATE_ACTIVE,

rmi_data_create() is an invalid function name.

	Memory can't longer be populated using rmi_rtt_data_map_init().

> +	/**
> +	 * @REALM_STATE_DYING:
> +	 *      Realm is in the process of being destroyed or has already been
> +	 *      destroyed.
> +	 */
> +	REALM_STATE_DYING,
> +	/**
> +	 * @REALM_STATE_DEAD:
> +	 *      Realm has been destroyed.
> +	 */
> +	REALM_STATE_DEAD
> +};
> +
> +/**
> + * struct realm - Additional per VM data for a Realm
> + *
> + * @state: The lifetime state machine for the realm
> + */
> +struct realm {
> +	enum realm_state state;
> +};
> +
> +void kvm_init_rmi(void);
> +
> +#endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
> index b546703c3ab9a..92cec42952f42 100644
> --- a/arch/arm64/include/asm/virt.h
> +++ b/arch/arm64/include/asm/virt.h
> @@ -87,6 +87,7 @@ void __hyp_reset_vectors(void);
>   bool is_kvm_arm_initialised(void);
>   
>   DECLARE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);
> +DECLARE_STATIC_KEY_FALSE(kvm_rmi_is_available);
>   
>   static inline bool is_pkvm_initialized(void)
>   {
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 59612d2f277c1..ed3cf30eb06e7 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -16,7 +16,7 @@ CFLAGS_handle_exit.o += -Wno-override-init
>   kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
>   	 inject_fault.o va_layout.o handle_exit.o config.o \
>   	 guest.o debug.o reset.o sys_regs.o stacktrace.o \
> -	 vgic-sys-reg-v3.o fpsimd.o pkvm.o \
> +	 vgic-sys-reg-v3.o fpsimd.o pkvm.o rmi.o \
>   	 arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
>   	 vgic/vgic.o vgic/vgic-init.o \
>   	 vgic/vgic-irqfd.o vgic/vgic-v2.o \
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 294cea4cc8271..37ef2ba4e43e2 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -42,6 +42,7 @@
>   #include <asm/kvm_nested.h>
>   #include <asm/kvm_pkvm.h>
>   #include <asm/kvm_ptrauth.h>
> +#include <asm/kvm_rmi.h>
>   #include <asm/sections.h>
>   #include <asm/stacktrace/nvhe.h>
>   
> @@ -112,6 +113,8 @@ long kvm_get_cap_for_kvm_ioctl(unsigned int ioctl, long *ext)
>   	return -EINVAL;
>   }
>   
> +DEFINE_STATIC_KEY_FALSE(kvm_rmi_is_available);
> +
>   DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);
>   
>   DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_base);
> @@ -2239,6 +2242,7 @@ static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {
>   	KVM_VCPU_OPS(VM_VHE, vhe_vcpu_ops),
>   	KVM_VCPU_OPS(VM_PKVM, pkvm_vcpu_ops),
>   	KVM_VCPU_OPS(VM_PROTECTED_PKVM, pkvm_vcpu_ops),
> +	KVM_VCPU_OPS(VM_REALM, realm_vcpu_ops),
>   };
>   
>   static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)
> @@ -3192,6 +3196,8 @@ static __init int kvm_arm_init(void)
>   
>   	in_hyp_mode = is_kernel_in_hyp_mode();
>   
> +	kvm_init_rmi();
> +
>   	if (cpus_have_final_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ||
>   	    cpus_have_final_cap(ARM64_WORKAROUND_1508412))
>   		kvm_info("Guests without required CPU erratum workarounds can deadlock system!\n" \
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 58bc6a85f48b4..697f1ba6667a9 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -2927,6 +2927,7 @@ static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {
>   	KVM_VM_S2_OPS(VM_NVHE, kvm_default_vm_s2_ops),
>   	KVM_VM_S2_OPS(VM_PKVM, pkvm_vm_s2_ops),
>   	KVM_VM_S2_OPS(VM_PROTECTED_PKVM, protected_pkvm_vm_s2_ops),
> +	KVM_VM_S2_OPS(VM_REALM, realm_vm_s2_ops),
>   };
>   
>   static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> new file mode 100644
> index 0000000000000..5ecc8b3498698
> --- /dev/null
> +++ b/arch/arm64/kvm/rmi.c
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023-2026 ARM Ltd.
> + */
> +
> +#include <linux/kvm_host.h>
> +
> +#include <asm/virt.h>
> +
> +void kvm_init_rmi(void)
> +{
> +	if (kvm_get_mode() != KVM_MODE_RMM)
> +		return;
> +
> +	/* TODO: Check if the RMI is available */
> +
> +	/* Future patch will enable static branch kvm_rmi_is_available */
> +}

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 20/23] KVM: arm64: CCA: WARN on injected undef exceptions
  2026-10-05  9:07 ` [PATCH v22 20/23] KVM: arm64: CCA: WARN on injected undef exceptions Suzuki K Poulose
@ 2026-10-06  3:49   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:49 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
> 
> The RMM doesn't allow injection of a undefined exception into a realm
> guest. Add a WARN to catch if this ever happens.
> 
> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/inject_fault.c | 1 +
>   1 file changed, 1 insertion(+)
> 
Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch
  2026-10-05  9:07 ` [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch Suzuki K Poulose
@ 2026-10-06  3:55   ` Gavin Shan
  2026-10-06  8:33   ` Marc Zyngier
  1 sibling, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  3:55 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> KVM arm64 has different types of VMs with all the different modes in which
> the hypervisor code can be run. e.g., VHE, nVHE, pKVM etc. Then there is
> protected VM and normal VMs with pKVM. We might soon add other types,
> e.g., Arm CCA Realm. So in an effort to make the handling of these
> different types of VMs a bit more friendly to the eyes, add a VM flavor to
> the kvm_arch and we could then add handlers for different operations based
> on the VM type.
> 
> Keep the flavor initialisation at the beginning to allow for the detection
> early enough and fail out on any unsupported requests.
> 
> With that, add wrappers for checking the "type" of a VM and replace the
> existing users with the new wrappers.
> 
> Given we already have the construct of "kvm_vm_is_protected" in the core
> KVM code, use that for all confidential compute guests including Realms
> that we are about to add. Adds __VM_PROTECTED marker vm flavor to draw the
> boundary for "protected VMs". In later patches, we would add Realm VMs,
>   which would also be classified as protected.
> 
> Add a explicit helper to detect if a given VM is a "protected" VM under pKVM.
> Change the existing users that precisely want to check the VM type. These
> include :
>    - kvm_arch_prepare_memory_region - For preventing memslot changes after
>      pVM creation.
> 
> All the others are retained as a wider check for confidential guest VMs.
> These are:
>   - kvm_vm_ioctl_set_counter_offset - For disallowing timer offset
>     configuration
>   - io_mem_abort for dabt handling without valid syndrome information
> 
> Both of which are true for Realms too.
> 
> Realms support is restricted to VHE host and thus "kvm_vm_is_protected()"
> checks in the pkvm hyp specific code doesn't need to change, as the only
> protected guests it deals with is "protected pKVM" guests. To tighten this
> init_pkvm_hyp_vm() restricts the hyp copy of the vm_flavor to the ones it
> supports.
> 
> vcpu_is_protected() usage from nVHE hyp code is tricky, as we need to
> convert the vcpu->kvm to the HYP VA before checking the flavor. This
> involves kern_hyp_va() usage in asm/kvm_host.h. To avoid build breaks,
> include asm/kvm_mmu.h to arm64/kvm/mmio.c.
> 
> While at it move the psci_version around to keep the structure packed.
> 
> Suggested-by: Marc Zyngier <maz@kernel.org>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v21:
>   - Drop kern_hyp_va() and restrict nvhe code to always use vcpu_is_protected_pkvm()
>   - Drop kvm_vm_is_unprotected_pkvm() and open code the check
>   - Move psci_version field in kvm_arch around to keep the structure packed
> ---
>   arch/arm64/include/asm/kvm_host.h      | 42 +++++++++++++++++++++++---
>   arch/arm64/include/asm/kvm_pkvm.h      |  4 +--
>   arch/arm64/kvm/arm.c                   | 33 ++++++++++++++++----
>   arch/arm64/kvm/hyp/include/nvhe/pkvm.h |  2 +-
>   arch/arm64/kvm/hyp/nvhe/pkvm.c         |  6 +++-
>   arch/arm64/kvm/hyp/nvhe/switch.c       |  4 +--
>   arch/arm64/kvm/hyp/nvhe/timer-sr.c     |  2 +-
>   arch/arm64/kvm/mmio.c                  |  1 +
>   arch/arm64/kvm/mmu.c                   |  2 +-
>   arch/arm64/kvm/pkvm.c                  |  6 ++--
>   10 files changed, 79 insertions(+), 23 deletions(-)
> 

Reviewed-by: Gavin Shan <gshan@redhat.com>


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 19/23] KVM: arm64: CCA: Don't expose unsupported capabilities for realm guests
  2026-10-05  9:07 ` [PATCH v22 19/23] KVM: arm64: CCA: Don't expose unsupported capabilities for realm guests Suzuki K Poulose
@ 2026-10-06  4:58   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  4:58 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> Limit the capabilities that are allowed for Realm VMs. Similarly block
> the vm_ioctls backed by the capabilities.
> 
> Repurpose the kvm_pkvm_ioctl_allowed() to support both pKVM and Realm
> ioctls. Rename the helper to kvm_vm_ioctl_allowed() and move it
> into arch/arm64/kvm/arm.c. Also add a generic kvm_vm_ext_allowed()
> to handle pKVM and Realm capability filtering and route them accordingly.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v21:
>   - Drop WARN_ON_ONCE() from kvm_vm_ioctl_allowed to synchronise with
>     what is in -rc5 and make the conflict resolution easier.
> ---
>   arch/arm64/include/asm/kvm_pkvm.h | 19 ------------
>   arch/arm64/include/asm/kvm_rmi.h  | 24 +++++++++++++++
>   arch/arm64/kvm/arm.c              | 49 +++++++++++++++++++++++++++++--
>   3 files changed, 70 insertions(+), 22 deletions(-)
> 

One nitpick below. In either way:

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
> index 2addc37c500e1..b833abb1be9c7 100644
> --- a/arch/arm64/include/asm/kvm_pkvm.h
> +++ b/arch/arm64/include/asm/kvm_pkvm.h
> @@ -53,25 +53,6 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
>   	}
>   }
>   
> -/*
> - * Check whether the KVM VM IOCTL is allowed in pKVM.
> - *
> - * Certain features are allowed only for non-protected VMs in pKVM, which is why
> - * this takes the VM (kvm) as a parameter.
> - */
> -static inline bool kvm_pkvm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
> -{
> -	long ext;
> -	int r;
> -
> -	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
> -
> -	if (WARN_ON_ONCE(r < 0))
> -		return false;
> -
> -	return kvm_pkvm_ext_allowed(kvm, ext);
> -}
> -
>   extern struct memblock_region kvm_nvhe_sym(hyp_memory)[];
>   extern unsigned int kvm_nvhe_sym(hyp_memblock_nr);
>   
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 44f5c75a27b5b..ea1450e0c8619 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -6,6 +6,8 @@
>   #ifndef __ASM_KVM_RMI_H
>   #define __ASM_KVM_RMI_H
>   
> +#include <linux/kvm.h>
> +
>   /**
>    * enum realm_state - State of a Realm
>    *
> @@ -58,4 +60,26 @@ struct realm {
>   
>   void kvm_init_rmi(void);
>   
> +static inline bool kvm_realm_ext_allowed(long ext)
> +{
> +	switch (ext) {
> +	case KVM_CAP_IRQCHIP:
> +	case KVM_CAP_ARM_PSCI:
> +	case KVM_CAP_ARM_PSCI_0_2:
> +	case KVM_CAP_DEVICE_CTRL:
> +	case KVM_CAP_NR_VCPUS:
> +	case KVM_CAP_MAX_VCPUS:
> +	case KVM_CAP_MAX_VCPU_ID:
> +	case KVM_CAP_MSI_DEVID:
> +	case KVM_CAP_ARM_VM_IPA_SIZE:
> +	case KVM_CAP_ARM_SVE:
> +	case KVM_CAP_ONE_REG:
> +	case KVM_CAP_ARM_PTRAUTH_ADDRESS:
> +	case KVM_CAP_ARM_PTRAUTH_GENERIC:
> +	case KVM_CAP_SYNC_MMU:
> +		return true;
> +	}
> +	return false;
> +}
> +
>   #endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 37ef2ba4e43e2..fe707a0c47308 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -136,6 +136,49 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
>   	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
>   }
>   
> +static inline bool kvm_vm_ext_allowed(struct kvm *kvm, long ext)
> +{
> +	/*
> +	 * We could be called with kvm as NULL, so can't use kvm_vm_* for pKVM
> +	 * flavors
> +	 */
> +	if (is_protected_kvm_enabled())
> +		return kvm_pkvm_ext_allowed(kvm, ext);
> +	else if (kvm && kvm_vm_is_realm(kvm))
> +		return kvm_realm_ext_allowed(ext);
> +	else
> +		return true;
> +}
> +
> +/*
> + * Check whether the KVM VM IOCTL is allowed. For pKVM and Realm VMs, certain
> + * ioctls are not allowed. Further, certain features are allowed only for
> + * non-protected VMs in pKVM.
> + */
> +static inline bool kvm_vm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
> +{
> +	long ext;
> +	int r;

	long ext, r;

	kvm_get_cap_for_kvm_ioctl() returns 'long' instead of 'int' value.

> +
> +	/*
> +	 * We are guaranteed to be called with a valid kvm instance, as the
> +	 * only caller is kvm_arch_vm_ioctl(). Catch any deviations, as we
> +	 * rely on the kvm instance below.
> +	 */
> +	if (WARN_ON_ONCE(!kvm))
> +		return false;
> +
> +	/* Cover both pKVM host and Realm VMs */
> +	if (!kvm_vm_hyp_is_distrusting(kvm))
> +		return true;
> +
> +	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
> +	if (r < 0)
> +		return false;
> +
> +	return kvm_vm_ext_allowed(kvm, ext);
> +}
> +
>   int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>   			    struct kvm_enable_cap *cap)
>   {
> @@ -144,7 +187,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
>   	if (cap->flags)
>   		return -EINVAL;
>   
> -	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, cap->cap))
> +	if (!kvm_vm_ext_allowed(kvm, cap->cap))
>   		return -EINVAL;
>   
>   	switch (cap->cap) {
> @@ -403,7 +446,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>   {
>   	int r;
>   
> -	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
> +	if (!kvm_vm_ext_allowed(kvm, ext))
>   		return 0;
>   
>   	switch (ext) {
> @@ -2146,7 +2189,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
>   	void __user *argp = (void __user *)arg;
>   	struct kvm_device_attr attr;
>   
> -	if (is_protected_kvm_enabled() && !kvm_pkvm_ioctl_allowed(kvm, ioctl))
> +	if (!kvm_vm_ioctl_allowed(kvm, ioctl))
>   		return -EINVAL;
>   
>   	switch (ioctl) {

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms
  2026-10-06  3:16   ` Gavin Shan
@ 2026-10-06  5:09     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  5:09 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron


Hi Gavin

On 06/10/2026 04:16, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> RMM controls the VCPU settings and most are hidden from the KVM, except
>> for the VGIC and timer bits.
>>
>> A later patch would add syncing the VCPU state into the Realm REC related
>> SMC parameters.

This is what you are looking for ^^^

>>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>>   arch/arm64/kvm/arm.c | 18 ++++++++++++++++++
>>   1 file changed, 18 insertions(+)
>>
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 6c81fec35ecec..294cea4cc8271 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -802,6 +802,13 @@ static void pkvm_vcpu_load(struct kvm_vcpu *vcpu)
>>                 &vcpu->arch.vgic_cpu.vgic_v3);
>>   }
>> +static void realm_vcpu_load(struct kvm_vcpu *vcpu)
>> +{
>> +    kvm_timer_vcpu_load(vcpu);
>> +    kvm_vgic_load(vcpu);
>> +    vcpu_set_wfx_traps(vcpu);
> 
> Why we need to call vcpu_set_wfx_traps(), which updates 'vcpu- 
>  >arch.hcr_el2'? I don't
> see how 'vcpu->arch.hcr_el2' is affects realm in RMM. Could you please 
> explain in the
> commit log.

See above.


Cheers
Suzuki

> 
>> +}
>> +
>>   void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
>>   {
>>       vcpu->cpu = cpu;
>> @@ -854,6 +861,12 @@ static void pkvm_vcpu_put(struct kvm_vcpu *vcpu)
>>       kvm_vcpu_pmu_restore_host(vcpu);
>>   }
>> +static void realm_vcpu_put(struct kvm_vcpu *vcpu)
>> +{
>> +    kvm_timer_vcpu_put(vcpu);
>> +    kvm_vgic_put(vcpu);
>> +}
>> +
>>   void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
>>   {
>>       vcpu->arch.vcpu_ops->vcpu_put(vcpu);
>> @@ -2213,6 +2226,11 @@ static const struct kvm_vcpu_ops pkvm_vcpu_ops = {
>>       .vcpu_put = pkvm_vcpu_put,
>>   };
>> +static const struct kvm_vcpu_ops realm_vcpu_ops = {
>> +    .vcpu_load = realm_vcpu_load,
>> +    .vcpu_put = realm_vcpu_put,
>> +};
>> +
>>   #define KVM_VCPU_OPS(flavor, ops)        \
>>       [(flavor)] = &(ops)
> 
> Thanks,
> Gavin
> 
> 


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms
  2026-10-06  3:42   ` Gavin Shan
@ 2026-10-06  5:10     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  5:10 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 06/10/2026 04:42, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> From: Steven Price <steven.price@arm.com>
>>
>> Add foundational work for supporting Realms.
>>   - Add a new VM flavor.
>>   - At KVM init, check if the KVM can support Realms (though not 
>> functional
>>     yet) and will be advertised by static key kvm_rmi_is_available. This
>>     will be turned on in a later patches, once we have all the bits and
>>     pieces ready. For now check if we are blessed with KVM_MODE_RMM.
>>   - Add realm specific tracking in kvm_arch. Since Realm and protected 
>> pKVM
>>     states are mutually exclusive, move them into a union.
>>
>> Please note that we cannot create Realm VMs yet. This requires further
>> changes to the UABI and core RMI driver support, which will come later.
>>
>> Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>>   arch/arm64/include/asm/kvm_emulate.h | 16 ++++++++
>>   arch/arm64/include/asm/kvm_host.h    | 19 ++++++---
>>   arch/arm64/include/asm/kvm_rmi.h     | 61 ++++++++++++++++++++++++++++
>>   arch/arm64/include/asm/virt.h        |  1 +
>>   arch/arm64/kvm/Makefile              |  2 +-
>>   arch/arm64/kvm/arm.c                 |  6 +++
>>   arch/arm64/kvm/mmu.c                 |  1 +
>>   arch/arm64/kvm/rmi.c                 | 18 ++++++++
>>   8 files changed, 118 insertions(+), 6 deletions(-)
>>   create mode 100644 arch/arm64/include/asm/kvm_rmi.h
>>   create mode 100644 arch/arm64/kvm/rmi.c
>>
>> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/ 
>> include/asm/kvm_emulate.h
>> index a3c1928bdf743..d360a8b05b8bf 100644
>> --- a/arch/arm64/include/asm/kvm_emulate.h
>> +++ b/arch/arm64/include/asm/kvm_emulate.h
>> @@ -793,4 +793,20 @@ static inline void kvm_reset_vcpu_psci(struct 
>> kvm_vcpu *vcpu,
>>       vcpu_set_reg(vcpu, 0, reset_state->r0);
>>   }
>> +static inline enum realm_state kvm_realm_state(struct kvm *kvm)
>> +{
>> +    return READ_ONCE(kvm->arch.realm.state);
>> +}
>> +
>> +static inline void kvm_set_realm_state(struct kvm *kvm,
>> +                       enum realm_state new_state)
>> +{
>> +    WRITE_ONCE(kvm->arch.realm.state, new_state);
>> +}
>> +
>> +static inline bool kvm_realm_is_created(struct kvm *kvm)
>> +{
>> +    return kvm_vm_is_realm(kvm) && kvm_realm_state(kvm) != 
>> REALM_STATE_NONE;
>> +}
>> +
>>   #endif /* __ARM64_KVM_EMULATE_H__ */
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/ 
>> asm/kvm_host.h
>> index dfa9d4ec61a76..3debffef638a4 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -27,6 +27,7 @@
>>   #include <asm/fpsimd.h>
>>   #include <asm/kvm.h>
>>   #include <asm/kvm_asm.h>
>> +#include <asm/kvm_rmi.h>
>>   #include <asm/vncr_mapping.h>
>>   #define __KVM_HAVE_ARCH_INTC_INITIALIZED
>> @@ -334,6 +335,7 @@ enum kvm_arm_vm_flavor {
>>       VM_PKVM,        /* Normal guests on pKVM */
>>       MARKER(__VM_PROTECTED),
>>       VM_PROTECTED_PKVM,    /* Protected VM */
>> +    VM_REALM,        /* CCA */
>>       VM_FLAVOR_MAX
>>   };
>> @@ -450,11 +452,14 @@ struct kvm_arch {
>>       /* Count the number of VNCR_EL2 TLBs */
>>       atomic_t vncr_tlb_count;
>> -    /*
>> -     * For an untrusted host VM, 'pkvm.handle' is used to lookup
>> -     * the associated pKVM instance in the hypervisor.
>> -     */
>> -    struct kvm_protected_vm pkvm;
>> +    union {
>> +        /*
>> +         * For an untrusted host VM, 'pkvm.handle' is used to lookup
>> +         * the associated pKVM instance in the hypervisor.
>> +         */
>> +        struct kvm_protected_vm pkvm;
>> +        struct realm realm;
>> +    };
>>   #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
>>       /* Nested virtualization info */
>> @@ -1565,6 +1570,10 @@ struct kvm *kvm_arch_alloc_vm(void);
>>           (__kvm && kvm_vm_is_protected_pkvm(__kvm));        \
>>       })
>> +
>> +#define kvm_vm_is_realm(kvm)        ((kvm)->arch.vm_flavor == VM_REALM)
>> +#define vcpu_is_rec(vcpu)        kvm_vm_is_realm((vcpu)->kvm)
>> +
> 
> vcpu_is_rec() isn't safely called in nVHE hyp stub. So do we need add 
> something
> similiar to what we had for vcpu_is_protected(vcpu) in PATCH[05]?
> 
> #ifdef __KVM_NVHE_HYPERVISOR__
> /* vcpu_is_rec() isn't expected to called in nVHE hyp stub */
> #define vcpu_is_rec(vcpu)    BUILD_BUG_ON(1)
> #else
> #define vcpu_is_rec(vcpu)    kvm_vm_is_realm((vcpu)->kvm)
> #endif
> 
> 
>>   #define kvm_vm_hyp_is_distrusting(kvm)    ((kvm)->arch.vm_flavor >= 
>> __VM_DISTRUSTING_HYP)
>>   int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/ 
>> asm/kvm_rmi.h
>> new file mode 100644
>> index 0000000000000..44f5c75a27b5b
>> --- /dev/null
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -0,0 +1,61 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023-2026 ARM Ltd.
>> + */
>> +
>> +#ifndef __ASM_KVM_RMI_H
>> +#define __ASM_KVM_RMI_H
>> +
>> +/**
>> + * enum realm_state - State of a Realm
>> + *
>> + * Mirrors the RMM's Realm lifecycle states where they are meaningful 
>> to KVM,
>> + * with REALM_STATE_DYING being a KVM-internal state used to prevent 
>> further
>> + * requests while teardown is in progress. KVM does not track 
>> REALM_SYSTEM_OFF
>> + * or REALM_ZOMBIE separately as they naturally lead to teardown.
>> + */
>> +enum realm_state {
>> +    /**
>> +     * @REALM_STATE_NONE:
>> +     *      Realm has not yet been created. rmi_realm_create() has not
>> +     *      yet been called.
>> +     */
>> +    REALM_STATE_NONE,
>> +    /**
>> +     * @REALM_STATE_NEW:
>> +     *      Realm is under construction, rmi_realm_create() has been
>> +     *      called, but it is not yet activated. Pages may be populated.
>                                                       ^^^^^
> Granule instead of page is the term applicable to RMM. Lets use a generic
> term 'memory' here. Also, lets mention the name of the function used to
> populate the memory (granules), which is consistent to the comments for
> REALM_STATE_ACTIVE.
> 
>      Memory may be populated by rmi_rtt_data_map_init().
> 
> 
>> +     */
>> +    REALM_STATE_NEW,
>> +    /**
>> +     * @REALM_STATE_ACTIVE:
>> +     *      Realm has been created and is eligible for execution with
>> +     *      rmi_rec_enter(). Pages may no longer be populated with
>> +     *      rmi_data_create().
>> +     */
>> +    REALM_STATE_ACTIVE,
> 
> rmi_data_create() is an invalid function name.
> 
>      Memory can't longer be populated using rmi_rtt_data_map_init().

Ack, thanks for spotting, I will fix them.


Cheers
Suzuki

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types
  2026-10-06  2:25   ` Gavin Shan
@ 2026-10-06  5:16     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  5:16 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 06/10/2026 03:25, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> Prevent unsupported VCPU features for the protected VCPUs. Realms and 
>> pVMs
>> not support 32bit EL1 or NV yet. pKVM doesn't rely on the host vcpu
>> features and it clears the unsupported features while hyp_vcpu is
>> initialised. Block the features early in the vcpu init if we detect
>> incompatible features.
>>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>>   arch/arm64/kvm/arm.c | 10 ++++++----
>>   1 file changed, 6 insertions(+), 4 deletions(-)
>>
> 
> One nitpick below. In either way:
> 
> Reviewed-by: Gavin Shan <gshan@redhat.com>

Thanks !

> 
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index f31d31fa27ad9..9c2ef7ca6a961 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -1668,11 +1668,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, 
>> struct kvm_irq_level *irq_level,
>>       return -EINVAL;
>>   }
>> -static unsigned long system_supported_vcpu_features(void)
>> +static unsigned long system_supported_vcpu_features(struct kvm_vcpu 
>> *vcpu)
>>   {
>>       unsigned long features = KVM_VCPU_VALID_FEATURES;
>> -    if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
>> +    if (vcpu_is_protected(vcpu) ||
>> +        !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
>>           clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
>>       if (!kvm_supports_guest_pmuv3()) {
>> @@ -1688,7 +1689,8 @@ static unsigned long 
>> system_supported_vcpu_features(void)
>>           clear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features);
>>       }
>> -    if (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
>> +    if (vcpu_is_protected(vcpu) ||
>> +        !cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
>>           clear_bit(KVM_ARM_VCPU_HAS_EL2, &features);
>>       return features;
>> @@ -1708,7 +1710,7 @@ static int kvm_vcpu_init_check_features(struct 
>> kvm_vcpu *vcpu,
>>               return -ENOENT;
>>       }
>> -    if (features & ~system_supported_vcpu_features())
>> +    if (features & ~system_supported_vcpu_features(vcpu))
>>           return -EINVAL;
>>
> 
> The following check at the beginning of kvm_vcpu_init_check_features() 
> is redundant to
> the check here. We can drop that in this patch or a preparatory patch.
> 
> static int kvm_vcpu_init_check_features(...)
> {
>         /*
>          * This check can be dropped since the same check has been done
>      * by the followup "if (features & 
> ~system_supported_vcpu_features(vcpu))"
>          */
>         if (features & ~KVM_VCPU_VALID_FEATURES)
>                  return -ENOENT;

We could, but the errno is different with -ENOENT vs -EINVAL, with a
relevant meaning. So I would leave that alone.

Cheers
Suzuki

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations
  2026-10-06  3:00   ` Gavin Shan
@ 2026-10-06  5:22     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  5:22 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 06/10/2026 04:00, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> Add VM type specific S2 MMU operation backends which can be 
>> initialized per
>> VM flavor, to keep the handling cleaner.
>>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Change since v21:
>>   - Define all vm_s2_ops call back. All calls are mandatory.
>>   - Define callback for each flavor, disjointing the non-protetcted 
>> pKVM and
>>     normal KVM (VHE & nVHE) and remove the KVM_PGT_FN() hacks.
>>   - Dropped Reviews due to the changes.
>>   - Add "no_age_gfn" and "no_stage2_unmap_range" for pKVM callbacks, 
>> no_age_*
>>     to be also reused by Realms later.
>>   - Move kvm_vm_s2_ops field to keep the structure packed

...

>>    */
>>   int kvm_arch_flush_remote_tlbs(struct kvm *kvm)
>>   {
>> -    if (is_protected_kvm_enabled())
>> -        kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
>> -    else
>> -        kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
>> -    return 0;
>> +    return kvm->arch.vm_s2_ops->vm_flush_remote_tlbs(kvm);
>>   }
>> -int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
>> -                      gfn_t gfn, u64 nr_pages)
>> +static int pkvm_flush_remote_tlbs_range(struct kvm *kvm,
>> +                    gfn_t gfn, u64 nr_pages)
>> +{
>> +    return pkvm_flush_remote_tlbs(kvm);
> 
> No need to have another function call, which causes unnecessary overhead?
> 
>      kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);

Maybe, but that is in another way, telling the reader that pKVM can only
do full VM TLB flush, no range TLB flush.

>      return 0;
> 
>> +}
>> +
>> +static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,
>> +                     gfn_t gfn, u64 nr_pages)
>>   {
>>       u64 size = nr_pages << PAGE_SHIFT;
>>       u64 addr = gfn << PAGE_SHIFT;
>> -    if (is_protected_kvm_enabled())
>> -        kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
>> -    else
>> -        kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
>> +    kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
>>       return 0;
>>   }
>>
> 
> Since we're here, the local variable 'addr' and 'size' can be dropped by:
> 
>      kvm_tlb_flush_vmid_range(&kvm->arch.mmu,
>                                   gfn << PAGE_SHIFT,
>                                   nr_pages << PAGE_SHIFT);
> 

Does it really matter, the compiler can optimise this anyways and looks
more readable ?

> 
>> +int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
>> +                     gfn_t gfn, u64 nr_pages)
>> +{
>> +    return kvm->arch.vm_s2_ops->vm_flush_remote_tlbs_range(kvm, gfn, 
>> nr_pages);
>> +}
>> +

...

>> +static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
>> +    .vm_flush_remote_tlbs        = kvm_vm_flush_remote_tlbs,
>> +    .vm_flush_remote_tlbs_range    = kvm_vm_flush_remote_tlbs_range,
>> +    .vm_age_gfn            = kvm_vm_age_gfn,
>> +    .vm_test_age_gfn        = kvm_vm_test_age_gfn,
>> +    .vm_stage2_unmap_range        = kvm_vm_stage2_unmap_range,
>> +};
>> +
>> +#define KVM_VM_S2_OPS(flavor, ops)        \
>> +        [flavor] = &(ops)
> 
> s/[flavor]/[(flavor)]

Ack


Thanks !
Suzuki

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling
  2026-10-06  3:07   ` Gavin Shan
@ 2026-10-06  5:25     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  5:25 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 06/10/2026 04:07, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> Move the memory abort handling under VM specific s2 operation.
>>
>> Tested-by: Gavin Shan <gshan@redhat.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v21:
>>   - Rename protected_vm_mem_abort => protected_pkvm_mem_abort for 
>> consistency
>>     with the other callbacks.
>> ---
>>   arch/arm64/include/asm/kvm_host.h |  2 ++
>>   arch/arm64/kvm/mmu.c              | 33 ++++++++++++++++++-------------
>>   2 files changed, 21 insertions(+), 14 deletions(-)
>>
> 
> One nitpick below. In either way:
> 
> Reviewed-by: Gavin Shan <gshan@redhat.com>

Thanks Gavin !

...

>> +static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>> +{
>> +    struct kvm_vcpu *vcpu = s2fd->vcpu;
>> +
>> +    VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
>> +            !kvm_is_write_fault(vcpu) &&
>> +            !kvm_vcpu_trap_is_exec_fault(vcpu));
>> +
>> +    if (kvm_slot_has_gmem(s2fd->memslot))
>> +        return gmem_abort(s2fd);
>> +    else
>> +        return user_mem_abort(s2fd);
>> +}
> 
> The "if...else" block is moved from kvm_handle_guest_abort(), but it 
> might be
> clearer if we have:
> 
>      if (kvm_slot_has_gmem(s2fd->memslot))
>          return gmem_abort(s2fd);
> 
>      return user_mem_abort(s2fd);

This was chosen based on another review comment. So, lets stick with 
this now.

Cheers
Suzuki

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization
  2026-10-05  9:07 ` [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization Suzuki K Poulose
@ 2026-10-06  5:35   ` Gavin Shan
  2026-10-06  5:57     ` Suzuki K Poulose
  0 siblings, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  5:35 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
> 
> Userspace must configure the SVE vector length before the Realm is created
> (as it is part of the parameter for Realm creation), but the Realm VCPUs
> cannot be finalized until after the Realm Descriptor has been created.
> 
> KVM_GET_REG_LIST currently rejects the unfinalized VCPUs, which prevents
> the userspace from discovering and configuring the VLs for  the Realm.
> 
> Allow KVM_GET_REG_LIST for unfinalized RECs and make the SVE register
> enumeration handle the unfinalized case explicitly. i.e., only expose
> KVM_REG_ARM64_SVE_VLS before SVE is finalized.
> 
> One adverse side effect of this change is that a KVM_GET_REG_LIST call that
> only probes for the array size will now succeed even if SVE is not
> finalized, but that seems harmless since the following KVM_GET_REG_LIST
> with the full array will fail.
> 
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/arm.c   | 15 ++++++++++++++-
>   arch/arm64/kvm/guest.c | 10 +++++-----
>   2 files changed, 19 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index fe707a0c47308..d99e7818f5894 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -2004,6 +2004,19 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
>   	return __kvm_arm_vcpu_set_events(vcpu, events);
>   }
>   
> +/*
> + * Realm VCPUs can be finalized only after the Realm descriptor is created.
> + * But in order to seal the SVE VL, we need to allow the userspace to read/write
> + * to the SVE_VL, before everything is finalized.
> + * Allow the register list for RECs before the VCPUs are finalized.
> + */
> +static bool kvm_arm_vcpu_reg_list_allowed(struct kvm_vcpu *vcpu)
> +{
> +	if (kvm_arm_vcpu_is_finalized(vcpu))
> +		return true;
> +	return vcpu_is_rec(vcpu);
> +}
> +

Needn't to keep this helper, and the code can be integrated to kvm_arch_vcpu_ioctl(),
see below.

>   long kvm_arch_vcpu_ioctl(struct file *filp,
>   			 unsigned int ioctl, unsigned long arg)
>   {
> @@ -2059,7 +2072,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>   			break;
>   
>   		r = -EPERM;
> -		if (!kvm_arm_vcpu_is_finalized(vcpu))
> +		if (!kvm_arm_vcpu_reg_list_allowed(vcpu))
>   			break;

Needn't to keep the helper kvm_arm_vcpu_reg_list_allowed() after its logic is
combined to kvm_arch_vcpu_ioctl().

		/*
		 * Realm vCPUs can be finalized only after the Realm descriptor is created.
		 * We need to allow access KVM_REG_ARM64_SVE_VLS before that so that the
		 * register can be sealed.
		 */
		if (!(vcpu_is_rec(vcpu) || kvm_arm_vcpu_is_finalized(vcpu))
			break;

>   
>   		r = -EFAULT;
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index b01d6622b8720..c3ca369882273 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -598,8 +598,8 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
>   	if (!vcpu_has_sve(vcpu))
>   		return 0;
>   
> -	/* Policed by KVM_GET_REG_LIST: */
> -	WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
> +	if (!kvm_arm_vcpu_sve_finalized(vcpu))
> +		return 1; /* KVM_REG_ARM64_SVE_VLS */
>   

Question: After a (Rec) vCPU is finalized, the returned number of registers won't
be 1. Is this expected? Otherwise, we need to use vcpu_is_rec() here.

	/*
	 * KVM_REG_ARM64_SVE_VLS is visible for realm vCPUs no matter if
	 * they have been finalized.
	 */
	if (vcpu_is_rec(vcpu))
		return 1;

>   	return slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */)
>   		+ 1; /* KVM_REG_ARM64_SVE_VLS */
> @@ -616,9 +616,6 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
>   	if (!vcpu_has_sve(vcpu))
>   		return 0;
>   
> -	/* Policed by KVM_GET_REG_LIST: */
> -	WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
> -
>   	/*
>   	 * Enumerate this first, so that userspace can save/restore in
>   	 * the order reported by KVM_GET_REG_LIST:
> @@ -628,6 +625,9 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
>   		return -EFAULT;
>   	++num_regs;
>   
> +	if (!kvm_arm_vcpu_sve_finalized(vcpu))
> +		return num_regs;
> +

Same question as above.

>   	for (i = 0; i < slices; i++) {
>   		for (n = 0; n < SVE_NUM_ZREGS; n++) {
>   			reg = KVM_REG_ARM64_SVE_ZREG(n, i);

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 21/23] KVM: arm64: CCA: Support timers in realm RECs
  2026-10-05  9:07 ` [PATCH v22 21/23] KVM: arm64: CCA: Support timers in realm RECs Suzuki K Poulose
@ 2026-10-06  5:44   ` Gavin Shan
  0 siblings, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  5:44 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> From: Steven Price <steven.price@arm.com>
> 
> The RMM keeps track of the timer while the realm REC is running, but on
> exit to the normal world KVM is responsible for handling the timers.
> 
> A later patch adds the support for propagating the timer values from the
> exit data structure and making sure the values are in sync for KVM.
> 
> Also, RMM doesn't support injecting virtual interrupts backed by Physical
> interrupts. So, use the existing software resampling mechanims for Realm
                                                        ^^^^^^^^^
typo: mechanism

> timer interrupts.
> 
> Signed-off-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/arch_timer.c | 22 ++++++++++++++++++++--
>   1 file changed, 20 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
> index a45845f4ae4ea..428709f499205 100644
> --- a/arch/arm64/kvm/arch_timer.c
> +++ b/arch/arm64/kvm/arch_timer.c
> @@ -56,11 +56,25 @@ static unsigned long kvm_arch_timer_get_irq_flags(void)
>   	return kvm_vgic_global_state.no_hw_deactivation ? VGIC_IRQ_SW_RESAMPLE : 0;
>   }
>   
> +static unsigned long kvm_realm_timer_get_irq_flags(void)
> +{
> +	/*
> +	 * RMI_REC_ENTER rejects LRs with the HW bit set, so use the existing
> +	 * software resampling mechanism for Realm timer interrupts.
> +	 */
> +	return VGIC_IRQ_SW_RESAMPLE;
> +}
> +
>   static const struct irq_ops arch_timer_irq_ops = {
>   	.get_flags	 = kvm_arch_timer_get_irq_flags,
>   	.get_input_level = kvm_arch_timer_get_input_level,
>   };
>   
> +static const struct irq_ops realm_timer_irq_ops = {
> +	.get_flags	 = kvm_realm_timer_get_irq_flags,
> +	.get_input_level = kvm_arch_timer_get_input_level,
> +};
> +
>   static const struct irq_ops arch_timer_irq_ops_vgic_v5 = {
>   	.get_input_level = kvm_arch_timer_get_input_level,
>   	.queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock,
> @@ -1617,8 +1631,12 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
>   
>   	get_timer_map(vcpu, &map);
>   
> -	ops = vgic_is_v5(vcpu->kvm) ? &arch_timer_irq_ops_vgic_v5 :
> -				      &arch_timer_irq_ops;
> +	if (vcpu_is_rec(vcpu))
> +		ops = &realm_timer_irq_ops;
> +	else if (vgic_is_v5(vcpu->kvm))
> +		ops = &arch_timer_irq_ops_vgic_v5;
> +	else
> +		ops = &arch_timer_irq_ops;
>   
>   	for (int i = 0; i < nr_timers(vcpu); i++)
>   		kvm_vgic_set_irq_ops(vcpu, timer_irq(vcpu_get_timer(vcpu, i)), ops);


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms
  2026-10-05  9:07 ` [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms Suzuki K Poulose
@ 2026-10-06  5:47   ` Gavin Shan
  2026-10-06  6:01     ` Suzuki K Poulose
  0 siblings, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  5:47 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
> 
> The RMM restricts the access to the register states that the host can
> read/modify for a given Realm.
> 
> e.g., At VCPU creation, can modify GPRS (x0-x30) and PC.
>        While servicing SMCCC calls via RSI_HOST_CALL or servicing PSCI
>        requests.
>        MMIO emulation in the unprotected space.
> 
> Additionally we use the sysreg configuration to advertise/configure the
> following Realm parameters, which are required before the Realm Descriptor
> is created:
>   - SVE Vector Length
>   - Number of HW Breakpoints/Watchpoints
>   - PMU Counters.
> 
> Thus KVM also additionally allows access to ID_AA64DFR0_EL1 and SVE_VLS for
> the configuration of Realm creation parameters. We don't support PMUs for
> the Realm VMs yet, so PMCR is not exposed.
> 
> The RMM makes similar restrictions for reading of the guest's registers
> (this is *confidential* compute after all), however we don't impose the
> restriction here. This allows the VMM to read (stale) values from the
> registers which might be useful to read back the initial values even if
> the RMM doesn't provide the latest version. For migration of a realm VM,
> a new interface will be needed so that the VMM can receive an
> (encrypted) blob of the VM's state.
> 
> Reflect the above in KVM_GET_REG_LIST, KVM_SET_ONE_REG calls.
> 
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> Co-developed-by: Steven Price <steven.price@arm.com>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>   arch/arm64/kvm/guest.c      | 63 +++++++++++++++++++++++++++++++++++++
>   arch/arm64/kvm/hypercalls.c |  4 +--
>   arch/arm64/kvm/sys_regs.c   | 28 +++++++++++++----
>   3 files changed, 87 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index c3ca369882273..ffe3f5ce4b3cf 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -73,6 +73,25 @@ static u64 core_reg_offset_from_id(u64 id)
>   	return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
>   }
>   
> +static bool kvm_realm_validate_core_reg(u64 off)
> +{
> +	/*
> +	 * Note that GPRs can only sometimes be controlled by the VMM.
> +	 * For PSCI only X0-X6 are used, higher registers are ignored (restored
> +	 * from the REC).
> +	 * For HOST_CALL all of X0-X30 are copied to the RsiHostCall structure.
> +	 * For emulated MMIO X0 is always used.
> +	 * PC can only be set before the realm is activated.
> +	 */
> +	switch (off) {
> +	case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
> +	     KVM_REG_ARM_CORE_REG(regs.regs[30]):
> +	case KVM_REG_ARM_CORE_REG(regs.pc):
> +		return true;
> +	}
> +	return false;
> +}
> +
>   static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
>   {
>   	int size;
> @@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,
>   		u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
>   		int size = core_reg_size_from_offset(vcpu, i);
>   
> +		if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
> +			continue;
> +
>   		if (size < 0)
>   			continue;
>   
> @@ -598,6 +620,9 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
>   	if (!vcpu_has_sve(vcpu))
>   		return 0;
>   
> +	if (kvm_vm_is_realm(vcpu->kvm))
> +		return 1; /* KVM_REG_ARM64_SVE_VLS */
> +
>   	if (!kvm_arm_vcpu_sve_finalized(vcpu))
>   		return 1; /* KVM_REG_ARM64_SVE_VLS */
> 

Aren't above two checks conflicting to each other?

   
> @@ -625,6 +650,10 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
>   		return -EFAULT;
>   	++num_regs;
>   
> +	/* For Realms only support SVE_VLS */
> +	if (kvm_vm_is_realm(vcpu->kvm))
> +		return num_regs;
> +
>   	if (!kvm_arm_vcpu_sve_finalized(vcpu))
>   		return num_regs;
>   

Same question here.

> @@ -705,6 +734,11 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   	if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
>   		return -EINVAL;
>   
> +	/*
> +	 * We don't filter out the register reads for Realms, like we do for
> +	 * the user writes. We expose junk data for the VMM instead of
> +	 * denying the requests.
> +	 */
>   	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>   	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
>   	case KVM_REG_ARM_FW:
> @@ -716,12 +750,41 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   	return kvm_arm_sys_reg_get_reg(vcpu, reg);
>   }
>   
> +#define KVM_REG_ARM_ID_AA64DFR0_EL1	ARM64_SYS_REG(3, 0, 0, 5, 0)
> +/*
> + * The RMI ABI only enables setting some GPRs and PC. The selection of GPRs
> + * that are available depends on the Realm state and the reason for the last
> + * exit.  All other registers are reset to architectural or otherwise defined
> + * reset values by the RMM, except for a few configuration fields that
> + * correspond to Realm parameters.
> + */
> +static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,
> +				   const struct kvm_one_reg *reg)
> +{
> +	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) {
> +		u64 off = core_reg_offset_from_id(reg->id);
> +
> +		return kvm_realm_validate_core_reg(off);
> +	}
> +
> +	switch (reg->id) {
> +	case KVM_REG_ARM_ID_AA64DFR0_EL1:
> +	case KVM_REG_ARM64_SVE_VLS:
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
>   int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>   {
>   	/* We currently use nothing arch-specific in upper 32 bits */
>   	if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
>   		return -EINVAL;
>   
> +	if (kvm_vm_is_realm(vcpu->kvm) && !validate_realm_set_reg(vcpu, reg))
> +		return -EINVAL;
> +
>   	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
>   	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
>   	case KVM_REG_ARM_FW:
> diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
> index b11b8821c9fbc..2b1e6fdeb4d5c 100644
> --- a/arch/arm64/kvm/hypercalls.c
> +++ b/arch/arm64/kvm/hypercalls.c
> @@ -414,14 +414,14 @@ void kvm_arm_teardown_hypercalls(struct kvm *kvm)
>   
>   int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
>   {
> -	return ARRAY_SIZE(kvm_arm_fw_reg_ids);
> +	return vcpu_is_rec(vcpu) ? 0 : ARRAY_SIZE(kvm_arm_fw_reg_ids);
>   }
>   
>   int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>   {
>   	int i;
>   
> -	for (i = 0; i < ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {
> +	for (i = 0; i < kvm_arm_get_fw_num_regs(vcpu); i++) {
>   		if (put_user(kvm_arm_fw_reg_ids[i], uindices++))
>   			return -EFAULT;
>   	}
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 44aae52c473d7..47ebde943a09e 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -5638,18 +5638,18 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
>   				    sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
>   }
>   
> -static unsigned int num_demux_regs(void)
> +static inline unsigned int num_demux_regs(struct kvm_vcpu *vcpu)
>   {
> -	return CSSELR_MAX;
> +	return vcpu_is_rec(vcpu) ? 0 : CSSELR_MAX;
>   }
>   
> -static int write_demux_regids(u64 __user *uindices)
> +static int write_demux_regids(struct kvm_vcpu *vcpu, u64 __user *uindices)
>   {
>   	u64 val = KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
>   	unsigned int i;
>   
>   	val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
> -	for (i = 0; i < CSSELR_MAX; i++) {
> +	for (i = 0; i < num_demux_regs(vcpu); i++) {
>   		if (put_user(val | i, uindices))
>   			return -EFAULT;
>   		uindices++;
> @@ -5693,11 +5693,27 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
>   	return true;
>   }
>   
> +static inline bool kvm_realm_sys_reg_hidden_user(const struct kvm_vcpu *vcpu,
> +						 u64 reg)
> +{
> +	if (!vcpu_is_rec(vcpu))
> +		return false;
> +
> +	switch (reg) {
> +	case SYS_ID_AA64DFR0_EL1:
> +		return false;
> +	}
> +	return true;
> +}
> +
>   static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
>   			    const struct sys_reg_desc *rd,
>   			    u64 __user **uind,
>   			    unsigned int *total)
>   {
> +	if (kvm_realm_sys_reg_hidden_user(vcpu, reg_to_encoding(rd)))
> +		return 0;
> +
>   	/*
>   	 * Ignore registers we trap but don't save,
>   	 * and for which no custom user accessor is provided.
> @@ -5735,7 +5751,7 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
>   
>   unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
>   {
> -	return num_demux_regs()
> +	return num_demux_regs(vcpu)
>   		+ walk_sys_regs(vcpu, (u64 __user *)NULL);
>   }
>   
> @@ -5748,7 +5764,7 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>   		return err;
>   	uindices += err;
>   
> -	return write_demux_regids(uindices);
> +	return write_demux_regids(vcpu, uindices);
>   }
>   
>   #define KVM_ARM_FEATURE_ID_RANGE_INDEX(r)			\

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization
  2026-10-06  5:35   ` Gavin Shan
@ 2026-10-06  5:57     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  5:57 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 06/10/2026 06:35, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>
>> Userspace must configure the SVE vector length before the Realm is 
>> created
>> (as it is part of the parameter for Realm creation), but the Realm VCPUs
>> cannot be finalized until after the Realm Descriptor has been created.
>>
>> KVM_GET_REG_LIST currently rejects the unfinalized VCPUs, which prevents
>> the userspace from discovering and configuring the VLs for  the Realm.
>>
>> Allow KVM_GET_REG_LIST for unfinalized RECs and make the SVE register
>> enumeration handle the unfinalized case explicitly. i.e., only expose
>> KVM_REG_ARM64_SVE_VLS before SVE is finalized.
>>
>> One adverse side effect of this change is that a KVM_GET_REG_LIST call 
>> that
>> only probes for the array size will now succeed even if SVE is not
>> finalized, but that seems harmless since the following KVM_GET_REG_LIST
>> with the full array will fail.
>>
>> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>>   arch/arm64/kvm/arm.c   | 15 ++++++++++++++-
>>   arch/arm64/kvm/guest.c | 10 +++++-----
>>   2 files changed, 19 insertions(+), 6 deletions(-)
>>
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index fe707a0c47308..d99e7818f5894 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -2004,6 +2004,19 @@ static int kvm_arm_vcpu_set_events(struct 
>> kvm_vcpu *vcpu,
>>       return __kvm_arm_vcpu_set_events(vcpu, events);
>>   }
>> +/*
>> + * Realm VCPUs can be finalized only after the Realm descriptor is 
>> created.
>> + * But in order to seal the SVE VL, we need to allow the userspace to 
>> read/write
>> + * to the SVE_VL, before everything is finalized.
>> + * Allow the register list for RECs before the VCPUs are finalized.
>> + */
>> +static bool kvm_arm_vcpu_reg_list_allowed(struct kvm_vcpu *vcpu)
>> +{
>> +    if (kvm_arm_vcpu_is_finalized(vcpu))
>> +        return true;
>> +    return vcpu_is_rec(vcpu);
>> +}
>> +
> 
> Needn't to keep this helper, and the code can be integrated to 
> kvm_arch_vcpu_ioctl(),
> see below.
> 
>>   long kvm_arch_vcpu_ioctl(struct file *filp,
>>                unsigned int ioctl, unsigned long arg)
>>   {
>> @@ -2059,7 +2072,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>>               break;
>>           r = -EPERM;
>> -        if (!kvm_arm_vcpu_is_finalized(vcpu))
>> +        if (!kvm_arm_vcpu_reg_list_allowed(vcpu))
>>               break;
> 
> Needn't to keep the helper kvm_arm_vcpu_reg_list_allowed() after its 
> logic is
> combined to kvm_arch_vcpu_ioctl().
> 
>          /*
>           * Realm vCPUs can be finalized only after the Realm descriptor 
> is created.
>           * We need to allow access KVM_REG_ARM64_SVE_VLS before that so 
> that the
>           * register can be sealed.
>           */
>          if (!(vcpu_is_rec(vcpu) || kvm_arm_vcpu_is_finalized(vcpu))
>              break;


Wanted to keep the ioctl handling section cleaner and easier to read.
Hence the wrapper. The name is intuitive enough and compiler can do
away with inlining.

> 
>>           r = -EFAULT;
>> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
>> index b01d6622b8720..c3ca369882273 100644
>> --- a/arch/arm64/kvm/guest.c
>> +++ b/arch/arm64/kvm/guest.c
>> @@ -598,8 +598,8 @@ static unsigned long num_sve_regs(const struct 
>> kvm_vcpu *vcpu)
>>       if (!vcpu_has_sve(vcpu))
>>           return 0;
>> -    /* Policed by KVM_GET_REG_LIST: */
>> -    WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
>> +    if (!kvm_arm_vcpu_sve_finalized(vcpu))
>> +        return 1; /* KVM_REG_ARM64_SVE_VLS */
> 
> Question: After a (Rec) vCPU is finalized, the returned number of 
> registers won't
> be 1. Is this expected? Otherwise, we need to use vcpu_is_rec() here.
> 
>      /*
>       * KVM_REG_ARM64_SVE_VLS is visible for realm vCPUs no matter if
>       * they have been finalized.
>       */
>      if (vcpu_is_rec(vcpu))
>          return 1;

This is addressed in the next patch.

Cheers
Suzuki


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms
  2026-10-06  5:47   ` Gavin Shan
@ 2026-10-06  6:01     ` Suzuki K Poulose
  2026-10-06  6:16       ` Gavin Shan
  2026-10-06  6:23       ` Gavin Shan
  0 siblings, 2 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  6:01 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 06/10/2026 06:47, Gavin Shan wrote:
> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>
>> The RMM restricts the access to the register states that the host can
>> read/modify for a given Realm.
>>
>> e.g., At VCPU creation, can modify GPRS (x0-x30) and PC.
>>        While servicing SMCCC calls via RSI_HOST_CALL or servicing PSCI
>>        requests.
>>        MMIO emulation in the unprotected space.
>>
>> Additionally we use the sysreg configuration to advertise/configure the
>> following Realm parameters, which are required before the Realm 
>> Descriptor
>> is created:
>>   - SVE Vector Length
>>   - Number of HW Breakpoints/Watchpoints
>>   - PMU Counters.
>>
>> Thus KVM also additionally allows access to ID_AA64DFR0_EL1 and 
>> SVE_VLS for
>> the configuration of Realm creation parameters. We don't support PMUs for
>> the Realm VMs yet, so PMCR is not exposed.
>>
>> The RMM makes similar restrictions for reading of the guest's registers
>> (this is *confidential* compute after all), however we don't impose the
>> restriction here. This allows the VMM to read (stale) values from the
>> registers which might be useful to read back the initial values even if
>> the RMM doesn't provide the latest version. For migration of a realm VM,
>> a new interface will be needed so that the VMM can receive an
>> (encrypted) blob of the VM's state.
>>
>> Reflect the above in KVM_GET_REG_LIST, KVM_SET_ONE_REG calls.

>>   static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, 
>> u64 off)
>>   {
>>       int size;
>> @@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct 
>> kvm_vcpu *vcpu,
>>           u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
>>           int size = core_reg_size_from_offset(vcpu, i);
>> +        if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
>> +            continue;
>> +
>>           if (size < 0)
>>               continue;
>> @@ -598,6 +620,9 @@ static unsigned long num_sve_regs(const struct 
>> kvm_vcpu *vcpu)
>>       if (!vcpu_has_sve(vcpu))
>>           return 0;
>> +    if (kvm_vm_is_realm(vcpu->kvm))
>> +        return 1; /* KVM_REG_ARM64_SVE_VLS */
>> +
>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>           return 1; /* KVM_REG_ARM64_SVE_VLS */
>>
> 
> Aren't above two checks conflicting to each other?

Do they? We allow SVE_VLS only for the Realms and we allow that
before the vCPUs are finalized. For normal VMs, depending on
whether the vcpus are finalized, we either send 1 or the full list.

> 
>> @@ -625,6 +650,10 @@ static int copy_sve_reg_indices(const struct 
>> kvm_vcpu *vcpu,
>>           return -EFAULT;
>>       ++num_regs;
>> +    /* For Realms only support SVE_VLS */
>> +    if (kvm_vm_is_realm(vcpu->kvm))
>> +        return num_regs;
>> +
>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>           return num_regs;
> 
> Same question here.

As above.

Suzuki

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms
  2026-10-06  6:01     ` Suzuki K Poulose
@ 2026-10-06  6:16       ` Gavin Shan
  2026-10-06 12:36         ` Suzuki K Poulose
  2026-10-06  6:23       ` Gavin Shan
  1 sibling, 1 reply; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  6:16 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 10/6/26 4:01 PM, Suzuki K Poulose wrote:
> On 06/10/2026 06:47, Gavin Shan wrote:
>> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>>
>>> The RMM restricts the access to the register states that the host can
>>> read/modify for a given Realm.
>>>
>>> e.g., At VCPU creation, can modify GPRS (x0-x30) and PC.
>>>        While servicing SMCCC calls via RSI_HOST_CALL or servicing PSCI
>>>        requests.
>>>        MMIO emulation in the unprotected space.
>>>
>>> Additionally we use the sysreg configuration to advertise/configure the
>>> following Realm parameters, which are required before the Realm Descriptor
>>> is created:
>>>   - SVE Vector Length
>>>   - Number of HW Breakpoints/Watchpoints
>>>   - PMU Counters.
>>>
>>> Thus KVM also additionally allows access to ID_AA64DFR0_EL1 and SVE_VLS for
>>> the configuration of Realm creation parameters. We don't support PMUs for
>>> the Realm VMs yet, so PMCR is not exposed.
>>>
>>> The RMM makes similar restrictions for reading of the guest's registers
>>> (this is *confidential* compute after all), however we don't impose the
>>> restriction here. This allows the VMM to read (stale) values from the
>>> registers which might be useful to read back the initial values even if
>>> the RMM doesn't provide the latest version. For migration of a realm VM,
>>> a new interface will be needed so that the VMM can receive an
>>> (encrypted) blob of the VM's state.
>>>
>>> Reflect the above in KVM_GET_REG_LIST, KVM_SET_ONE_REG calls.
> 
>>>   static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
>>>   {
>>>       int size;
>>> @@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,
>>>           u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
>>>           int size = core_reg_size_from_offset(vcpu, i);
>>> +        if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
>>> +            continue;
>>> +
>>>           if (size < 0)
>>>               continue;
>>> @@ -598,6 +620,9 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
>>>       if (!vcpu_has_sve(vcpu))
>>>           return 0;
>>> +    if (kvm_vm_is_realm(vcpu->kvm))
>>> +        return 1; /* KVM_REG_ARM64_SVE_VLS */
>>> +
>>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>>           return 1; /* KVM_REG_ARM64_SVE_VLS */
>>>
>>
>> Aren't above two checks conflicting to each other?
> 
> Do they? We allow SVE_VLS only for the Realms and we allow that
> before the vCPUs are finalized. For normal VMs, depending on
> whether the vcpus are finalized, we either send 1 or the full list.
> 

num_sve_regs() can be called for 3 cases: (a) non-finalized RECs; (b) finalized
RECs; (c) Other finalized vCPUs, correct? "if (kvm_vm_is_realm(vcpu->kvm))", which
would be "if (vcpu_is_rec(vcpu))", covers (a) and (b). We needn't the excessive
check "if (!kvm_arm_vcpu_sve_finalized(vcpu))". So the check would be something
as below after this series is applied:

	/*
	 * KVM_REG_ARM64_SVE_VLS is visible on realm vCPU no matter if it
	 * has been finalized.
	 */
	if (vcpu_is_rec(vcpu))
		return 1;

This check "if (vcpu_is_rec(vcpu))" belongs to PATCH[22]. Hope I make myself
clear this time :)

>>
>>> @@ -625,6 +650,10 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
>>>           return -EFAULT;
>>>       ++num_regs;
>>> +    /* For Realms only support SVE_VLS */
>>> +    if (kvm_vm_is_realm(vcpu->kvm))
>>> +        return num_regs;
>>> +
>>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>>           return num_regs;
>>
>> Same question here.
> 
> As above.
> 
> Suzuki

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms
  2026-10-06  6:01     ` Suzuki K Poulose
  2026-10-06  6:16       ` Gavin Shan
@ 2026-10-06  6:23       ` Gavin Shan
  1 sibling, 0 replies; 61+ messages in thread
From: Gavin Shan @ 2026-10-06  6:23 UTC (permalink / raw)
  To: Suzuki K Poulose, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 10/6/26 4:01 PM, Suzuki K Poulose wrote:
> On 06/10/2026 06:47, Gavin Shan wrote:
>> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>>
>>> The RMM restricts the access to the register states that the host can
>>> read/modify for a given Realm.
>>>
>>> e.g., At VCPU creation, can modify GPRS (x0-x30) and PC.
>>>        While servicing SMCCC calls via RSI_HOST_CALL or servicing PSCI
>>>        requests.
>>>        MMIO emulation in the unprotected space.
>>>
>>> Additionally we use the sysreg configuration to advertise/configure the
>>> following Realm parameters, which are required before the Realm Descriptor
>>> is created:
>>>   - SVE Vector Length
>>>   - Number of HW Breakpoints/Watchpoints
>>>   - PMU Counters.
>>>
>>> Thus KVM also additionally allows access to ID_AA64DFR0_EL1 and SVE_VLS for
>>> the configuration of Realm creation parameters. We don't support PMUs for
>>> the Realm VMs yet, so PMCR is not exposed.
>>>
>>> The RMM makes similar restrictions for reading of the guest's registers
>>> (this is *confidential* compute after all), however we don't impose the
>>> restriction here. This allows the VMM to read (stale) values from the
>>> registers which might be useful to read back the initial values even if
>>> the RMM doesn't provide the latest version. For migration of a realm VM,
>>> a new interface will be needed so that the VMM can receive an
>>> (encrypted) blob of the VM's state.
>>>
>>> Reflect the above in KVM_GET_REG_LIST, KVM_SET_ONE_REG calls.
> 
>>>   static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
>>>   {
>>>       int size;
>>> @@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,
>>>           u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
>>>           int size = core_reg_size_from_offset(vcpu, i);
>>> +        if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
>>> +            continue;
>>> +
>>>           if (size < 0)
>>>               continue;
>>> @@ -598,6 +620,9 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
>>>       if (!vcpu_has_sve(vcpu))
>>>           return 0;
>>> +    if (kvm_vm_is_realm(vcpu->kvm))
>>> +        return 1; /* KVM_REG_ARM64_SVE_VLS */
>>> +
>>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>>           return 1; /* KVM_REG_ARM64_SVE_VLS */
>>>
>>
>> Aren't above two checks conflicting to each other?
> 
> Do they? We allow SVE_VLS only for the Realms and we allow that
> before the vCPUs are finalized. For normal VMs, depending on
> whether the vcpus are finalized, we either send 1 or the full list.
> 

num_sve_regs() can be called for 3 cases: (a) non-finalized realm vCPU; (b) finalized
realm vCPU; (c) Other finalized vCPUs, correct? The check "if (kvm_vm_is_realm(vcpu->kvm))",
which would be "if (vcpu_is_rec(vcpu))", covers (a) and (b). The second check
"if (!kvm_arm_vcpu_sve_finalized(vcpu))" isn't used. The check here would be something
as below after this series is applied:

	/*
	 * Only KVM_REG_ARM64_SVE_VLS is visible on realm vCPUs no matter if
	 * they have been finalized.
	 */
	if (vcpu_is_rec(vcpu))
		return 1;

Besides, this check "if (vcpu_is_rec(vcpu))" belongs to PATCH[22]. Hope I make myself
clear :-)

>>
>>> @@ -625,6 +650,10 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
>>>           return -EFAULT;
>>>       ++num_regs;
>>> +    /* For Realms only support SVE_VLS */
>>> +    if (kvm_vm_is_realm(vcpu->kvm))
>>> +        return num_regs;
>>> +
>>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>>           return num_regs;
>>
>> Same question here.
> 
> As above.
> 
> Suzuki

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch
  2026-10-05  9:07 ` [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch Suzuki K Poulose
  2026-10-06  3:55   ` Gavin Shan
@ 2026-10-06  8:33   ` Marc Zyngier
  2026-10-06  8:49     ` Suzuki K Poulose
  1 sibling, 1 reply; 61+ messages in thread
From: Marc Zyngier @ 2026-10-06  8:33 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvm, kvmarm, will, catalin.marinas, linux-kernel,
	linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
	joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
	sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
	enju.kohei, sudeep.holla, jonathan.cameron

On Mon, 05 Oct 2026 10:07:36 +0100,
Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> 
> KVM arm64 has different types of VMs with all the different modes in which
> the hypervisor code can be run. e.g., VHE, nVHE, pKVM etc. Then there is
> protected VM and normal VMs with pKVM. We might soon add other types,
> e.g., Arm CCA Realm. So in an effort to make the handling of these
> different types of VMs a bit more friendly to the eyes, add a VM flavor to
> the kvm_arch and we could then add handlers for different operations based
> on the VM type.
> 
> Keep the flavor initialisation at the beginning to allow for the detection
> early enough and fail out on any unsupported requests.
> 
> With that, add wrappers for checking the "type" of a VM and replace the
> existing users with the new wrappers.
> 
> Given we already have the construct of "kvm_vm_is_protected" in the core
> KVM code, use that for all confidential compute guests including Realms
> that we are about to add. Adds __VM_PROTECTED marker vm flavor to draw the
> boundary for "protected VMs". In later patches, we would add Realm VMs,
>  which would also be classified as protected.
> 
> Add a explicit helper to detect if a given VM is a "protected" VM under pKVM.
> Change the existing users that precisely want to check the VM type. These
> include :
>   - kvm_arch_prepare_memory_region - For preventing memslot changes after
>     pVM creation.
> 
> All the others are retained as a wider check for confidential guest VMs.
> These are:
>  - kvm_vm_ioctl_set_counter_offset - For disallowing timer offset
>    configuration
>  - io_mem_abort for dabt handling without valid syndrome information
> 
> Both of which are true for Realms too.
> 
> Realms support is restricted to VHE host and thus "kvm_vm_is_protected()"
> checks in the pkvm hyp specific code doesn't need to change, as the only
> protected guests it deals with is "protected pKVM" guests. To tighten this
> init_pkvm_hyp_vm() restricts the hyp copy of the vm_flavor to the ones it
> supports.
> 
> vcpu_is_protected() usage from nVHE hyp code is tricky, as we need to
> convert the vcpu->kvm to the HYP VA before checking the flavor. This
> involves kern_hyp_va() usage in asm/kvm_host.h. To avoid build breaks,
> include asm/kvm_mmu.h to arm64/kvm/mmio.c.
> 
> While at it move the psci_version around to keep the structure packed.
> 
> Suggested-by: Marc Zyngier <maz@kernel.org>
> Tested-by: Gavin Shan <gshan@redhat.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v21:
>  - Drop kern_hyp_va() and restrict nvhe code to always use vcpu_is_protected_pkvm()
>  - Drop kvm_vm_is_unprotected_pkvm() and open code the check
>  - Move psci_version field in kvm_arch around to keep the structure packed
> ---
>  arch/arm64/include/asm/kvm_host.h      | 42 +++++++++++++++++++++++---
>  arch/arm64/include/asm/kvm_pkvm.h      |  4 +--
>  arch/arm64/kvm/arm.c                   | 33 ++++++++++++++++----
>  arch/arm64/kvm/hyp/include/nvhe/pkvm.h |  2 +-
>  arch/arm64/kvm/hyp/nvhe/pkvm.c         |  6 +++-
>  arch/arm64/kvm/hyp/nvhe/switch.c       |  4 +--
>  arch/arm64/kvm/hyp/nvhe/timer-sr.c     |  2 +-
>  arch/arm64/kvm/mmio.c                  |  1 +
>  arch/arm64/kvm/mmu.c                   |  2 +-
>  arch/arm64/kvm/pkvm.c                  |  6 ++--
>  10 files changed, 79 insertions(+), 23 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 286489a69dff5..dedb5df15a803 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -257,7 +257,6 @@ struct kvm_protected_vm {
>  	pkvm_handle_t handle;
>  	struct kvm_hyp_memcache teardown_mc;
>  	struct kvm_hyp_memcache stage2_teardown_mc;
> -	bool is_protected;
>  	bool is_created;
>  
>  	/*
> @@ -306,9 +305,22 @@ enum fgt_group_id {
>  	__NR_FGT_GROUP_IDS__
>  };
>  
> +enum kvm_arm_vm_flavor {
> +	VM_NVHE,
> +	VM_VHE,
> +	VM_PKVM,		/* Normal guests on pKVM */
> +	MARKER(__VM_PROTECTED),
> +	VM_PROTECTED_PKVM,	/* Protected VM */
> +	VM_FLAVOR_MAX
> +};
> +
>  struct kvm_arch {
>  	struct kvm_s2_mmu mmu;
>  
> +	enum kvm_arm_vm_flavor vm_flavor;
> +	/* Mandated version of PSCI */
> +	u32 psci_version;
> +
>  	/*
>  	 * Fine-Grained UNDEF, mimicking the FGT layout defined by the
>  	 * architecture. We track them globally, as we present the
> @@ -332,9 +344,6 @@ struct kvm_arch {
>  	/* Timers */
>  	struct arch_timer_vm_data timer_data;
>  
> -	/* Mandated version of PSCI */
> -	u32 psci_version;
> -
>  	/* Protects VM-scoped configuration data */
>  	struct mutex config_lock;
>  
> @@ -1504,9 +1513,32 @@ struct kvm *kvm_arch_alloc_vm(void);
>  
>  #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE
>  
> -#define kvm_vm_is_protected(kvm)	(is_protected_kvm_enabled() && (kvm)->arch.pkvm.is_protected)
> +#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor >= __VM_PROTECTED)
>  
> +#ifdef __KVM_NVHE_HYPERVISOR__
> +/*
> + * Accessing vcpu->kvm from nVHE hyp stub is tricky, as we need to convert the
> + * pointer to the hyp VA. With pKVM, the nVHE code runs with the hyp_vcpu,
> + * which is populated correctly. Always vcpu_is_protected_pkvm(), which is

Always *use*?

> + * gated on is_protected_kvm_enabled().
> + */
> +#define vcpu_is_protected(vcpu)		BUILD_BUG_ON(1)
> +#else
>  #define vcpu_is_protected(vcpu)		kvm_vm_is_protected((vcpu)->kvm)
> +#endif
> +
> +#define kvm_vm_is_protected_pkvm(kvm)					\
> +	(is_protected_kvm_enabled() && ((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM))
> +/*
> + * Rely on is_protected_kvm_enabled() check in kvm_vm_is_protected_pkvm() to
> + * make sure the vcpu->kvm is always valid VA in the context
> + */
> +#define vcpu_is_protected_pkvm(vcpu)					\
> +	({								\
> +		struct kvm *__kvm = READ_ONCE((vcpu)->kvm);		\
> +									\
> +		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
> +	})

But why do we have to have this pkvm-specific stuff? I thought we had
established it is not necessary in [1].

I really want to avoid any backend-specific helper, as it really gets
in the way of maintainability.

	M.

[1] https://lore.kernel.org/r/86pkxs2npx.wl-maz@kernel.org


-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch
  2026-10-06  8:33   ` Marc Zyngier
@ 2026-10-06  8:49     ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06  8:49 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm, kvmarm, will, catalin.marinas, linux-kernel,
	linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
	joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
	sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
	enju.kohei, sudeep.holla, jonathan.cameron

On 06/10/2026 09:33, Marc Zyngier wrote:
> On Mon, 05 Oct 2026 10:07:36 +0100,
> Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>>
>> KVM arm64 has different types of VMs with all the different modes in which
>> the hypervisor code can be run. e.g., VHE, nVHE, pKVM etc. Then there is
>> protected VM and normal VMs with pKVM. We might soon add other types,
>> e.g., Arm CCA Realm. So in an effort to make the handling of these
>> different types of VMs a bit more friendly to the eyes, add a VM flavor to
>> the kvm_arch and we could then add handlers for different operations based
>> on the VM type.
>>
>> Keep the flavor initialisation at the beginning to allow for the detection
>> early enough and fail out on any unsupported requests.
>>
>> With that, add wrappers for checking the "type" of a VM and replace the
>> existing users with the new wrappers.
>>
>> Given we already have the construct of "kvm_vm_is_protected" in the core
>> KVM code, use that for all confidential compute guests including Realms
>> that we are about to add. Adds __VM_PROTECTED marker vm flavor to draw the
>> boundary for "protected VMs". In later patches, we would add Realm VMs,
>>   which would also be classified as protected.
>>
>> Add a explicit helper to detect if a given VM is a "protected" VM under pKVM.
>> Change the existing users that precisely want to check the VM type. These
>> include :
>>    - kvm_arch_prepare_memory_region - For preventing memslot changes after
>>      pVM creation.
>>
>> All the others are retained as a wider check for confidential guest VMs.
>> These are:
>>   - kvm_vm_ioctl_set_counter_offset - For disallowing timer offset
>>     configuration
>>   - io_mem_abort for dabt handling without valid syndrome information
>>
>> Both of which are true for Realms too.
>>
>> Realms support is restricted to VHE host and thus "kvm_vm_is_protected()"
>> checks in the pkvm hyp specific code doesn't need to change, as the only
>> protected guests it deals with is "protected pKVM" guests. To tighten this
>> init_pkvm_hyp_vm() restricts the hyp copy of the vm_flavor to the ones it
>> supports.
>>
>> vcpu_is_protected() usage from nVHE hyp code is tricky, as we need to
>> convert the vcpu->kvm to the HYP VA before checking the flavor. This
>> involves kern_hyp_va() usage in asm/kvm_host.h. To avoid build breaks,
>> include asm/kvm_mmu.h to arm64/kvm/mmio.c.
>>
>> While at it move the psci_version around to keep the structure packed.
>>
>> Suggested-by: Marc Zyngier <maz@kernel.org>
>> Tested-by: Gavin Shan <gshan@redhat.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v21:
>>   - Drop kern_hyp_va() and restrict nvhe code to always use vcpu_is_protected_pkvm()
>>   - Drop kvm_vm_is_unprotected_pkvm() and open code the check
>>   - Move psci_version field in kvm_arch around to keep the structure packed
>> ---
>>   arch/arm64/include/asm/kvm_host.h      | 42 +++++++++++++++++++++++---
>>   arch/arm64/include/asm/kvm_pkvm.h      |  4 +--
>>   arch/arm64/kvm/arm.c                   | 33 ++++++++++++++++----
>>   arch/arm64/kvm/hyp/include/nvhe/pkvm.h |  2 +-
>>   arch/arm64/kvm/hyp/nvhe/pkvm.c         |  6 +++-
>>   arch/arm64/kvm/hyp/nvhe/switch.c       |  4 +--
>>   arch/arm64/kvm/hyp/nvhe/timer-sr.c     |  2 +-
>>   arch/arm64/kvm/mmio.c                  |  1 +
>>   arch/arm64/kvm/mmu.c                   |  2 +-
>>   arch/arm64/kvm/pkvm.c                  |  6 ++--
>>   10 files changed, 79 insertions(+), 23 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>> index 286489a69dff5..dedb5df15a803 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -257,7 +257,6 @@ struct kvm_protected_vm {
>>   	pkvm_handle_t handle;
>>   	struct kvm_hyp_memcache teardown_mc;
>>   	struct kvm_hyp_memcache stage2_teardown_mc;
>> -	bool is_protected;
>>   	bool is_created;
>>   
>>   	/*
>> @@ -306,9 +305,22 @@ enum fgt_group_id {
>>   	__NR_FGT_GROUP_IDS__
>>   };
>>   
>> +enum kvm_arm_vm_flavor {
>> +	VM_NVHE,
>> +	VM_VHE,
>> +	VM_PKVM,		/* Normal guests on pKVM */
>> +	MARKER(__VM_PROTECTED),
>> +	VM_PROTECTED_PKVM,	/* Protected VM */
>> +	VM_FLAVOR_MAX
>> +};
>> +
>>   struct kvm_arch {
>>   	struct kvm_s2_mmu mmu;
>>   
>> +	enum kvm_arm_vm_flavor vm_flavor;
>> +	/* Mandated version of PSCI */
>> +	u32 psci_version;
>> +
>>   	/*
>>   	 * Fine-Grained UNDEF, mimicking the FGT layout defined by the
>>   	 * architecture. We track them globally, as we present the
>> @@ -332,9 +344,6 @@ struct kvm_arch {
>>   	/* Timers */
>>   	struct arch_timer_vm_data timer_data;
>>   
>> -	/* Mandated version of PSCI */
>> -	u32 psci_version;
>> -
>>   	/* Protects VM-scoped configuration data */
>>   	struct mutex config_lock;
>>   
>> @@ -1504,9 +1513,32 @@ struct kvm *kvm_arch_alloc_vm(void);
>>   
>>   #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE
>>   
>> -#define kvm_vm_is_protected(kvm)	(is_protected_kvm_enabled() && (kvm)->arch.pkvm.is_protected)
>> +#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor >= __VM_PROTECTED)
>>   
>> +#ifdef __KVM_NVHE_HYPERVISOR__
>> +/*
>> + * Accessing vcpu->kvm from nVHE hyp stub is tricky, as we need to convert the
>> + * pointer to the hyp VA. With pKVM, the nVHE code runs with the hyp_vcpu,
>> + * which is populated correctly. Always vcpu_is_protected_pkvm(), which is
> 
> Always *use*? 

Ack

> 
>> + * gated on is_protected_kvm_enabled().
>> + */
>> +#define vcpu_is_protected(vcpu)		BUILD_BUG_ON(1)
>> +#else
>>   #define vcpu_is_protected(vcpu)		kvm_vm_is_protected((vcpu)->kvm)
>> +#endif
>> +
>> +#define kvm_vm_is_protected_pkvm(kvm)					\
>> +	(is_protected_kvm_enabled() && ((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM))
>> +/*
>> + * Rely on is_protected_kvm_enabled() check in kvm_vm_is_protected_pkvm() to
>> + * make sure the vcpu->kvm is always valid VA in the context
>> + */
>> +#define vcpu_is_protected_pkvm(vcpu)					\
>> +	({								\
>> +		struct kvm *__kvm = READ_ONCE((vcpu)->kvm);		\
>> +									\
>> +		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
>> +	})
> 
> But why do we have to have this pkvm-specific stuff? I thought we had
> established it is not necessary in [1].

My bad. I need to wear the glasses :-(

> 
> I really want to avoid any backend-specific helper, as it really gets
> in the way of maintainability.

Agreed. I will fix this. Apologies

Cheers
Suzuki

> 
> 	M.
> 
> [1] https://lore.kernel.org/r/86pkxs2npx.wl-maz@kernel.org
> 
> 


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types
  2026-10-05  9:07 ` [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types Suzuki K Poulose
  2026-10-06  2:24   ` Gavin Shan
  2026-10-06  2:25   ` Gavin Shan
@ 2026-10-06  8:50   ` Marc Zyngier
  2 siblings, 0 replies; 61+ messages in thread
From: Marc Zyngier @ 2026-10-06  8:50 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvm, kvmarm, will, catalin.marinas, linux-kernel,
	linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
	joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
	sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
	enju.kohei, sudeep.holla, jonathan.cameron

On Mon, 05 Oct 2026 10:07:40 +0100,
Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> 
> Prevent unsupported VCPU features for the protected VCPUs. Realms and pVMs
> not support 32bit EL1 or NV yet. pKVM doesn't rely on the host vcpu
> features and it clears the unsupported features while hyp_vcpu is
> initialised. Block the features early in the vcpu init if we detect
> incompatible features.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
>  arch/arm64/kvm/arm.c | 10 ++++++----
>  1 file changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index f31d31fa27ad9..9c2ef7ca6a961 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -1668,11 +1668,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
>  	return -EINVAL;
>  }
>  
> -static unsigned long system_supported_vcpu_features(void)
> +static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
>  {
>  	unsigned long features = KVM_VCPU_VALID_FEATURES;
>  
> -	if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
> +	if (vcpu_is_protected(vcpu) ||
> +	    !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
>  		clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
>

This looks a bit daft. Why don't you simply use different base feature
sets for the various backends? The nothing else need to change.

	M.

-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations
  2026-10-05  9:07 ` [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations Suzuki K Poulose
  2026-10-06  3:00   ` Gavin Shan
@ 2026-10-06  9:24   ` Marc Zyngier
  2026-10-06 10:36     ` Suzuki K Poulose
  1 sibling, 1 reply; 61+ messages in thread
From: Marc Zyngier @ 2026-10-06  9:24 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvm, kvmarm, will, catalin.marinas, linux-kernel,
	linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
	joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
	sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
	enju.kohei, sudeep.holla, jonathan.cameron

On Mon, 05 Oct 2026 10:07:42 +0100,
Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> 
> Add VM type specific S2 MMU operation backends which can be initialized per
> VM flavor, to keep the handling cleaner.
> 
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Change since v21:
>  - Define all vm_s2_ops call back. All calls are mandatory.
>  - Define callback for each flavor, disjointing the non-protetcted pKVM and
>    normal KVM (VHE & nVHE) and remove the KVM_PGT_FN() hacks.

It is a bit annoying that we still have part of the operations being
indirected by kvm_vm_s2_ops, and others by KVM_PGT_FN(), which is
still there. I was hoping that we'd have only one indirection. after
this patch.

	M.

-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations
  2026-10-06  9:24   ` Marc Zyngier
@ 2026-10-06 10:36     ` Suzuki K Poulose
  2026-10-06 15:14       ` Suzuki K Poulose
  0 siblings, 1 reply; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06 10:36 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm, kvmarm, will, catalin.marinas, linux-kernel,
	linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
	joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
	sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
	enju.kohei, sudeep.holla, jonathan.cameron

On 06/10/2026 10:24, Marc Zyngier wrote:
> On Mon, 05 Oct 2026 10:07:42 +0100,
> Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>>
>> Add VM type specific S2 MMU operation backends which can be initialized per
>> VM flavor, to keep the handling cleaner.
>>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Change since v21:
>>   - Define all vm_s2_ops call back. All calls are mandatory.
>>   - Define callback for each flavor, disjointing the non-protetcted pKVM and
>>     normal KVM (VHE & nVHE) and remove the KVM_PGT_FN() hacks.
> 
> It is a bit annoying that we still have part of the operations being
> indirected by kvm_vm_s2_ops, and others by KVM_PGT_FN(), which is
> still there. I was hoping that we'd have only one indirection. after
> this patch.

I agree and I did think about it. The issue is, some of these calls are
deep burried from the higher leve dispatcher callbacks (e.g.,
user_mem_abort->kvm_pgtable_stage2_map). We could go all in and remove
all of them if you are happy with that change. Also, some of the call
paths aren't valid for certain VM types, that adds quite a lot of dummy
callbacks (now that they all are mandatory. e.g., pVM or Realms).


Cheers
Suzuki

> 
> 	M.
> 


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms
  2026-10-06  6:16       ` Gavin Shan
@ 2026-10-06 12:36         ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06 12:36 UTC (permalink / raw)
  To: Gavin Shan, kvm, kvmarm
  Cc: maz, will, catalin.marinas, linux-kernel, linux-arm-kernel,
	steven.price, aneesh.kumar, oupton, joey.gouly, tabba, yuzenghui,
	linux-coco, gankulkarni, sdonthineni, alpergun, fj0570is,
	WeiLin.Chang, lpieralisi, enju.kohei, sudeep.holla,
	jonathan.cameron, Jean-Philippe Brucker

On 06/10/2026 07:16, Gavin Shan wrote:
> On 10/6/26 4:01 PM, Suzuki K Poulose wrote:
>> On 06/10/2026 06:47, Gavin Shan wrote:
>>> On 10/5/26 7:07 PM, Suzuki K Poulose wrote:
>>>> From: Jean-Philippe Brucker <jean-philippe@linaro.org>
>>>>
>>>> The RMM restricts the access to the register states that the host can
>>>> read/modify for a given Realm.
>>>>
>>>> e.g., At VCPU creation, can modify GPRS (x0-x30) and PC.
>>>>        While servicing SMCCC calls via RSI_HOST_CALL or servicing PSCI
>>>>        requests.
>>>>        MMIO emulation in the unprotected space.
>>>>
>>>> Additionally we use the sysreg configuration to advertise/configure the
>>>> following Realm parameters, which are required before the Realm 
>>>> Descriptor
>>>> is created:
>>>>   - SVE Vector Length
>>>>   - Number of HW Breakpoints/Watchpoints
>>>>   - PMU Counters.
>>>>
>>>> Thus KVM also additionally allows access to ID_AA64DFR0_EL1 and 
>>>> SVE_VLS for
>>>> the configuration of Realm creation parameters. We don't support 
>>>> PMUs for
>>>> the Realm VMs yet, so PMCR is not exposed.
>>>>
>>>> The RMM makes similar restrictions for reading of the guest's registers
>>>> (this is *confidential* compute after all), however we don't impose the
>>>> restriction here. This allows the VMM to read (stale) values from the
>>>> registers which might be useful to read back the initial values even if
>>>> the RMM doesn't provide the latest version. For migration of a realm 
>>>> VM,
>>>> a new interface will be needed so that the VMM can receive an
>>>> (encrypted) blob of the VM's state.
>>>>
>>>> Reflect the above in KVM_GET_REG_LIST, KVM_SET_ONE_REG calls.
>>
>>>>   static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, 
>>>> u64 off)
>>>>   {
>>>>       int size;
>>>> @@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct 
>>>> kvm_vcpu *vcpu,
>>>>           u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
>>>>           int size = core_reg_size_from_offset(vcpu, i);
>>>> +        if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
>>>> +            continue;
>>>> +
>>>>           if (size < 0)
>>>>               continue;
>>>> @@ -598,6 +620,9 @@ static unsigned long num_sve_regs(const struct 
>>>> kvm_vcpu *vcpu)
>>>>       if (!vcpu_has_sve(vcpu))
>>>>           return 0;
>>>> +    if (kvm_vm_is_realm(vcpu->kvm))
>>>> +        return 1; /* KVM_REG_ARM64_SVE_VLS */
>>>> +

This could be vcpu_is_rec().
>>>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>>>           return 1; /* KVM_REG_ARM64_SVE_VLS */
>>>>
>>>
>>> Aren't above two checks conflicting to each other?
>>
>> Do they? We allow SVE_VLS only for the Realms and we allow that
>> before the vCPUs are finalized. For normal VMs, depending on
>> whether the vcpus are finalized, we either send 1 or the full list.
>>
> 
> num_sve_regs() can be called for 3 cases: (a) non-finalized RECs; (b) 
> finalized
> RECs; (c) Other finalized vCPUs, correct? "if (kvm_vm_is_realm(vcpu- 
>  >kvm))", which
> would be "if (vcpu_is_rec(vcpu))", covers (a) and (b). We needn't the 
> excessive
> check "if (!kvm_arm_vcpu_sve_finalized(vcpu))". So the check would be 
> something
> as below after this series is applied:
> 
>      /*
>       * KVM_REG_ARM64_SVE_VLS is visible on realm vCPU no matter if it
>       * has been finalized.
>       */
>      if (vcpu_is_rec(vcpu))
>          return 1;
> 
> This check "if (vcpu_is_rec(vcpu))" belongs to PATCH[22]. Hope I make 
> myself
> clear this time :)

Sure, these two patches are closely related and may be even could be
folded in. I split it out to make it easier to review.

22: Allow exposing SVE_VLS for unfinalized VCPU RECs
23: Control register accesses includingthe SVE_VLS, but prevent
everything else

Does it help ?

Cheers
Suzuki


> 
>>>
>>>> @@ -625,6 +650,10 @@ static int copy_sve_reg_indices(const struct 
>>>> kvm_vcpu *vcpu,
>>>>           return -EFAULT;
>>>>       ++num_regs;
>>>> +    /* For Realms only support SVE_VLS */
>>>> +    if (kvm_vm_is_realm(vcpu->kvm))
>>>> +        return num_regs;
>>>> +
>>>>       if (!kvm_arm_vcpu_sve_finalized(vcpu))
>>>>           return num_regs;
>>>
>>> Same question here.
>>
>> As above.
>>
>> Suzuki
> 
> Thanks,
> Gavin
> 


^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations
  2026-10-06 10:36     ` Suzuki K Poulose
@ 2026-10-06 15:14       ` Suzuki K Poulose
  0 siblings, 0 replies; 61+ messages in thread
From: Suzuki K Poulose @ 2026-10-06 15:14 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm, kvmarm, will, catalin.marinas, linux-kernel,
	linux-arm-kernel, steven.price, aneesh.kumar, oupton, gshan,
	joey.gouly, tabba, yuzenghui, linux-coco, gankulkarni,
	sdonthineni, alpergun, fj0570is, WeiLin.Chang, lpieralisi,
	enju.kohei, sudeep.holla, jonathan.cameron

On 06/10/2026 11:36, Suzuki K Poulose wrote:
> On 06/10/2026 10:24, Marc Zyngier wrote:
>> On Mon, 05 Oct 2026 10:07:42 +0100,
>> Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>>>
>>> Add VM type specific S2 MMU operation backends which can be 
>>> initialized per
>>> VM flavor, to keep the handling cleaner.
>>>
>>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>>> ---
>>> Change since v21:
>>>   - Define all vm_s2_ops call back. All calls are mandatory.
>>>   - Define callback for each flavor, disjointing the non-protetcted 
>>> pKVM and
>>>     normal KVM (VHE & nVHE) and remove the KVM_PGT_FN() hacks.
>>
>> It is a bit annoying that we still have part of the operations being
>> indirected by kvm_vm_s2_ops, and others by KVM_PGT_FN(), which is
>> still there. I was hoping that we'd have only one indirection. after
>> this patch.
> 
> I agree and I did think about it. The issue is, some of these calls are
> deep burried from the higher leve dispatcher callbacks (e.g.,
> user_mem_abort->kvm_pgtable_stage2_map). We could go all in and remove
> all of them if you are happy with that change. Also, some of the call
> paths aren't valid for certain VM types, that adds quite a lot of dummy
> callbacks (now that they all are mandatory. e.g., pVM or Realms).
> 

For the record, as discussed, there are callbacks that don't have a kvm
instance available, e.g. kvm_pgtable_stage2_free_unlinked() where we
only have a page and level. Further abstractions will be explored in a
future series.

Suzuki

> 
> Cheers
> Suzuki
> 
>>
>>     M.
>>
> 


^ permalink raw reply	[flat|nested] 61+ messages in thread

end of thread, other threads:[~2026-10-06 15:14 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-05  9:07 [PATCH v22 00/23] KVM: arm64: CCA: Add basic plumbing for Realms Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 01/23] KVM: arm64: protected VM: Handle user writes to CNTVCT_EL0/CNTPCT_EL0 Suzuki K Poulose
2026-10-06  0:02   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 02/23] KVM: arm64: Disable Steal time accounting for protected guests Suzuki K Poulose
2026-10-06  0:03   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 03/23] KVM: arm64: Include kvm_emulate.h in kvm/arm_psci.h Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 04/23] KVM: arm64: Avoid including linux/kvm_host.h in kvm_pgtable.h Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 05/23] KVM: arm64: Track the type of VM in kvm_arch Suzuki K Poulose
2026-10-06  3:55   ` Gavin Shan
2026-10-06  8:33   ` Marc Zyngier
2026-10-06  8:49     ` Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 06/23] KVM: arm64: Don't call vcpu_set_pauth_traps for pKVM host Suzuki K Poulose
2026-10-06  0:29   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 07/23] KVM: arm64: Refactor the vcpu_load to allow for VM specific callbacks Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 08/23] KVM: arm64: Add vcpu load/put call backs for flavors Suzuki K Poulose
2026-10-06  2:15   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 09/23] KVM: arm64: Prevent unsupported vcpu features for VM types Suzuki K Poulose
2026-10-06  2:24   ` Gavin Shan
2026-10-06  2:25   ` Gavin Shan
2026-10-06  5:16     ` Suzuki K Poulose
2026-10-06  8:50   ` Marc Zyngier
2026-10-05  9:07 ` [PATCH v22 10/23] KVM: arm64: Consolidate stage2 unmap range into kvm_stage2_unmap_range Suzuki K Poulose
2026-10-06  2:37   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 11/23] KVM: arm64: Add VM specific callback for S2 MMU operations Suzuki K Poulose
2026-10-06  3:00   ` Gavin Shan
2026-10-06  5:22     ` Suzuki K Poulose
2026-10-06  9:24   ` Marc Zyngier
2026-10-06 10:36     ` Suzuki K Poulose
2026-10-06 15:14       ` Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 12/23] KVM: arm64: Use a local kvm pointer in kvm_handle_guest_abort() Suzuki K Poulose
2026-10-06  3:02   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 13/23] KVM: arm64: Abstract out memory abort handling Suzuki K Poulose
2026-10-06  3:07   ` Gavin Shan
2026-10-06  5:25     ` Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 14/23] KVM: arm64: Mandate VGIC v3 for pKVM VMs and Realms Suzuki K Poulose
2026-10-06  3:10   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 15/23] KVM: arm64: CCA: Add a new mode for supporting Realm guests Suzuki K Poulose
2026-10-06  3:11   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 16/23] KVM: arm64: CCA: Add VCPU load/put for Realms Suzuki K Poulose
2026-10-06  3:16   ` Gavin Shan
2026-10-06  5:09     ` Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 17/23] KVM: arm64: CCA: Add bare minimal S2 operations for Realm Suzuki K Poulose
2026-10-06  3:18   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 18/23] KVM: arm64: CCA: Introduce Realms Suzuki K Poulose
2026-10-06  3:42   ` Gavin Shan
2026-10-06  5:10     ` Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 19/23] KVM: arm64: CCA: Don't expose unsupported capabilities for realm guests Suzuki K Poulose
2026-10-06  4:58   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 20/23] KVM: arm64: CCA: WARN on injected undef exceptions Suzuki K Poulose
2026-10-06  3:49   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 21/23] KVM: arm64: CCA: Support timers in realm RECs Suzuki K Poulose
2026-10-06  5:44   ` Gavin Shan
2026-10-05  9:07 ` [PATCH v22 22/23] KVM: arm64: CCA: Expose SVE VL register before VCPU finalization Suzuki K Poulose
2026-10-06  5:35   ` Gavin Shan
2026-10-06  5:57     ` Suzuki K Poulose
2026-10-05  9:07 ` [PATCH v22 23/23] KVM: arm64: CCA: Control user register access for Realms Suzuki K Poulose
2026-10-06  5:47   ` Gavin Shan
2026-10-06  6:01     ` Suzuki K Poulose
2026-10-06  6:16       ` Gavin Shan
2026-10-06 12:36         ` Suzuki K Poulose
2026-10-06  6:23       ` Gavin Shan

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

all inboxes | Powered by JetHome®