AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/08 20:12 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series modifies ARM64 KVM and Arm Confidential Compute Architecture (CCA / Realm Management Extension) code exclusively, as well as documentation. Since the target architecture is amd64, all modified code paths are exclusive to arm64 and unreachable on amd64.",
  "WorthFuzzing": false
}

1/1 2026/09/08 20:12 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit af95de954d413df61f83827589de0b2dcd5eadf7\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 8 20:12:50 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt\nindex 68647ff4bdd24..1afe3df3b923e 100644\n--- a/Documentation/admin-guide/kernel-parameters.txt\n+++ b/Documentation/admin-guide/kernel-parameters.txt\n@@ -3256,6 +3256,9 @@ Kernel parameters\n \t\t\tnested: VHE-based mode with support for nested\n \t\t\t\tvirtualization. Requires at least ARMv8.4\n \t\t\t\thardware (with FEAT_NV2).\n+\t\t\trmm: Support for running confidential guests in Realm\n+\t\t\t     world using RMM, as defined by Arm Confidential\n+\t\t\t     Compute Architecture (CCA)\n \n \t\t\tDefaults to VHE/nVHE based on hardware support. Setting\n \t\t\tmode to \"protected\" will disable kexec and hibernation\ndiff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h\nindex a3c1928bdf743..d360a8b05b8bf 100644\n--- a/arch/arm64/include/asm/kvm_emulate.h\n+++ b/arch/arm64/include/asm/kvm_emulate.h\n@@ -793,4 +793,20 @@ static inline void kvm_reset_vcpu_psci(struct kvm_vcpu *vcpu,\n \tvcpu_set_reg(vcpu, 0, reset_state-\u003er0);\n }\n \n+static inline enum realm_state kvm_realm_state(struct kvm *kvm)\n+{\n+\treturn READ_ONCE(kvm-\u003earch.realm.state);\n+}\n+\n+static inline void kvm_set_realm_state(struct kvm *kvm,\n+\t\t\t\t       enum realm_state new_state)\n+{\n+\tWRITE_ONCE(kvm-\u003earch.realm.state, new_state);\n+}\n+\n+static inline bool kvm_realm_is_created(struct kvm *kvm)\n+{\n+\treturn kvm_vm_is_realm(kvm) \u0026\u0026 kvm_realm_state(kvm) != REALM_STATE_NONE;\n+}\n+\n #endif /* __ARM64_KVM_EMULATE_H__ */\ndiff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h\nindex 27fe0cd5b2d7a..fbfd85dc999ef 100644\n--- a/arch/arm64/include/asm/kvm_host.h\n+++ b/arch/arm64/include/asm/kvm_host.h\n@@ -27,6 +27,7 @@\n #include \u003casm/fpsimd.h\u003e\n #include \u003casm/kvm.h\u003e\n #include \u003casm/kvm_asm.h\u003e\n+#include \u003casm/kvm_rmi.h\u003e\n #include \u003casm/vncr_mapping.h\u003e\n \n #define __KVM_HAVE_ARCH_INTC_INITIALIZED\n@@ -69,6 +70,7 @@ enum kvm_mode {\n \tKVM_MODE_DEFAULT,\n \tKVM_MODE_PROTECTED,\n \tKVM_MODE_NV,\n+\tKVM_MODE_RMM,\n \tKVM_MODE_NONE,\n };\n #ifdef CONFIG_KVM\n@@ -150,6 +152,26 @@ struct kvm_vmid {\n \tatomic64_t id;\n };\n \n+struct kvm_vcpu_ops {\n+\tvoid (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu);\n+\tvoid (*vcpu_put)(struct kvm_vcpu *vcpu);\n+};\n+\n+struct kvm_gfn_range;\n+struct kvm_s2_fault_desc;\n+\n+struct kvm_vm_s2_ops {\n+\tbool (*vm_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);\n+\tbool (*vm_test_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);\n+\tint (*vm_flush_remote_tlbs)(struct kvm *kvm);\n+\tint (*vm_flush_remote_tlbs_range)(struct kvm *kvm, gfn_t gfn,\n+\t\t\t\t\t  u64 nr_pages);\n+\tvoid (*vm_stage2_unmap_range)(struct kvm_s2_mmu *mmu,\n+\t\t\t\t      phys_addr_t start, u64 size,\n+\t\t\t\t      bool may_block);\n+\tint (*vm_mem_abort)(const struct kvm_s2_fault_desc *s2fd);\n+};\n+\n struct kvm_s2_mmu {\n \tstruct kvm_vmid vmid;\n \n@@ -257,7 +279,6 @@ struct kvm_protected_vm {\n \tpkvm_handle_t handle;\n \tstruct kvm_hyp_memcache teardown_mc;\n \tstruct kvm_hyp_memcache stage2_teardown_mc;\n-\tbool is_protected;\n \tbool is_created;\n \n \t/*\n@@ -306,9 +327,20 @@ enum fgt_group_id {\n \t__NR_FGT_GROUP_IDS__\n };\n \n+enum kvm_arm_vm_flavor {\n+\tVM_NVHE,\n+\tVM_VHE,\n+\tVM_PKVM,\t\t/* Normal guests on PKVM */\n+\tMARKER(__VM_CONFIDENTIAL),\n+\tVM_PROTECTED_PKVM,\t/* Protected VM */\n+\tVM_REALM,\t\t/* CCA */\n+\tVM_FLAVOR_MAX,\n+};\n+\n struct kvm_arch {\n \tstruct kvm_s2_mmu mmu;\n \n+\tenum kvm_arm_vm_flavor vm_flavor;\n \t/*\n \t * Fine-Grained UNDEF, mimicking the FGT layout defined by the\n \t * architecture. We track them globally, as we present the\n@@ -318,6 +350,8 @@ struct kvm_arch {\n \t */\n \tu64 fgu[__NR_FGT_GROUP_IDS__];\n \n+\tconst struct kvm_vm_s2_ops *vm_s2_ops;\n+\n \t/*\n \t * Stage 2 paging state for VMs with nested S2 using a virtual\n \t * VMID.\n@@ -417,11 +451,14 @@ struct kvm_arch {\n \t/* Count the number of VNCR_EL2 TLBs */\n \tatomic_t vncr_tlb_count;\n \n-\t/*\n-\t * For an untrusted host VM, 'pkvm.handle' is used to lookup\n-\t * the associated pKVM instance in the hypervisor.\n-\t */\n-\tstruct kvm_protected_vm pkvm;\n+\tunion {\n+\t\t/*\n+\t\t * For an untrusted host VM, 'pkvm.handle' is used to lookup\n+\t\t * the associated pKVM instance in the hypervisor.\n+\t\t */\n+\t\tstruct kvm_protected_vm pkvm;\n+\t\tstruct realm realm;\n+\t};\n \n #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS\n \t/* Nested virtualization info */\n@@ -846,6 +883,7 @@ struct vncr_tlb;\n \n struct kvm_vcpu_arch {\n \tstruct kvm_cpu_context ctxt;\n+\tconst struct kvm_vcpu_ops *vcpu_ops;\n \n \t/*\n \t * Guest floating point state\n@@ -1346,7 +1384,7 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);\n gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu);\n void kvm_update_stolen_time(struct kvm_vcpu *vcpu);\n \n-bool kvm_arm_pvtime_supported(void);\n+bool kvm_arm_pvtime_supported(struct kvm *kvm);\n int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,\n \t\t\t    struct kvm_device_attr *attr);\n int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,\n@@ -1504,9 +1542,13 @@ struct kvm *kvm_arch_alloc_vm(void);\n \n #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE\n \n-#define kvm_vm_is_protected(kvm)\t(is_protected_kvm_enabled() \u0026\u0026 (kvm)-\u003earch.pkvm.is_protected)\n+#define kvm_vm_is_confidential(kvm)\t((kvm)-\u003earch.vm_flavor \u003e= __VM_CONFIDENTIAL)\n+#define kvm_vm_is_protected(kvm)\t((kvm)-\u003earch.vm_flavor == VM_PROTECTED_PKVM)\n+#define kvm_vm_is_realm(kvm)\t\t((kvm)-\u003earch.vm_flavor == VM_REALM)\n \n+#define vcpu_is_confidential(vcpu)\tkvm_vm_is_confidential((vcpu)-\u003ekvm)\n #define vcpu_is_protected(vcpu)\t\tkvm_vm_is_protected((vcpu)-\u003ekvm)\n+#define vcpu_is_rec(vcpu)\t\tkvm_vm_is_realm((vcpu)-\u003ekvm)\n \n int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);\n bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);\ndiff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h\nindex 41a8687938eb6..c2e4b29e605fc 100644\n--- a/arch/arm64/include/asm/kvm_pgtable.h\n+++ b/arch/arm64/include/asm/kvm_pgtable.h\n@@ -8,9 +8,13 @@\n #define __ARM64_KVM_PGTABLE_H__\n \n #include \u003clinux/bits.h\u003e\n-#include \u003clinux/kvm_host.h\u003e\n+#include \u003clinux/kvm_types.h\u003e\n+#include \u003clinux/rbtree_types.h\u003e\n+#include \u003clinux/rcupdate.h\u003e\n #include \u003clinux/types.h\u003e\n \n+struct kvm_s2_mmu;\n+\n #define KVM_PGTABLE_FIRST_LEVEL\t\t-1\n #define KVM_PGTABLE_LAST_LEVEL\t\t3\n \ndiff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h\nindex beea00e693a0a..54a618d887fa4 100644\n--- a/arch/arm64/include/asm/kvm_pkvm.h\n+++ b/arch/arm64/include/asm/kvm_pkvm.h\n@@ -7,9 +7,9 @@\n #define __ARM64_KVM_PKVM_H__\n \n #include \u003clinux/arm_ffa.h\u003e\n+#include \u003clinux/kvm_host.h\u003e\n #include \u003clinux/memblock.h\u003e\n #include \u003clinux/scatterlist.h\u003e\n-#include \u003casm/kvm_host.h\u003e\n #include \u003casm/kvm_pgtable.h\u003e\n \n /* Maximum number of VMs that can co-exist under pKVM. */\ndiff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h\nnew file mode 100644\nindex 0000000000000..44f5c75a27b5b\n--- /dev/null\n+++ b/arch/arm64/include/asm/kvm_rmi.h\n@@ -0,0 +1,61 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * Copyright (C) 2023-2026 ARM Ltd.\n+ */\n+\n+#ifndef __ASM_KVM_RMI_H\n+#define __ASM_KVM_RMI_H\n+\n+/**\n+ * enum realm_state - State of a Realm\n+ *\n+ * Mirrors the RMM's Realm lifecycle states where they are meaningful to KVM,\n+ * with REALM_STATE_DYING being a KVM-internal state used to prevent further\n+ * requests while teardown is in progress. KVM does not track REALM_SYSTEM_OFF\n+ * or REALM_ZOMBIE separately as they naturally lead to teardown.\n+ */\n+enum realm_state {\n+\t/**\n+\t * @REALM_STATE_NONE:\n+\t *      Realm has not yet been created. rmi_realm_create() has not\n+\t *      yet been called.\n+\t */\n+\tREALM_STATE_NONE,\n+\t/**\n+\t * @REALM_STATE_NEW:\n+\t *      Realm is under construction, rmi_realm_create() has been\n+\t *      called, but it is not yet activated. Pages may be populated.\n+\t */\n+\tREALM_STATE_NEW,\n+\t/**\n+\t * @REALM_STATE_ACTIVE:\n+\t *      Realm has been created and is eligible for execution with\n+\t *      rmi_rec_enter(). Pages may no longer be populated with\n+\t *      rmi_data_create().\n+\t */\n+\tREALM_STATE_ACTIVE,\n+\t/**\n+\t * @REALM_STATE_DYING:\n+\t *      Realm is in the process of being destroyed or has already been\n+\t *      destroyed.\n+\t */\n+\tREALM_STATE_DYING,\n+\t/**\n+\t * @REALM_STATE_DEAD:\n+\t *      Realm has been destroyed.\n+\t */\n+\tREALM_STATE_DEAD\n+};\n+\n+/**\n+ * struct realm - Additional per VM data for a Realm\n+ *\n+ * @state: The lifetime state machine for the realm\n+ */\n+struct realm {\n+\tenum realm_state state;\n+};\n+\n+void kvm_init_rmi(void);\n+\n+#endif /* __ASM_KVM_RMI_H */\ndiff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h\nindex b546703c3ab9a..92cec42952f42 100644\n--- a/arch/arm64/include/asm/virt.h\n+++ b/arch/arm64/include/asm/virt.h\n@@ -87,6 +87,7 @@ void __hyp_reset_vectors(void);\n bool is_kvm_arm_initialised(void);\n \n DECLARE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);\n+DECLARE_STATIC_KEY_FALSE(kvm_rmi_is_available);\n \n static inline bool is_pkvm_initialized(void)\n {\ndiff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile\nindex 59612d2f277c1..ed3cf30eb06e7 100644\n--- a/arch/arm64/kvm/Makefile\n+++ b/arch/arm64/kvm/Makefile\n@@ -16,7 +16,7 @@ CFLAGS_handle_exit.o += -Wno-override-init\n kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \\\n \t inject_fault.o va_layout.o handle_exit.o config.o \\\n \t guest.o debug.o reset.o sys_regs.o stacktrace.o \\\n-\t vgic-sys-reg-v3.o fpsimd.o pkvm.o \\\n+\t vgic-sys-reg-v3.o fpsimd.o pkvm.o rmi.o \\\n \t arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \\\n \t vgic/vgic.o vgic/vgic-init.o \\\n \t vgic/vgic-irqfd.o vgic/vgic-v2.o \\\ndiff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c\nindex 6ac3321f4c575..3eddf44f7b1dd 100644\n--- a/arch/arm64/kvm/arch_timer.c\n+++ b/arch/arm64/kvm/arch_timer.c\n@@ -56,11 +56,25 @@ static unsigned long kvm_arch_timer_get_irq_flags(void)\n \treturn kvm_vgic_global_state.no_hw_deactivation ? VGIC_IRQ_SW_RESAMPLE : 0;\n }\n \n+static unsigned long kvm_realm_timer_get_irq_flags(void)\n+{\n+\t/*\n+\t * RMI_REC_ENTER rejects LRs with the HW bit set, so use the existing\n+\t * software resampling mechanism for Realm timer interrupts.\n+\t */\n+\treturn VGIC_IRQ_SW_RESAMPLE;\n+}\n+\n static const struct irq_ops arch_timer_irq_ops = {\n \t.get_flags\t = kvm_arch_timer_get_irq_flags,\n \t.get_input_level = kvm_arch_timer_get_input_level,\n };\n \n+static const struct irq_ops realm_timer_irq_ops = {\n+\t.get_flags\t = kvm_realm_timer_get_irq_flags,\n+\t.get_input_level = kvm_arch_timer_get_input_level,\n+};\n+\n static const struct irq_ops arch_timer_irq_ops_vgic_v5 = {\n \t.get_input_level = kvm_arch_timer_get_input_level,\n \t.queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock,\n@@ -1079,7 +1093,7 @@ static void timer_context_init(struct kvm_vcpu *vcpu, int timerid)\n \n \tctxt-\u003etimer_id = timerid;\n \n-\tif (!kvm_vm_is_protected(vcpu-\u003ekvm)) {\n+\tif (!vcpu_is_confidential(vcpu)) {\n \t\tif (timerid == TIMER_VTIMER)\n \t\t\tctxt-\u003eoffset.vm_offset = \u0026kvm-\u003earch.timer_data.voffset;\n \t\telse\n@@ -1110,7 +1124,7 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)\n \t\ttimer_context_init(vcpu, i);\n \n \t/* Synchronize offsets across timers of a VM if not already provided */\n-\tif (!vcpu_is_protected(vcpu) \u0026\u0026\n+\tif (!vcpu_is_confidential(vcpu) \u0026\u0026\n \t    !test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, \u0026vcpu-\u003ekvm-\u003earch.flags)) {\n \t\ttimer_set_offset(vcpu_vtimer(vcpu), kvm_phys_timer_read());\n \t\ttimer_set_offset(vcpu_ptimer(vcpu), 0);\n@@ -1609,8 +1623,12 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)\n \n \tget_timer_map(vcpu, \u0026map);\n \n-\tops = vgic_is_v5(vcpu-\u003ekvm) ? \u0026arch_timer_irq_ops_vgic_v5 :\n-\t\t\t\t      \u0026arch_timer_irq_ops;\n+\tif (vcpu_is_rec(vcpu))\n+\t\tops = \u0026realm_timer_irq_ops;\n+\telse if (vgic_is_v5(vcpu-\u003ekvm))\n+\t\tops = \u0026arch_timer_irq_ops_vgic_v5;\n+\telse\n+\t\tops = \u0026arch_timer_irq_ops;\n \n \tfor (int i = 0; i \u003c nr_timers(vcpu); i++)\n \t\tkvm_vgic_set_irq_ops(vcpu, timer_irq(vcpu_get_timer(vcpu, i)), ops);\n@@ -1736,7 +1754,7 @@ int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,\n \tif (offset-\u003ereserved)\n \t\treturn -EINVAL;\n \n-\tif (kvm_vm_is_protected(kvm))\n+\tif (kvm_vm_is_confidential(kvm))\n \t\treturn -EINVAL;\n \n \tmutex_lock(\u0026kvm-\u003elock);\ndiff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c\nindex 8b080804bc90b..3fe5a4f77738e 100644\n--- a/arch/arm64/kvm/arm.c\n+++ b/arch/arm64/kvm/arm.c\n@@ -42,6 +42,7 @@\n #include \u003casm/kvm_nested.h\u003e\n #include \u003casm/kvm_pkvm.h\u003e\n #include \u003casm/kvm_ptrauth.h\u003e\n+#include \u003casm/kvm_rmi.h\u003e\n #include \u003casm/sections.h\u003e\n #include \u003casm/stacktrace/nvhe.h\u003e\n \n@@ -74,6 +75,8 @@ struct kvm_ioctl_cap_map {\n \tlong ext;\n };\n \n+static const struct kvm_vcpu_ops *arm64_vcpu_ops[VM_FLAVOR_MAX];\n+\n /* Make KVM_CAP_NR_VCPUS the reference for features we always supported */\n #define KVM_CAP_ARM_BASIC\tKVM_CAP_NR_VCPUS\n \n@@ -111,6 +114,8 @@ long kvm_get_cap_for_kvm_ioctl(unsigned int ioctl, long *ext)\n \treturn -EINVAL;\n }\n \n+DEFINE_STATIC_KEY_FALSE(kvm_rmi_is_available);\n+\n DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);\n \n DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_base);\n@@ -132,6 +137,25 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)\n \treturn kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;\n }\n \n+static bool kvm_realm_ext_allowed(long ext)\n+{\n+\tswitch (ext) {\n+\tcase KVM_CAP_IRQCHIP:\n+\tcase KVM_CAP_ARM_PSCI:\n+\tcase KVM_CAP_ARM_PSCI_0_2:\n+\tcase KVM_CAP_NR_VCPUS:\n+\tcase KVM_CAP_MAX_VCPUS:\n+\tcase KVM_CAP_MAX_VCPU_ID:\n+\tcase KVM_CAP_MSI_DEVID:\n+\tcase KVM_CAP_ARM_VM_IPA_SIZE:\n+\tcase KVM_CAP_ARM_PTRAUTH_ADDRESS:\n+\tcase KVM_CAP_ARM_PTRAUTH_GENERIC:\n+\tcase KVM_CAP_SYNC_MMU:\n+\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n int kvm_vm_ioctl_enable_cap(struct kvm *kvm,\n \t\t\t    struct kvm_enable_cap *cap)\n {\n@@ -143,6 +167,9 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,\n \tif (is_protected_kvm_enabled() \u0026\u0026 !kvm_pkvm_ext_allowed(kvm, cap-\u003ecap))\n \t\treturn -EINVAL;\n \n+\tif (kvm \u0026\u0026 kvm_vm_is_realm(kvm) \u0026\u0026 !kvm_realm_ext_allowed(cap-\u003ecap))\n+\t\treturn -EINVAL;\n+\n \tswitch (cap-\u003ecap) {\n \tcase KVM_CAP_ARM_NISV_TO_USER:\n \t\tr = 0;\n@@ -214,6 +241,26 @@ static int kvm_arm_default_max_vcpus(void)\n \treturn vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;\n }\n \n+static int kvm_init_vm_flavor(struct kvm *kvm, unsigned long type)\n+{\n+\tbool protected = type \u0026 KVM_VM_TYPE_ARM_PROTECTED;\n+\n+\tif (is_protected_kvm_enabled()) {\n+\t\tif (protected)\n+\t\t\tkvm-\u003earch.vm_flavor = VM_PROTECTED_PKVM;\n+\t\telse\n+\t\t\tkvm-\u003earch.vm_flavor = VM_PKVM;\n+\t} else if (protected) {\n+\t\treturn -EINVAL;\n+\t} else if (has_vhe()) {\n+\t\tkvm-\u003earch.vm_flavor = VM_VHE;\n+\t} else {\n+\t\tkvm-\u003earch.vm_flavor = VM_NVHE;\n+\t}\n+\n+\treturn 0;\n+}\n+\n /**\n  * kvm_arch_init_vm - initializes a VM data structure\n  * @kvm:\tpointer to the KVM struct\n@@ -236,6 +283,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n \tmutex_unlock(\u0026kvm-\u003elock);\n #endif\n \n+\tret = kvm_init_vm_flavor(kvm, type);\n+\tif (ret)\n+\t\treturn ret;\n+\n \tkvm_init_nested(kvm);\n \n \tret = kvm_share_hyp(kvm, kvm + 1);\n@@ -260,9 +311,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n \t\tret = pkvm_init_host_vm(kvm, type);\n \t\tif (ret)\n \t\t\tgoto err_uninit_mmu;\n-\t} else if (type \u0026 KVM_VM_TYPE_ARM_PROTECTED) {\n-\t\tret = -EINVAL;\n-\t\tgoto err_uninit_mmu;\n \t}\n \n \tkvm_vgic_early_init(kvm);\n@@ -381,6 +429,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)\n \tif (is_protected_kvm_enabled() \u0026\u0026 !kvm_pkvm_ext_allowed(kvm, ext))\n \t\treturn 0;\n \n+\tif (kvm \u0026\u0026 kvm_vm_is_realm(kvm) \u0026\u0026 !kvm_realm_ext_allowed(ext))\n+\t\treturn 0;\n+\n \tswitch (ext) {\n \tcase KVM_CAP_IRQCHIP:\n \t\tr = vgic_present;\n@@ -447,7 +498,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)\n \t\tr = system_supports_mte();\n \t\tbreak;\n \tcase KVM_CAP_STEAL_TIME:\n-\t\tr = kvm_arm_pvtime_supported();\n+\t\tr = kvm_arm_pvtime_supported(kvm);\n \t\tbreak;\n \tcase KVM_CAP_ARM_EL1_32BIT:\n \t\tr = cpus_have_final_cap(ARM64_HAS_32BIT_EL1);\n@@ -548,6 +599,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)\n \tmutex_unlock(\u0026vcpu-\u003emutex);\n #endif\n \n+\tvcpu-\u003earch.vcpu_ops = arm64_vcpu_ops[vcpu-\u003ekvm-\u003earch.vm_flavor];\n+\n \t/* Force users to call KVM_ARM_VCPU_INIT */\n \tvcpu_clear_flag(vcpu, VCPU_INITIALIZED);\n \n@@ -662,14 +715,11 @@ static bool kvm_vcpu_should_clear_twe(struct kvm_vcpu *vcpu)\n \treturn single_task_running();\n }\n \n-void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n+static void vcpu_prepare_mmu(struct kvm_vcpu *vcpu)\n {\n \tstruct kvm_s2_mmu *mmu;\n \tint *last_ran;\n \n-\tif (is_protected_kvm_enabled())\n-\t\tgoto nommu;\n-\n \tif (vcpu_has_nv(vcpu))\n \t\tkvm_vcpu_load_hw_mmu(vcpu);\n \n@@ -699,10 +749,30 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \t\tkvm_call_hyp(__kvm_flush_cpu_context, mmu);\n \t\t*last_ran = vcpu-\u003evcpu_idx;\n \t}\n+}\n \n-nommu:\n-\tvcpu-\u003ecpu = cpu;\n+static void vcpu_set_wfx_traps(struct kvm_vcpu *vcpu)\n+{\n+\tif (kvm_vcpu_should_clear_twe(vcpu))\n+\t\tvcpu-\u003earch.hcr_el2 \u0026= ~HCR_TWE;\n+\telse\n+\t\tvcpu-\u003earch.hcr_el2 |= HCR_TWE;\n+\n+\tif (kvm_vcpu_should_clear_twi(vcpu))\n+\t\tvcpu-\u003earch.hcr_el2 \u0026= ~HCR_TWI;\n+\telse\n+\t\tvcpu-\u003earch.hcr_el2 |= HCR_TWI;\n+}\n+\n+static void vcpu_load_pvtime(struct kvm_vcpu *vcpu)\n+{\n+\tif (kvm_arm_is_pvtime_enabled(\u0026vcpu-\u003earch))\n+\t\tkvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);\n+}\n \n+static void vhe_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n+{\n+\tvcpu_prepare_mmu(vcpu);\n \t/*\n \t * The timer must be loaded before the vgic to correctly set up physical\n \t * interrupt deactivation in nested state (e.g. timer interrupt).\n@@ -711,32 +781,68 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \tkvm_vgic_load(vcpu);\n \tkvm_vcpu_load_debug(vcpu);\n \tkvm_vcpu_load_fgt(vcpu);\n-\tif (has_vhe())\n-\t\tkvm_vcpu_load_vhe(vcpu);\n+\tkvm_vcpu_load_vhe(vcpu);\n \tkvm_arch_vcpu_load_fp(vcpu);\n \tkvm_vcpu_pmu_restore_guest(vcpu);\n-\tif (kvm_arm_is_pvtime_enabled(\u0026vcpu-\u003earch))\n-\t\tkvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);\n \n-\tif (kvm_vcpu_should_clear_twe(vcpu))\n-\t\tvcpu-\u003earch.hcr_el2 \u0026= ~HCR_TWE;\n-\telse\n-\t\tvcpu-\u003earch.hcr_el2 |= HCR_TWE;\n+\tvcpu_load_pvtime(vcpu);\n+\tvcpu_set_wfx_traps(vcpu);\n+\tvcpu_set_pauth_traps(vcpu);\n+}\n \n-\tif (kvm_vcpu_should_clear_twi(vcpu))\n-\t\tvcpu-\u003earch.hcr_el2 \u0026= ~HCR_TWI;\n-\telse\n-\t\tvcpu-\u003earch.hcr_el2 |= HCR_TWI;\n+static void nvhe_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n+{\n+\tvcpu_prepare_mmu(vcpu);\n+\t/*\n+\t * The timer must be loaded before the vgic to correctly set up physical\n+\t * interrupt deactivation in nested state (e.g. timer interrupt).\n+\t */\n+\tkvm_timer_vcpu_load(vcpu);\n+\tkvm_vgic_load(vcpu);\n+\tkvm_vcpu_load_debug(vcpu);\n+\tkvm_vcpu_load_fgt(vcpu);\n+\tkvm_arch_vcpu_load_fp(vcpu);\n+\tkvm_vcpu_pmu_restore_guest(vcpu);\n \n+\tvcpu_load_pvtime(vcpu);\n+\tvcpu_set_wfx_traps(vcpu);\n \tvcpu_set_pauth_traps(vcpu);\n+}\n \n-\tif (is_protected_kvm_enabled()) {\n-\t\tkvm_call_hyp_nvhe(__pkvm_vcpu_load,\n-\t\t\t\t  vcpu-\u003ekvm-\u003earch.pkvm.handle,\n-\t\t\t\t  vcpu-\u003evcpu_idx, vcpu-\u003earch.hcr_el2);\n-\t\tkvm_call_hyp(__vgic_v3_restore_vmcr_aprs,\n-\t\t\t     \u0026vcpu-\u003earch.vgic_cpu.vgic_v3);\n-\t}\n+static void pkvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n+{\n+\t/*\n+\t * The timer must be loaded before the vgic to correctly set up physical\n+\t * interrupt deactivation in nested state (e.g. timer interrupt).\n+\t */\n+\tkvm_timer_vcpu_load(vcpu);\n+\tkvm_vgic_load(vcpu);\n+\tkvm_vcpu_load_debug(vcpu);\n+\tkvm_vcpu_load_fgt(vcpu);\n+\tkvm_arch_vcpu_load_fp(vcpu);\n+\tkvm_vcpu_pmu_restore_guest(vcpu);\n+\n+\tvcpu_load_pvtime(vcpu);\n+\tvcpu_set_wfx_traps(vcpu);\n+\n+\tkvm_call_hyp_nvhe(__pkvm_vcpu_load,\n+\t\t\t  vcpu-\u003ekvm-\u003earch.pkvm.handle,\n+\t\t\t  vcpu-\u003evcpu_idx, vcpu-\u003earch.hcr_el2);\n+\tkvm_call_hyp(__vgic_v3_restore_vmcr_aprs,\n+\t\t     \u0026vcpu-\u003earch.vgic_cpu.vgic_v3);\n+}\n+\n+static void realm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n+{\n+\tkvm_timer_vcpu_load(vcpu);\n+\tkvm_vgic_load(vcpu);\n+\tvcpu_set_wfx_traps(vcpu);\n+}\n+\n+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n+{\n+\tvcpu-\u003ecpu = cpu;\n+\tvcpu-\u003earch.vcpu_ops-\u003evcpu_load(vcpu, cpu);\n \n \tif (!cpumask_test_cpu(cpu, vcpu-\u003ekvm-\u003earch.supported_cpus))\n \t\tvcpu_set_on_unsupported_cpu(vcpu);\n@@ -744,28 +850,50 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \tvcpu-\u003earch.pid = pid_nr(vcpu-\u003epid);\n }\n \n-void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)\n+static void vhe_vcpu_put(struct kvm_vcpu *vcpu)\n {\n-\tif (is_protected_kvm_enabled()) {\n-\t\tkvm_call_hyp(__vgic_v3_save_aprs, \u0026vcpu-\u003earch.vgic_cpu.vgic_v3);\n-\t\tkvm_call_hyp_nvhe(__pkvm_vcpu_put);\n-\n-\t\t/* __pkvm_vcpu_put implies a sync of the state */\n-\t\tif (!kvm_vm_is_protected(vcpu-\u003ekvm))\n-\t\t\tvcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);\n-\t}\n-\n \tkvm_vcpu_put_debug(vcpu);\n \tkvm_arch_vcpu_put_fp(vcpu);\n-\tif (has_vhe())\n-\t\tkvm_vcpu_put_vhe(vcpu);\n+\tkvm_vcpu_put_vhe(vcpu);\n \tkvm_timer_vcpu_put(vcpu);\n \tkvm_vgic_put(vcpu);\n \tkvm_vcpu_pmu_restore_host(vcpu);\n \tif (vcpu_has_nv(vcpu))\n \t\tkvm_vcpu_put_hw_mmu(vcpu);\n \tkvm_arm_vmid_clear_active();\n+}\n \n+static void nvhe_vcpu_put(struct kvm_vcpu *vcpu)\n+{\n+\tkvm_vcpu_put_debug(vcpu);\n+\tkvm_arch_vcpu_put_fp(vcpu);\n+\tkvm_timer_vcpu_put(vcpu);\n+\tkvm_vgic_put(vcpu);\n+\tkvm_vcpu_pmu_restore_host(vcpu);\n+\tkvm_arm_vmid_clear_active();\n+}\n+\n+static void pkvm_vcpu_put(struct kvm_vcpu *vcpu)\n+{\n+\tkvm_call_hyp(__vgic_v3_save_aprs, \u0026vcpu-\u003earch.vgic_cpu.vgic_v3);\n+\tkvm_call_hyp_nvhe(__pkvm_vcpu_put);\n+\n+\t/* __pkvm_vcpu_put implies a sync of the state */\n+\tif (!kvm_vm_is_protected(vcpu-\u003ekvm))\n+\t\tvcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);\n+\n+\tnvhe_vcpu_put(vcpu);\n+}\n+\n+static void realm_vcpu_put(struct kvm_vcpu *vcpu)\n+{\n+\tkvm_timer_vcpu_put(vcpu);\n+\tkvm_vgic_put(vcpu);\n+}\n+\n+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)\n+{\n+\tvcpu-\u003earch.vcpu_ops-\u003evcpu_put(vcpu);\n \tvcpu_clear_on_unsupported_cpu(vcpu);\n \tvcpu-\u003ecpu = -1;\n }\n@@ -1852,6 +1980,18 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,\n \treturn __kvm_arm_vcpu_set_events(vcpu, events);\n }\n \n+/*\n+ * Realm VCPUs can be finalized after the Realm descriptor is created.\n+ * Allow the register list for RECs before the VCPUs are finalized, but\n+ * after the SVE is finalized.\n+ */\n+static bool kvm_arm_vcpu_reg_list_allowed(struct kvm_vcpu *vcpu)\n+{\n+\tif (kvm_arm_vcpu_is_finalized(vcpu))\n+\t\treturn true;\n+\treturn vcpu_is_rec(vcpu);\n+}\n+\n long kvm_arch_vcpu_ioctl(struct file *filp,\n \t\t\t unsigned int ioctl, unsigned long arg)\n {\n@@ -1907,7 +2047,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,\n \t\t\tbreak;\n \n \t\tr = -EPERM;\n-\t\tif (!kvm_arm_vcpu_is_finalized(vcpu))\n+\t\tif (!kvm_arm_vcpu_reg_list_allowed(vcpu))\n \t\t\tbreak;\n \n \t\tr = -EFAULT;\n@@ -2105,6 +2245,34 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)\n \t}\n }\n \n+static const struct kvm_vcpu_ops vhe_vcpu_ops = {\n+\t.vcpu_load = vhe_vcpu_load,\n+\t.vcpu_put = vhe_vcpu_put,\n+};\n+\n+static const struct kvm_vcpu_ops nvhe_vcpu_ops = {\n+\t.vcpu_load = nvhe_vcpu_load,\n+\t.vcpu_put = nvhe_vcpu_put,\n+};\n+\n+static const struct kvm_vcpu_ops pkvm_vcpu_ops = {\n+\t.vcpu_load = pkvm_vcpu_load,\n+\t.vcpu_put = pkvm_vcpu_put,\n+};\n+\n+static const struct kvm_vcpu_ops realm_vcpu_ops = {\n+\t.vcpu_load = realm_vcpu_load,\n+\t.vcpu_put = realm_vcpu_put,\n+};\n+\n+static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {\n+\t[VM_VHE] = \u0026vhe_vcpu_ops,\n+\t[VM_NVHE] = \u0026nvhe_vcpu_ops,\n+\t[VM_PKVM] = \u0026pkvm_vcpu_ops,\n+\t[VM_PROTECTED_PKVM] = \u0026pkvm_vcpu_ops,\n+\t[VM_REALM] = \u0026realm_vcpu_ops,\n+};\n+\n static unsigned long nvhe_percpu_size(void)\n {\n \treturn (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_end) -\n@@ -3050,6 +3218,8 @@ static __init int kvm_arm_init(void)\n \n \tin_hyp_mode = is_kernel_in_hyp_mode();\n \n+\tkvm_init_rmi();\n+\n \tif (cpus_have_final_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ||\n \t    cpus_have_final_cap(ARM64_WORKAROUND_1508412))\n \t\tkvm_info(\"Guests without required CPU erratum workarounds can deadlock system!\\n\" \\\n@@ -3155,6 +3325,11 @@ static int __init early_kvm_mode_cfg(char *arg)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(arg, \"rmm\") == 0 \u0026\u0026 !WARN_ON(!is_kernel_in_hyp_mode())) {\n+\t\tkvm_mode = KVM_MODE_RMM;\n+\t\treturn 0;\n+\t}\n+\n \treturn -EINVAL;\n }\n early_param(\"kvm-arm.mode\", early_kvm_mode_cfg);\ndiff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c\nindex b01d6622b8720..6bec73d1ee050 100644\n--- a/arch/arm64/kvm/guest.c\n+++ b/arch/arm64/kvm/guest.c\n@@ -598,8 +598,11 @@ static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)\n \tif (!vcpu_has_sve(vcpu))\n \t\treturn 0;\n \n-\t/* Policed by KVM_GET_REG_LIST: */\n-\tWARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));\n+\tif (!kvm_arm_vcpu_sve_finalized(vcpu))\n+\t\treturn 1; /* KVM_REG_ARM64_SVE_VLS */\n+\n+\tif (kvm_vm_is_realm(vcpu-\u003ekvm))\n+\t\treturn 1; /* KVM_REG_ARM64_SVE_VLS */\n \n \treturn slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */)\n \t\t+ 1; /* KVM_REG_ARM64_SVE_VLS */\n@@ -616,9 +619,6 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,\n \tif (!vcpu_has_sve(vcpu))\n \t\treturn 0;\n \n-\t/* Policed by KVM_GET_REG_LIST: */\n-\tWARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));\n-\n \t/*\n \t * Enumerate this first, so that userspace can save/restore in\n \t * the order reported by KVM_GET_REG_LIST:\n@@ -628,6 +628,12 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,\n \t\treturn -EFAULT;\n \t++num_regs;\n \n+\tif (!kvm_arm_vcpu_sve_finalized(vcpu))\n+\t\treturn num_regs;\n+\n+\tif (kvm_vm_is_realm(vcpu-\u003ekvm))\n+\t\treturn num_regs;\n+\n \tfor (i = 0; i \u003c slices; i++) {\n \t\tfor (n = 0; n \u003c SVE_NUM_ZREGS; n++) {\n \t\t\treg = KVM_REG_ARM64_SVE_ZREG(n, i);\ndiff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c\nindex 459bd9eb7e4bc..09961c0056f2b 100644\n--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c\n+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c\n@@ -432,7 +432,7 @@ static void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm,\n \n \thyp_vm-\u003ehost_kvm = host_kvm;\n \thyp_vm-\u003ekvm.created_vcpus = nr_vcpus;\n-\thyp_vm-\u003ekvm.arch.pkvm.is_protected = READ_ONCE(host_kvm-\u003earch.pkvm.is_protected);\n+\thyp_vm-\u003ekvm.arch.vm_flavor = READ_ONCE(host_kvm-\u003earch.vm_flavor);\n \thyp_vm-\u003ekvm.arch.flags = 0;\n \tpkvm_init_features_from_host(hyp_vm, host_kvm);\n \ndiff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c\nindex b74dd5ce1efd3..f48253b9d88b5 100644\n--- a/arch/arm64/kvm/hyp/pgtable.c\n+++ b/arch/arm64/kvm/hyp/pgtable.c\n@@ -8,6 +8,7 @@\n  */\n \n #include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/kvm_host.h\u003e\n #include \u003casm/kvm_pgtable.h\u003e\n #include \u003casm/stage2_pgtable.h\u003e\n \ndiff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c\nindex b11b8821c9fbc..2b1e6fdeb4d5c 100644\n--- a/arch/arm64/kvm/hypercalls.c\n+++ b/arch/arm64/kvm/hypercalls.c\n@@ -414,14 +414,14 @@ void kvm_arm_teardown_hypercalls(struct kvm *kvm)\n \n int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)\n {\n-\treturn ARRAY_SIZE(kvm_arm_fw_reg_ids);\n+\treturn vcpu_is_rec(vcpu) ? 0 : ARRAY_SIZE(kvm_arm_fw_reg_ids);\n }\n \n int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)\n {\n \tint i;\n \n-\tfor (i = 0; i \u003c ARRAY_SIZE(kvm_arm_fw_reg_ids); i++) {\n+\tfor (i = 0; i \u003c kvm_arm_get_fw_num_regs(vcpu); i++) {\n \t\tif (put_user(kvm_arm_fw_reg_ids[i], uindices++))\n \t\t\treturn -EFAULT;\n \t}\ndiff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c\nindex d6c4fc16f8795..d61a3ff04fabb 100644\n--- a/arch/arm64/kvm/inject_fault.c\n+++ b/arch/arm64/kvm/inject_fault.c\n@@ -317,6 +317,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)\n  */\n void kvm_inject_undefined(struct kvm_vcpu *vcpu)\n {\n+\tKVM_BUG(vcpu_is_rec(vcpu), vcpu-\u003ekvm, \"Unexpected undefined exception injection to REC\");\n \tif (vcpu_el1_is_32bit(vcpu))\n \t\tinject_undef32(vcpu);\n \telse\ndiff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c\nindex d1c3a352d5a22..bbcf52f01e912 100644\n--- a/arch/arm64/kvm/mmio.c\n+++ b/arch/arm64/kvm/mmio.c\n@@ -168,14 +168,14 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)\n \t * No valid syndrome? Ask userspace for help if it has\n \t * volunteered to do so, and bail out otherwise.\n \t *\n-\t * In the protected VM case, there isn't much userspace can do\n+\t * In the protected/Realm VM case, there isn't much userspace can do\n \t * though, so directly deliver an exception to the guest.\n \t */\n \tif (!kvm_vcpu_dabt_isvalid(vcpu)) {\n \t\ttrace_kvm_mmio_nisv(*vcpu_pc(vcpu), esr,\n \t\t\t\t    kvm_vcpu_get_hfar(vcpu), fault_ipa);\n \n-\t\tif (vcpu_is_protected(vcpu))\n+\t\tif (vcpu_is_confidential(vcpu))\n \t\t\treturn kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));\n \n \t\tif (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,\ndiff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c\nindex 9ba86450fe4af..5e7cdbe310fc8 100644\n--- a/arch/arm64/kvm/mmu.c\n+++ b/arch/arm64/kvm/mmu.c\n@@ -37,6 +37,10 @@ static unsigned long __ro_after_init io_map_base;\n \n #define KVM_PGT_FN(fn)\t\t(!is_protected_kvm_enabled() ? fn : p ## fn)\n \n+static const struct kvm_vm_s2_ops protected_pkvm_s2_ops;\n+static const struct kvm_vm_s2_ops unprotected_pkvm_s2_ops;\n+static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops;\n+\n static phys_addr_t __stage2_range_addr_end(phys_addr_t addr, phys_addr_t end,\n \t\t\t\t\t   phys_addr_t size)\n {\n@@ -166,6 +170,18 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)\n \treturn memslot-\u003edirty_bitmap \u0026\u0026 !(memslot-\u003eflags \u0026 KVM_MEM_READONLY);\n }\n \n+static int pkvm_flush_remote_tlbs(struct kvm *kvm)\n+{\n+\tkvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm-\u003earch.pkvm.handle);\n+\treturn 0;\n+}\n+\n+static int kvm_vm_flush_remote_tlbs(struct kvm *kvm)\n+{\n+\tkvm_call_hyp(__kvm_tlb_flush_vmid, \u0026kvm-\u003earch.mmu);\n+\treturn 0;\n+}\n+\n /**\n  * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8\n  * @kvm:\tpointer to kvm structure.\n@@ -174,26 +190,36 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)\n  */\n int kvm_arch_flush_remote_tlbs(struct kvm *kvm)\n {\n-\tif (is_protected_kvm_enabled())\n-\t\tkvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm-\u003earch.pkvm.handle);\n-\telse\n-\t\tkvm_call_hyp(__kvm_tlb_flush_vmid, \u0026kvm-\u003earch.mmu);\n-\treturn 0;\n+\tif (!kvm-\u003earch.vm_s2_ops-\u003evm_flush_remote_tlbs)\n+\t\treturn 0;\n+\treturn kvm-\u003earch.vm_s2_ops-\u003evm_flush_remote_tlbs(kvm);\n }\n \n-int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,\n-\t\t\t\t      gfn_t gfn, u64 nr_pages)\n+static int pkvm_flush_remote_tlbs_range(struct kvm *kvm,\n+\t\t\t\t\tgfn_t gfn, u64 nr_pages)\n+{\n+\treturn pkvm_flush_remote_tlbs(kvm);\n+}\n+\n+static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,\n+\t\t\t\t\t gfn_t gfn, u64 nr_pages)\n {\n \tu64 size = nr_pages \u003c\u003c PAGE_SHIFT;\n \tu64 addr = gfn \u003c\u003c PAGE_SHIFT;\n \n-\tif (is_protected_kvm_enabled())\n-\t\tkvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm-\u003earch.pkvm.handle);\n-\telse\n-\t\tkvm_tlb_flush_vmid_range(\u0026kvm-\u003earch.mmu, addr, size);\n+\tkvm_tlb_flush_vmid_range(\u0026kvm-\u003earch.mmu, addr, size);\n \treturn 0;\n }\n \n+int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,\n+\t\t\t\t     gfn_t gfn, u64 nr_pages)\n+{\n+\tif (!kvm-\u003earch.vm_s2_ops-\u003evm_flush_remote_tlbs_range)\n+\t\treturn 0;\n+\n+\treturn kvm-\u003earch.vm_s2_ops-\u003evm_flush_remote_tlbs_range(kvm, gfn, nr_pages);\n+}\n+\n static void *stage2_memcache_zalloc_page(void *arg)\n {\n \tstruct kvm_mmu_memory_cache *mc = arg;\n@@ -337,13 +363,20 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64\n \t\t\t\t   may_block));\n }\n \n+static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,\n+\t\t\t\t      phys_addr_t start,\n+\t\t\t\t      u64 size, bool may_block)\n+{\n+\t__unmap_stage2_range(mmu, start, size, may_block);\n+}\n+\n void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,\n \t\t\t    u64 size, bool may_block)\n {\n-\tif (kvm_vm_is_protected(kvm_s2_mmu_to_kvm(mmu)))\n-\t\treturn;\n+\tstruct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);\n \n-\t__unmap_stage2_range(mmu, start, size, may_block);\n+\tif (kvm-\u003earch.vm_s2_ops-\u003evm_stage2_unmap_range)\n+\t\tkvm-\u003earch.vm_s2_ops-\u003evm_stage2_unmap_range(mmu, start, size, may_block);\n }\n \n void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)\n@@ -963,6 +996,18 @@ static void kvm_stage2_destroy(struct kvm_pgtable *pgt)\n \tKVM_PGT_FN(kvm_pgtable_stage2_destroy_pgd)(pgt);\n }\n \n+static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[VM_FLAVOR_MAX] = {\n+\t[VM_VHE] = \u0026kvm_default_vm_s2_ops,\n+\t[VM_NVHE] = \u0026kvm_default_vm_s2_ops,\n+\t[VM_PKVM] = \u0026unprotected_pkvm_s2_ops,\n+\t[VM_PROTECTED_PKVM] = \u0026protected_pkvm_s2_ops,\n+};\n+\n+static void kvm_vm_init_vm_s2_ops(struct kvm *kvm, unsigned long type)\n+{\n+\tkvm-\u003earch.vm_s2_ops = arm64_vm_s2_ops[kvm-\u003earch.vm_flavor];\n+}\n+\n /**\n  * kvm_init_stage2_mmu - Initialise a S2 MMU structure\n  * @kvm:\tThe pointer to the KVM structure\n@@ -983,6 +1028,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t\n \tint cpu, err;\n \tstruct kvm_pgtable *pgt;\n \n+\t/* Initialize the VM ops for the VM instance for the first time */\n+\tif (mmu == \u0026kvm-\u003earch.mmu)\n+\t\tkvm_vm_init_vm_s2_ops(kvm, type);\n \t/*\n \t * If we already have our page tables in place, and that the\n \t * MMU context is the canonical one, we have a bug somewhere,\n@@ -1705,7 +1753,7 @@ struct kvm_s2_fault_vma_info {\n \tbool\t\tmap_non_cacheable;\n };\n \n-static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd)\n+static int protected_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)\n {\n \tunsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;\n \tstruct kvm_vcpu *vcpu = s2fd-\u003evcpu;\n@@ -2143,6 +2191,22 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)\n \treturn kvm_s2_fault_map(s2fd, \u0026s2vi, prot, memcache);\n }\n \n+static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)\n+{\n+\tint ret;\n+\tstruct kvm_vcpu *vcpu = s2fd-\u003evcpu;\n+\n+\tVM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) \u0026\u0026\n+\t\t\t!kvm_is_write_fault(vcpu) \u0026\u0026\n+\t\t\t!kvm_vcpu_trap_is_exec_fault(vcpu));\n+\n+\tif (kvm_slot_has_gmem(s2fd-\u003ememslot))\n+\t\tret = gmem_abort(s2fd);\n+\telse\n+\t\tret = user_mem_abort(s2fd);\n+\treturn ret;\n+}\n+\n /* Resolve the access fault by making the page young again. */\n static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)\n {\n@@ -2250,6 +2314,7 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)\n int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)\n {\n \tstruct kvm_s2_trans nested_trans, *nested = NULL;\n+\tstruct kvm *kvm = vcpu-\u003ekvm;\n \tunsigned long esr;\n \tphys_addr_t fault_ipa; /* The address we faulted on */\n \tphys_addr_t ipa; /* Always the IPA in the L1 guest phys space */\n@@ -2411,19 +2476,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)\n \t\t.hva\t\t= hva,\n \t};\n \n-\tif (kvm_vm_is_protected(vcpu-\u003ekvm)) {\n-\t\tret = pkvm_mem_abort(\u0026s2fd);\n-\t} else {\n-\t\tVM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) \u0026\u0026\n-\t\t\t\t!write_fault \u0026\u0026\n-\t\t\t\t!kvm_vcpu_trap_is_exec_fault(vcpu));\n-\n-\t\tif (kvm_slot_has_gmem(memslot))\n-\t\t\tret = gmem_abort(\u0026s2fd);\n-\t\telse\n-\t\t\tret = user_mem_abort(\u0026s2fd);\n-\t}\n-\n+\tret = kvm-\u003earch.vm_s2_ops-\u003evm_mem_abort(\u0026s2fd);\n \tif (ret == 0)\n \t\tret = 1;\n out:\n@@ -2436,38 +2489,43 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)\n \n bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)\n {\n-\tif (!kvm-\u003earch.mmu.pgt || kvm_vm_is_protected(kvm))\n+\tif (!kvm-\u003earch.mmu.pgt)\n \t\treturn false;\n \n-\t__unmap_stage2_range(\u0026kvm-\u003earch.mmu, range-\u003estart \u003c\u003c PAGE_SHIFT,\n-\t\t\t     (range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT,\n-\t\t\t     range-\u003emay_block);\n+\tkvm_stage2_unmap_range(\u0026kvm-\u003earch.mmu, range-\u003estart \u003c\u003c PAGE_SHIFT,\n+\t\t\t\t(range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT,\n+\t\t\t\trange-\u003emay_block);\n \n \tkvm_nested_s2_unmap(kvm, range-\u003emay_block);\n \treturn false;\n }\n \n-bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n+static bool kvm_vm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n {\n \tu64 size = (range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT;\n \n-\tif (!kvm-\u003earch.mmu.pgt || kvm_vm_is_protected(kvm))\n-\t\treturn false;\n-\n \treturn KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm-\u003earch.mmu.pgt,\n \t\t\t\t\t\t   range-\u003estart \u003c\u003c PAGE_SHIFT,\n \t\t\t\t\t\t   size, true);\n+}\n+\n+bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n+{\n+\tif (!kvm-\u003earch.mmu.pgt || !kvm-\u003earch.vm_s2_ops-\u003evm_age_gfn)\n+\t\treturn false;\n+\n+\treturn kvm-\u003earch.vm_s2_ops-\u003evm_age_gfn(kvm, range);\n \t/*\n \t * TODO: Handle nested_mmu structures here using the reverse mapping in\n \t * a later version of patch series.\n \t */\n }\n \n-bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n+static bool kvm_vm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n {\n \tu64 size = (range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT;\n \n-\tif (!kvm-\u003earch.mmu.pgt || kvm_vm_is_protected(kvm))\n+\tif (!kvm-\u003earch.mmu.pgt)\n \t\treturn false;\n \n \treturn KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm-\u003earch.mmu.pgt,\n@@ -2475,6 +2533,15 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n \t\t\t\t\t\t   size, false);\n }\n \n+bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n+{\n+\n+\tif (!kvm-\u003earch.mmu.pgt || !kvm-\u003earch.vm_s2_ops-\u003evm_test_age_gfn)\n+\t\treturn false;\n+\n+\treturn kvm-\u003earch.vm_s2_ops-\u003evm_test_age_gfn(kvm, range);\n+}\n+\n phys_addr_t kvm_mmu_get_httbr(void)\n {\n \treturn __pa(hyp_pgtable-\u003epgd);\n@@ -2796,3 +2863,33 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)\n \n \ttrace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);\n }\n+\n+static const struct kvm_vm_s2_ops protected_vm_s2_ops = {\n+\t.vm_flush_remote_tlbs\t\t= pkvm_flush_remote_tlbs,\n+\t.vm_flush_remote_tlbs_range\t= pkvm_flush_remote_tlbs_range,\n+\t/*\n+\t * Not supported for Protected VMs under pKVM\n+\t * .vm_age_gfn\n+\t * .vm_test_age_gfn\n+\t * .vm_stage2_unmap_range\n+\t */\n+\t.vm_mem_abort\t\t\t= protected_vm_mem_abort,\n+};\n+\n+static const struct kvm_vm_s2_ops pkvm_vm_s2_ops = {\n+\t.vm_flush_remote_tlbs\t\t= pkvm_flush_remote_tlbs,\n+\t.vm_flush_remote_tlbs_range\t= pkvm_flush_remote_tlbs_range,\n+\t.vm_age_gfn\t\t\t= kvm_vm_age_gfn,\n+\t.vm_test_age_gfn\t\t= kvm_vm_test_age_gfn,\n+\t.vm_stage2_unmap_range\t\t= kvm_vm_stage2_unmap_range,\n+\t.vm_mem_abort\t\t\t= kvm_vm_mem_abort,\n+};\n+\n+static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {\n+\t.vm_flush_remote_tlbs\t\t= kvm_vm_flush_remote_tlbs,\n+\t.vm_flush_remote_tlbs_range\t= kvm_vm_flush_remote_tlbs_range,\n+\t.vm_age_gfn\t\t\t= kvm_vm_age_gfn,\n+\t.vm_test_age_gfn\t\t= kvm_vm_test_age_gfn,\n+\t.vm_stage2_unmap_range\t\t= kvm_vm_stage2_unmap_range,\n+\t.vm_mem_abort\t\t\t= kvm_vm_mem_abort,\n+};\ndiff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c\nindex 8e4c6e4bec123..3948fa46f4d75 100644\n--- a/arch/arm64/kvm/pkvm.c\n+++ b/arch/arm64/kvm/pkvm.c\n@@ -240,7 +240,6 @@ int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)\n \t\treturn ret;\n \n \tkvm-\u003earch.pkvm.handle = ret;\n-\tkvm-\u003earch.pkvm.is_protected = protected;\n \tif (protected) {\n \t\tpr_warn_once(\"kvm: protected VMs are experimental and for development only, tainting kernel\\n\");\n \t\tadd_taint(TAINT_USER, LOCKDEP_STILL_OK);\ndiff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c\nindex 4ceabaa4c30bd..41fbfd92cd292 100644\n--- a/arch/arm64/kvm/pvtime.c\n+++ b/arch/arm64/kvm/pvtime.c\n@@ -67,9 +67,9 @@ gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)\n \treturn base;\n }\n \n-bool kvm_arm_pvtime_supported(void)\n+bool kvm_arm_pvtime_supported(struct kvm *kvm)\n {\n-\treturn !!sched_info_on();\n+\treturn !!sched_info_on() \u0026\u0026 (!kvm || !kvm_vm_is_confidential(kvm));\n }\n \n int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,\n@@ -81,7 +81,7 @@ int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,\n \tint ret = 0;\n \tint idx;\n \n-\tif (!kvm_arm_pvtime_supported() ||\n+\tif (!kvm_arm_pvtime_supported(kvm) ||\n \t    attr-\u003eattr != KVM_ARM_VCPU_PVTIME_IPA)\n \t\treturn -ENXIO;\n \n@@ -110,7 +110,7 @@ int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,\n \tu64 __user *user = (u64 __user *)attr-\u003eaddr;\n \tu64 ipa;\n \n-\tif (!kvm_arm_pvtime_supported() ||\n+\tif (!kvm_arm_pvtime_supported(vcpu-\u003ekvm) ||\n \t    attr-\u003eattr != KVM_ARM_VCPU_PVTIME_IPA)\n \t\treturn -ENXIO;\n \n@@ -126,7 +126,7 @@ int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,\n {\n \tswitch (attr-\u003eattr) {\n \tcase KVM_ARM_VCPU_PVTIME_IPA:\n-\t\tif (kvm_arm_pvtime_supported())\n+\t\tif (kvm_arm_pvtime_supported(vcpu-\u003ekvm))\n \t\t\treturn 0;\n \t}\n \treturn -ENXIO;\ndiff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c\nnew file mode 100644\nindex 0000000000000..5ecc8b3498698\n--- /dev/null\n+++ b/arch/arm64/kvm/rmi.c\n@@ -0,0 +1,18 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Copyright (C) 2023-2026 ARM Ltd.\n+ */\n+\n+#include \u003clinux/kvm_host.h\u003e\n+\n+#include \u003casm/virt.h\u003e\n+\n+void kvm_init_rmi(void)\n+{\n+\tif (kvm_get_mode() != KVM_MODE_RMM)\n+\t\treturn;\n+\n+\t/* TODO: Check if the RMI is available */\n+\n+\t/* Future patch will enable static branch kvm_rmi_is_available */\n+}\ndiff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c\nindex 44aae52c473d7..a58c1fc13ea17 100644\n--- a/arch/arm64/kvm/sys_regs.c\n+++ b/arch/arm64/kvm/sys_regs.c\n@@ -5638,18 +5638,18 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg\n \t\t\t\t    sys_reg_descs, ARRAY_SIZE(sys_reg_descs));\n }\n \n-static unsigned int num_demux_regs(void)\n+static inline unsigned int num_demux_regs(struct kvm_vcpu *vcpu)\n {\n-\treturn CSSELR_MAX;\n+\treturn vcpu_is_rec(vcpu) ? 0 : CSSELR_MAX;\n }\n \n-static int write_demux_regids(u64 __user *uindices)\n+static int write_demux_regids(struct kvm_vcpu *vcpu, u64 __user *uindices)\n {\n \tu64 val = KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;\n \tunsigned int i;\n \n \tval |= KVM_REG_ARM_DEMUX_ID_CCSIDR;\n-\tfor (i = 0; i \u003c CSSELR_MAX; i++) {\n+\tfor (i = 0; i \u003c num_demux_regs(vcpu); i++) {\n \t\tif (put_user(val | i, uindices))\n \t\t\treturn -EFAULT;\n \t\tuindices++;\n@@ -5693,11 +5693,26 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)\n \treturn true;\n }\n \n+static inline bool kvm_realm_sys_reg_hidden_user(const struct kvm_vcpu *vcpu,\n+\t\t\t\t\t\t u64 reg)\n+{\n+\tswitch (reg) {\n+\tcase SYS_ID_AA64DFR0_EL1:\n+\tcase SYS_PMCR_EL0:\n+\t\treturn false;\n+\t}\n+\treturn true;\n+}\n+\n static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,\n \t\t\t    const struct sys_reg_desc *rd,\n \t\t\t    u64 __user **uind,\n \t\t\t    unsigned int *total)\n {\n+\tif (vcpu_is_rec(vcpu) \u0026\u0026\n+\t    kvm_realm_sys_reg_hidden_user(vcpu, reg_to_encoding(rd)))\n+\t\treturn 0;\n+\n \t/*\n \t * Ignore registers we trap but don't save,\n \t * and for which no custom user accessor is provided.\n@@ -5735,7 +5750,7 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)\n \n unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)\n {\n-\treturn num_demux_regs()\n+\treturn num_demux_regs(vcpu)\n \t\t+ walk_sys_regs(vcpu, (u64 __user *)NULL);\n }\n \n@@ -5748,7 +5763,7 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)\n \t\treturn err;\n \tuindices += err;\n \n-\treturn write_demux_regids(uindices);\n+\treturn write_demux_regids(vcpu, uindices);\n }\n \n #define KVM_ARM_FEATURE_ID_RANGE_INDEX(r)\t\t\t\\\ndiff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h\nindex f86a006d67136..06c20612e9e7d 100644\n--- a/include/kvm/arm_psci.h\n+++ b/include/kvm/arm_psci.h\n@@ -10,6 +10,8 @@\n #include \u003clinux/kvm_host.h\u003e\n #include \u003cuapi/linux/psci.h\u003e\n \n+#include \u003casm/kvm_emulate.h\u003e\n+\n #define KVM_ARM_PSCI_0_1\tPSCI_VERSION(0, 1)\n #define KVM_ARM_PSCI_0_2\tPSCI_VERSION(0, 2)\n #define KVM_ARM_PSCI_1_0\tPSCI_VERSION(1, 0)\n"
}

2/1 2026/09/08 20:12 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies ARM64 KVM and Arm Confidential Compute Architecture (CCA / Realm Management Extension) code exclusively, as well as documentation. Since the target architecture is amd64, all modified code paths are exclusive to arm64 and unreachable on amd64.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit af95de954d413df61f83827589de0b2dcd5eadf7
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 8 20:12:50 2026 +0000

    syz-cluster: applied patch under review

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_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 27fe0cd5b2d7a..fbfd85dc999ef 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
@@ -69,6 +70,7 @@ enum kvm_mode {
 	KVM_MODE_DEFAULT,
 	KVM_MODE_PROTECTED,
 	KVM_MODE_NV,
+	KVM_MODE_RMM,
 	KVM_MODE_NONE,
 };
 #ifdef CONFIG_KVM
@@ -150,6 +152,26 @@ struct kvm_vmid {
 	atomic64_t id;
 };
 
+struct kvm_vcpu_ops {
+	void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu);
+	void (*vcpu_put)(struct kvm_vcpu *vcpu);
+};
+
+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);
+	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);
+	int (*vm_mem_abort)(const struct kvm_s2_fault_desc *s2fd);
+};
+
 struct kvm_s2_mmu {
 	struct kvm_vmid vmid;
 
@@ -257,7 +279,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 +327,20 @@ 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_CONFIDENTIAL),
+	VM_PROTECTED_PKVM,	/* Protected VM */
+	VM_REALM,		/* CCA */
+	VM_FLAVOR_MAX,
+};
+
 struct kvm_arch {
 	struct kvm_s2_mmu mmu;
 
+	enum kvm_arm_vm_flavor vm_flavor;
 	/*
 	 * Fine-Grained UNDEF, mimicking the FGT layout defined by the
 	 * architecture. We track them globally, as we present the
@@ -318,6 +350,8 @@ struct kvm_arch {
 	 */
 	u64 fgu[__NR_FGT_GROUP_IDS__];
 
+	const struct kvm_vm_s2_ops *vm_s2_ops;
+
 	/*
 	 * Stage 2 paging state for VMs with nested S2 using a virtual
 	 * VMID.
@@ -417,11 +451,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 */
@@ -846,6 +883,7 @@ struct vncr_tlb;
 
 struct kvm_vcpu_arch {
 	struct kvm_cpu_context ctxt;
+	const struct kvm_vcpu_ops *vcpu_ops;
 
 	/*
 	 * Guest floating point state
@@ -1346,7 +1384,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,
@@ -1504,9 +1542,13 @@ 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_confidential(kvm)	((kvm)->arch.vm_flavor >= __VM_CONFIDENTIAL)
+#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM)
+#define kvm_vm_is_realm(kvm)		((kvm)->arch.vm_flavor == VM_REALM)
 
+#define vcpu_is_confidential(vcpu)	kvm_vm_is_confidential((vcpu)->kvm)
 #define vcpu_is_protected(vcpu)		kvm_vm_is_protected((vcpu)->kvm)
+#define vcpu_is_rec(vcpu)		kvm_vm_is_realm((vcpu)->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_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
index 41a8687938eb6..c2e4b29e605fc 100644
--- a/arch/arm64/include/asm/kvm_pgtable.h
+++ b/arch/arm64/include/asm/kvm_pgtable.h
@@ -8,9 +8,13 @@
 #define __ARM64_KVM_PGTABLE_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>
 
+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/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/arch_timer.c b/arch/arm64/kvm/arch_timer.c
index 6ac3321f4c575..3eddf44f7b1dd 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,
@@ -1079,7 +1093,7 @@ static void timer_context_init(struct kvm_vcpu *vcpu, int timerid)
 
 	ctxt->timer_id = timerid;
 
-	if (!kvm_vm_is_protected(vcpu->kvm)) {
+	if (!vcpu_is_confidential(vcpu)) {
 		if (timerid == TIMER_VTIMER)
 			ctxt->offset.vm_offset = &kvm->arch.timer_data.voffset;
 		else
@@ -1110,7 +1124,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) &&
+	if (!vcpu_is_confidential(vcpu) &&
 	    !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);
@@ -1609,8 +1623,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);
@@ -1736,7 +1754,7 @@ int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
 	if (offset->reserved)
 		return -EINVAL;
 
-	if (kvm_vm_is_protected(kvm))
+	if (kvm_vm_is_confidential(kvm))
 		return -EINVAL;
 
 	mutex_lock(&kvm->lock);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 8b080804bc90b..3fe5a4f77738e 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>
 
@@ -74,6 +75,8 @@ struct kvm_ioctl_cap_map {
 	long ext;
 };
 
+static const struct kvm_vcpu_ops *arm64_vcpu_ops[VM_FLAVOR_MAX];
+
 /* Make KVM_CAP_NR_VCPUS the reference for features we always supported */
 #define KVM_CAP_ARM_BASIC	KVM_CAP_NR_VCPUS
 
@@ -111,6 +114,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);
@@ -132,6 +137,25 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
 }
 
+static 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_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_PTRAUTH_ADDRESS:
+	case KVM_CAP_ARM_PTRAUTH_GENERIC:
+	case KVM_CAP_SYNC_MMU:
+		return true;
+	}
+	return false;
+}
+
 int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 			    struct kvm_enable_cap *cap)
 {
@@ -143,6 +167,9 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, cap->cap))
 		return -EINVAL;
 
+	if (kvm && kvm_vm_is_realm(kvm) && !kvm_realm_ext_allowed(cap->cap))
+		return -EINVAL;
+
 	switch (cap->cap) {
 	case KVM_CAP_ARM_NISV_TO_USER:
 		r = 0;
@@ -214,6 +241,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 +283,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);
@@ -260,9 +311,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 		ret = pkvm_init_host_vm(kvm, type);
 		if (ret)
 			goto err_uninit_mmu;
-	} else if (type & KVM_VM_TYPE_ARM_PROTECTED) {
-		ret = -EINVAL;
-		goto err_uninit_mmu;
 	}
 
 	kvm_vgic_early_init(kvm);
@@ -381,6 +429,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
 		return 0;
 
+	if (kvm && kvm_vm_is_realm(kvm) && !kvm_realm_ext_allowed(ext))
+		return 0;
+
 	switch (ext) {
 	case KVM_CAP_IRQCHIP:
 		r = vgic_present;
@@ -447,7 +498,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);
@@ -548,6 +599,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 	mutex_unlock(&vcpu->mutex);
 #endif
 
+	vcpu->arch.vcpu_ops = arm64_vcpu_ops[vcpu->kvm->arch.vm_flavor];
+
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
 
@@ -662,14 +715,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);
 
@@ -699,10 +749,30 @@ 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:
-	vcpu->cpu = cpu;
+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);
+}
 
+static void vhe_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	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).
@@ -711,32 +781,68 @@ 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);
-	if (has_vhe())
-		kvm_vcpu_load_vhe(vcpu);
+	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;
+	vcpu_load_pvtime(vcpu);
+	vcpu_set_wfx_traps(vcpu);
+	vcpu_set_pauth_traps(vcpu);
+}
 
-	if (kvm_vcpu_should_clear_twi(vcpu))
-		vcpu->arch.hcr_el2 &= ~HCR_TWI;
-	else
-		vcpu->arch.hcr_el2 |= HCR_TWI;
+static void nvhe_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	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).
+	 */
+	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);
+}
 
-	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);
-	}
+static void pkvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	/*
+	 * 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);
+	kvm_arch_vcpu_load_fp(vcpu);
+	kvm_vcpu_pmu_restore_guest(vcpu);
+
+	vcpu_load_pvtime(vcpu);
+	vcpu_set_wfx_traps(vcpu);
+
+	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);
+}
+
+static void realm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	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;
+	vcpu->arch.vcpu_ops->vcpu_load(vcpu, cpu);
 
 	if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus))
 		vcpu_set_on_unsupported_cpu(vcpu);
@@ -744,28 +850,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 (!kvm_vm_is_protected(vcpu->kvm))
-			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);
 	kvm_arm_vmid_clear_active();
+}
 
+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);
+	kvm_arm_vmid_clear_active();
+}
+
+static void pkvm_vcpu_put(struct kvm_vcpu *vcpu)
+{
+	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 (!kvm_vm_is_protected(vcpu->kvm))
+		vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
+
+	nvhe_vcpu_put(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);
 	vcpu_clear_on_unsupported_cpu(vcpu);
 	vcpu->cpu = -1;
 }
@@ -1852,6 +1980,18 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 	return __kvm_arm_vcpu_set_events(vcpu, events);
 }
 
+/*
+ * Realm VCPUs can be finalized after the Realm descriptor is created.
+ * Allow the register list for RECs before the VCPUs are finalized, but
+ * after the SVE is 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)
 {
@@ -1907,7 +2047,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;
@@ -2105,6 +2245,34 @@ 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,
+};
+
+static const struct kvm_vcpu_ops realm_vcpu_ops = {
+	.vcpu_load = realm_vcpu_load,
+	.vcpu_put = realm_vcpu_put,
+};
+
+static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {
+	[VM_VHE] = &vhe_vcpu_ops,
+	[VM_NVHE] = &nvhe_vcpu_ops,
+	[VM_PKVM] = &pkvm_vcpu_ops,
+	[VM_PROTECTED_PKVM] = &pkvm_vcpu_ops,
+	[VM_REALM] = &realm_vcpu_ops,
+};
+
 static unsigned long nvhe_percpu_size(void)
 {
 	return (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_end) -
@@ -3050,6 +3218,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" \
@@ -3155,6 +3325,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);
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index b01d6622b8720..6bec73d1ee050 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -598,8 +598,11 @@ 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 */
+
+	if (kvm_vm_is_realm(vcpu->kvm))
+		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 +619,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 +628,12 @@ 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;
+
+	if (kvm_vm_is_realm(vcpu->kvm))
+		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);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 459bd9eb7e4bc..09961c0056f2b 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -432,7 +432,7 @@ 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);
+	hyp_vm->kvm.arch.vm_flavor = READ_ONCE(host_kvm->arch.vm_flavor);
 	hyp_vm->kvm.arch.flags = 0;
 	pkvm_init_features_from_host(hyp_vm, host_kvm);
 
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>
 
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/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
diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c
index d1c3a352d5a22..bbcf52f01e912 100644
--- a/arch/arm64/kvm/mmio.c
+++ b/arch/arm64/kvm/mmio.c
@@ -168,14 +168,14 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
 	 * No valid syndrome? Ask userspace for help if it has
 	 * volunteered to do so, and bail out otherwise.
 	 *
-	 * In the protected VM case, there isn't much userspace can do
+	 * In the protected/Realm VM case, there isn't much userspace can do
 	 * though, so directly deliver an exception to the guest.
 	 */
 	if (!kvm_vcpu_dabt_isvalid(vcpu)) {
 		trace_kvm_mmio_nisv(*vcpu_pc(vcpu), esr,
 				    kvm_vcpu_get_hfar(vcpu), fault_ipa);
 
-		if (vcpu_is_protected(vcpu))
+		if (vcpu_is_confidential(vcpu))
 			return kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
 
 		if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 9ba86450fe4af..5e7cdbe310fc8 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -37,6 +37,10 @@ static unsigned long __ro_after_init io_map_base;
 
 #define KVM_PGT_FN(fn)		(!is_protected_kvm_enabled() ? fn : p ## fn)
 
+static const struct kvm_vm_s2_ops protected_pkvm_s2_ops;
+static const struct kvm_vm_s2_ops unprotected_pkvm_s2_ops;
+static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops;
+
 static phys_addr_t __stage2_range_addr_end(phys_addr_t addr, phys_addr_t end,
 					   phys_addr_t size)
 {
@@ -166,6 +170,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 +190,36 @@ 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;
+	if (!kvm->arch.vm_s2_ops->vm_flush_remote_tlbs)
+		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)
+{
+	if (!kvm->arch.vm_s2_ops->vm_flush_remote_tlbs_range)
+		return 0;
+
+	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;
@@ -337,13 +363,20 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64
 				   may_block));
 }
 
+static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
+				      phys_addr_t start,
+				      u64 size, bool may_block)
+{
+	__unmap_stage2_range(mmu, start, size, 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)))
-		return;
+	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
 
-	__unmap_stage2_range(mmu, start, size, may_block);
+	if (kvm->arch.vm_s2_ops->vm_stage2_unmap_range)
+		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)
@@ -963,6 +996,18 @@ static void kvm_stage2_destroy(struct kvm_pgtable *pgt)
 	KVM_PGT_FN(kvm_pgtable_stage2_destroy_pgd)(pgt);
 }
 
+static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[VM_FLAVOR_MAX] = {
+	[VM_VHE] = &kvm_default_vm_s2_ops,
+	[VM_NVHE] = &kvm_default_vm_s2_ops,
+	[VM_PKVM] = &unprotected_pkvm_s2_ops,
+	[VM_PROTECTED_PKVM] = &protected_pkvm_s2_ops,
+};
+
+static void kvm_vm_init_vm_s2_ops(struct kvm *kvm, unsigned long type)
+{
+	kvm->arch.vm_s2_ops = arm64_vm_s2_ops[kvm->arch.vm_flavor];
+}
+
 /**
  * kvm_init_stage2_mmu - Initialise a S2 MMU structure
  * @kvm:	The pointer to the KVM structure
@@ -983,6 +1028,9 @@ 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)
+		kvm_vm_init_vm_s2_ops(kvm, type);
 	/*
 	 * If we already have our page tables in place, and that the
 	 * MMU context is the canonical one, we have a bug somewhere,
@@ -1705,7 +1753,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_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 {
 	unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
 	struct kvm_vcpu *vcpu = s2fd->vcpu;
@@ -2143,6 +2191,22 @@ 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)
+{
+	int ret;
+	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))
+		ret = gmem_abort(s2fd);
+	else
+		ret = user_mem_abort(s2fd);
+	return ret;
+}
+
 /* Resolve the access fault by making the page young again. */
 static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
 {
@@ -2250,6 +2314,7 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 {
 	struct kvm_s2_trans nested_trans, *nested = NULL;
+	struct kvm *kvm = vcpu->kvm;
 	unsigned long esr;
 	phys_addr_t fault_ipa; /* The address we faulted on */
 	phys_addr_t ipa; /* Always the IPA in the L1 guest phys space */
@@ -2411,19 +2476,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		.hva		= hva,
 	};
 
-	if (kvm_vm_is_protected(vcpu->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:
@@ -2436,38 +2489,43 @@ 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;
 }
 
-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,
 						   range->start << PAGE_SHIFT,
 						   size, true);
+}
+
+bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	if (!kvm->arch.mmu.pgt || !kvm->arch.vm_s2_ops->vm_age_gfn)
+		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))
+	if (!kvm->arch.mmu.pgt)
 		return false;
 
 	return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
@@ -2475,6 +2533,15 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
 						   size, false);
 }
 
+bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+
+	if (!kvm->arch.mmu.pgt || !kvm->arch.vm_s2_ops->vm_test_age_gfn)
+		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);
@@ -2796,3 +2863,33 @@ 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_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= pkvm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= pkvm_flush_remote_tlbs_range,
+	/*
+	 * Not supported for Protected VMs under pKVM
+	 * .vm_age_gfn
+	 * .vm_test_age_gfn
+	 * .vm_stage2_unmap_range
+	 */
+	.vm_mem_abort			= protected_vm_mem_abort,
+};
+
+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			= 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,
+};
+
+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,
+	.vm_mem_abort			= kvm_vm_mem_abort,
+};
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 8e4c6e4bec123..3948fa46f4d75 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -240,7 +240,6 @@ 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) {
 		pr_warn_once("kvm: protected VMs are experimental and for development only, tainting kernel\n");
 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
diff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c
index 4ceabaa4c30bd..41fbfd92cd292 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_confidential(kvm));
 }
 
 int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
@@ -81,7 +81,7 @@ int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
 	int ret = 0;
 	int idx;
 
-	if (!kvm_arm_pvtime_supported() ||
+	if (!kvm_arm_pvtime_supported(kvm) ||
 	    attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
 		return -ENXIO;
 
@@ -110,7 +110,7 @@ int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
 	u64 __user *user = (u64 __user *)attr->addr;
 	u64 ipa;
 
-	if (!kvm_arm_pvtime_supported() ||
+	if (!kvm_arm_pvtime_supported(vcpu->kvm) ||
 	    attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
 		return -ENXIO;
 
@@ -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;
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 */
+}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 44aae52c473d7..a58c1fc13ea17 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,26 @@ 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)
+{
+	switch (reg) {
+	case SYS_ID_AA64DFR0_EL1:
+	case SYS_PMCR_EL0:
+		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 (vcpu_is_rec(vcpu) &&
+	    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 +5750,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 +5763,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)			\
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)

3/2 2026/09/08 20:12 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 22832
							output: 20
							thoughts: 540
						

Thoughts:
**Analyzing this Patch: ARM64 KVM Focus**

Okay, so the target architecture here is definitely amd64, which is my bread and butter. My initial instinct is to dig into the patch and understand the scope of the changes.

The patch list tells me it's modifying a bunch of files. Primarily, it's hitting a lot of files within the `arch/arm64/` directory. I see a lot of KVM-related files in there, things like `kvm_emulate.h`, `kvm_host.h`, `kvm_pgtable.h`, and a good chunk of C files: `arch_timer.c`, `arm.c`, `guest.c`, etc. I'm seeing changes across a significant portion of the ARM64 KVM implementation: HYP, MMIO, MMU, RMI, the works. The fact that `arch/arm64/kvm/pkvm.c` is being touched is interesting, given my expertise in that area.

But, hold on. Before I dive too deep into the ARM64-specific code, I notice two things: `Documentation/admin-guide/kernel-parameters.txt` and `include/kvm/arm_psci.h` are also on the list. This throws a slight curveball. I need to be sure these don't accidentally affect my target architecture. It's unusual to have ARM64 changes bleed into other architectures without good reason.

So, I need to verify that `include/kvm/arm_psci.h` and any of the other modified code are not used on amd64 or compiled on x86/amd64. I'll need to double-check those files and see what's happening. I need to specifically look at `include/kvm/arm_psci.h` first and then see if anything in the other files are included in the x86-64 build. This may be fine, but I need to be sure. I'll also have a look around and see if any of these changes are gated behind conditionals.



4/2 2026/09/08 20:12 tool grepper
0m Args:
{
  "Expression": "arm_psci.h"
}

Results:
{
  "Output": "arch/arm64/kvm/arm.c-50-#include \u003ckvm/arm_pmu.h\u003e\narch/arm64/kvm/arm.c:51:#include \u003ckvm/arm_psci.h\u003e\narch/arm64/kvm/arm.c-52-#include \u003ckvm/arm_vgic.h\u003e\n--\narch/arm64/kvm/hyp/include/hyp/switch.h-18-\narch/arm64/kvm/hyp/include/hyp/switch.h:19:#include \u003ckvm/arm_psci.h\u003e\narch/arm64/kvm/hyp/include/hyp/switch.h-20-\n--\narch/arm64/kvm/hyp/nvhe/switch.c-14-\narch/arm64/kvm/hyp/nvhe/switch.c:15:#include \u003ckvm/arm_psci.h\u003e\narch/arm64/kvm/hyp/nvhe/switch.c-16-\n--\narch/arm64/kvm/hyp/vhe/switch.c-15-\narch/arm64/kvm/hyp/vhe/switch.c:16:#include \u003ckvm/arm_psci.h\u003e\narch/arm64/kvm/hyp/vhe/switch.c-17-\n--\narch/arm64/kvm/hypercalls.c-9-#include \u003ckvm/arm_hypercalls.h\u003e\narch/arm64/kvm/hypercalls.c:10:#include \u003ckvm/arm_psci.h\u003e\narch/arm64/kvm/hypercalls.c-11-\n--\narch/arm64/kvm/psci.c-15-\narch/arm64/kvm/psci.c:16:#include \u003ckvm/arm_psci.h\u003e\narch/arm64/kvm/psci.c-17-#include \u003ckvm/arm_hypercalls.h\u003e\n"
}

5/2 2026/09/08 20:12 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 24348
							output: 86
							thoughts: 735
						

Thoughts:
**Analysis of Patch for Fuzzing Eligibility**

Okay, so I'm looking at this patch and determining if it's worth fuzzing, specifically in the context of amd64. First, I noticed that `include/kvm/arm_psci.h` is only used by `arch/arm64/kvm/*`. And the change is simply adding an include for `asm/kvm_emulate.h`. Straightforward.

Now, let's hit the negative criteria from Section 2. The key here is "Unreachable execution paths: - Code paths exclusive to architectures other than the target architecture." The target is amd64. This patch series is *completely* about `arch/arm64` – ARM64 KVM Realm/CCA/RMM support and refactoring, plus some documentation updates. No code touched gets compiled or executed on amd64. Boom.

Just to be absolutely certain, I'll quickly double-check the files changed: everything listed, from `Documentation/` to `arch/arm64/` include files and source files, they are all clearly ARM64-specific. No exceptions.

Alright, I'm confident. No other factors.

Therefore, because `WorthFuzzing` should be false because the patch only touches architecture specific code not applicable to the target architecture. So, `FocusSymbols` and `EnableConfigs` should be null. The `Reasoning` will be crystal clear: this patch is all ARM64-specific KVM/RMI/Realm code and Documentation, which is structurally unreachable on amd64.

Time to call `set-results`.



6/2 2026/09/08 20:13 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies ARM64 KVM and Arm Confidential Compute Architecture (CCA / Realm Management Extension) code exclusively, as well as documentation. Since the target architecture is amd64, all modified code paths are exclusive to arm64 and unreachable on amd64.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies ARM64 KVM and Arm Confidential Compute Architecture (CCA / Realm Management Extension) code exclusively, as well as documentation. Since the target architecture is amd64, all modified code paths are exclusive to arm64 and unreachable on amd64.",
  "WorthFuzzing": false
}

7/1 2026/09/08 20:13 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)