AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/15 23:03 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 / RMM / Realm) support located under arch/arm64/ and include/kvm/arm_*. The target architecture is amd64, so these changes are exclusive to arm64 and are not compiled or reachable on amd64.",
  "WorthFuzzing": false
}

1/1 2026/09/15 23:03 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 13711bcf75083192023594bac660ea2a4647e4e9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 15 23:03:13 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..90bfa0fa4f3cf 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);\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,22 @@ 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+\t/* VMs running on a hyp that doesn't trust */\n+\tMARKER(__VM_DISTRUSTING_HYP),\n+\tVM_PKVM,\t\t/* Normal guests on pKVM */\n+\tMARKER(__VM_PROTECTED),\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 +352,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 +453,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 +885,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 +1386,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 +1544,16 @@ 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_protected(kvm)\t((kvm)-\u003earch.vm_flavor \u003e= __VM_PROTECTED)\n+#define kvm_vm_is_protected_pkvm(kvm)\t((kvm)-\u003earch.vm_flavor == VM_PROTECTED_PKVM)\n+#define kvm_vm_is_unprotected_pkvm(kvm)\t((kvm)-\u003earch.vm_flavor == VM_PKVM)\n+\n+#define kvm_vm_hyp_is_distrusting(kvm)\t((kvm)-\u003earch.vm_flavor \u003e __VM_DISTRUSTING_HYP)\n+#define kvm_vm_hyp_is_pkvm(kvm)\t\t(is_protected_kvm_enabled())\n+#define kvm_vm_is_realm(kvm)\t\t((kvm)-\u003earch.vm_flavor == VM_REALM)\n \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..1bc4fe2726e9b 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. */\n@@ -17,7 +17,7 @@\n \n #define HYP_MEMBLOCK_REGIONS 128\n \n-int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);\n+int pkvm_init_host_vm(struct kvm *kvm);\n int pkvm_create_hyp_vm(struct kvm *kvm);\n bool pkvm_hyp_vm_is_created(struct kvm *kvm);\n void pkvm_destroy_hyp_vm(struct kvm *kvm);\n@@ -49,29 +49,10 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)\n \tcase KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:\n \t\treturn false;\n \tdefault:\n-\t\treturn !kvm || !kvm_vm_is_protected(kvm);\n+\t\treturn !kvm || kvm_vm_is_unprotected_pkvm(kvm);\n \t}\n }\n \n-/*\n- * Check whether the KVM VM IOCTL is allowed in pKVM.\n- *\n- * Certain features are allowed only for non-protected VMs in pKVM, which is why\n- * this takes the VM (kvm) as a parameter.\n- */\n-static inline bool kvm_pkvm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)\n-{\n-\tlong ext;\n-\tint r;\n-\n-\tr = kvm_get_cap_for_kvm_ioctl(ioctl, \u0026ext);\n-\n-\tif (WARN_ON_ONCE(r \u003c 0))\n-\t\treturn false;\n-\n-\treturn kvm_pkvm_ext_allowed(kvm, ext);\n-}\n-\n extern struct memblock_region kvm_nvhe_sym(hyp_memory)[];\n extern unsigned int kvm_nvhe_sym(hyp_memblock_nr);\n \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..ef5690956ee64 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,14 +1093,10 @@ 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-\t\tif (timerid == TIMER_VTIMER)\n-\t\t\tctxt-\u003eoffset.vm_offset = \u0026kvm-\u003earch.timer_data.voffset;\n-\t\telse\n-\t\t\tctxt-\u003eoffset.vm_offset = \u0026kvm-\u003earch.timer_data.poffset;\n-\t} else {\n-\t\tctxt-\u003eoffset.vm_offset = NULL;\n-\t}\n+\tif (timerid == TIMER_VTIMER)\n+\t\tctxt-\u003eoffset.vm_offset = \u0026kvm-\u003earch.timer_data.voffset;\n+\telse\n+\t\tctxt-\u003eoffset.vm_offset = \u0026kvm-\u003earch.timer_data.poffset;\n \n \thrtimer_setup(\u0026ctxt-\u003ehrtimer, kvm_hrtimer_expire, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);\n \n@@ -1110,8 +1120,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-\t    !test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, \u0026vcpu-\u003ekvm-\u003earch.flags)) {\n+\tif (!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 \t}\n@@ -1609,8 +1618,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);\ndiff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c\nindex 8b080804bc90b..3d8ede8460e96 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@@ -93,6 +94,7 @@ static const struct kvm_ioctl_cap_map vm_ioctl_caps[] = {\n \t{ KVM_ARM_PREFERRED_TARGET, KVM_CAP_ARM_BASIC },\n };\n \n+static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu);\n /*\n  * Set *ext to the capability.\n  * Return 0 if found, or -EINVAL if no IOCTL matches.\n@@ -111,6 +113,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 +136,58 @@ 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 inline bool kvm_realm_ext_allowed(long ext)\n+{\n+\tswitch (ext) {\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_SVE:\n+\tcase KVM_CAP_ONE_REG:\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+static inline bool kvm_arch_vm_ext_allowed(struct kvm *kvm, long ext)\n+{\n+\t/*\n+\t * We could be called with kvm as NULL, so can't use kvm_vm_* for pKVM\n+\t * flavors\n+\t */\n+\tif (is_protected_kvm_enabled())\n+\t\treturn kvm_pkvm_ext_allowed(kvm, ext);\n+\telse if (kvm \u0026\u0026 kvm_vm_is_realm(kvm))\n+\t\treturn kvm_realm_ext_allowed(ext);\n+\telse\n+\t\treturn true;\n+}\n+\n+/*\n+ * Check whether the KVM VM IOCTL is allowed.\n+ *\n+ * Certain features are allowed only for non-protected VMs in pKVM, which is why\n+ * this takes the VM (kvm) as a parameter.\n+ */\n+static inline bool kvm_arch_vm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)\n+{\n+\tlong ext;\n+\tint r;\n+\n+\tr = kvm_get_cap_for_kvm_ioctl(ioctl, \u0026ext);\n+\tif (WARN_ON_ONCE(r \u003c 0))\n+\t\treturn false;\n+\n+\treturn kvm_arch_vm_ext_allowed(kvm, ext);\n+}\n+\n int kvm_vm_ioctl_enable_cap(struct kvm *kvm,\n \t\t\t    struct kvm_enable_cap *cap)\n {\n@@ -140,7 +196,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,\n \tif (cap-\u003eflags)\n \t\treturn -EINVAL;\n \n-\tif (is_protected_kvm_enabled() \u0026\u0026 !kvm_pkvm_ext_allowed(kvm, cap-\u003ecap))\n+\tif (!kvm_arch_vm_ext_allowed(kvm, cap-\u003ecap))\n \t\treturn -EINVAL;\n \n \tswitch (cap-\u003ecap) {\n@@ -214,6 +270,40 @@ static int kvm_arm_default_max_vcpus(void)\n \treturn vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;\n }\n \n+/*\n+ * Fix the counter offset to 0 for Protected VMs and mark the\n+ * offset flag. The user can't set the offset via KVM_ARM_SET_COUNTER_OFFSET.\n+ */\n+static void kvm_arch_fix_timer_offsets(struct kvm *kvm)\n+{\n+\tif (!kvm_vm_is_protected(kvm))\n+\t\treturn;\n+\n+\t/* Fix the counter offset to 0 and mark the offset initialised */\n+\tkvm-\u003earch.timer_data.poffset = kvm-\u003earch.timer_data.voffset = 0;\n+\tset_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, \u0026kvm-\u003earch.flags);\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 +326,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@@ -252,21 +346,19 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n \tif (ret)\n \t\tgoto err_free_cpumask;\n \n-\tif (is_protected_kvm_enabled()) {\n+\tif (kvm_vm_hyp_is_pkvm(kvm)) {\n \t\t/*\n \t\t * If any failures occur after this is successful, make sure to\n \t\t * call __pkvm_unreserve_vm to unreserve the VM in hyp.\n \t\t */\n-\t\tret = pkvm_init_host_vm(kvm, type);\n+\t\tret = pkvm_init_host_vm(kvm);\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 \n+\tkvm_arch_fix_timer_offsets(kvm);\n \tkvm_timer_init_vm(kvm);\n \n \t/* The maximum number of VCPUs is limited by the host's GIC model */\n@@ -326,7 +418,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)\n \n \tkvm_vgic_destroy(kvm);\n \n-\tif (is_protected_kvm_enabled())\n+\tif (kvm_vm_hyp_is_pkvm(kvm))\n \t\tpkvm_destroy_hyp_vm(kvm);\n \n \tkvm_uninit_stage2_mmu(kvm);\n@@ -378,7 +470,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)\n {\n \tint r;\n \n-\tif (is_protected_kvm_enabled() \u0026\u0026 !kvm_pkvm_ext_allowed(kvm, ext))\n+\tif (!kvm_arch_vm_ext_allowed(kvm, ext))\n \t\treturn 0;\n \n \tswitch (ext) {\n@@ -447,7 +539,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 +640,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)\n \tmutex_unlock(\u0026vcpu-\u003emutex);\n #endif\n \n+\tkvm_init_vcpu_ops(vcpu);\n+\n \t/* Force users to call KVM_ARM_VCPU_INIT */\n \tvcpu_clear_flag(vcpu, VCPU_INITIALIZED);\n \n@@ -588,7 +682,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)\n \n void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)\n {\n-\tif (!is_protected_kvm_enabled())\n+\tif (!kvm_vm_hyp_is_pkvm(vcpu-\u003ekvm))\n \t\tkvm_mmu_free_memory_cache(\u0026vcpu-\u003earch.mmu_page_cache);\n \telse\n \t\tfree_hyp_memcache(\u0026vcpu-\u003earch.pkvm_memcache);\n@@ -662,14 +756,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 +790,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)\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 +822,60 @@ 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)\n+{\n+\tvcpu_prepare_mmu(vcpu);\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)\n+{\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_nvhe(__vgic_v3_restore_vmcr_aprs,\n+\t\t\t  \u0026vcpu-\u003earch.vgic_cpu.vgic_v3);\n+}\n+\n+static void realm_vcpu_load(struct kvm_vcpu *vcpu)\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);\n \n \tif (!cpumask_test_cpu(cpu, vcpu-\u003ekvm-\u003earch.supported_cpus))\n \t\tvcpu_set_on_unsupported_cpu(vcpu);\n@@ -744,28 +883,54 @@ 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_nvhe(__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_unprotected_pkvm(vcpu-\u003ekvm))\n+\t\tvcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);\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+}\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@@ -985,7 +1150,7 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)\n \n \tif (is_protected_kvm_enabled()) {\n \t\t/* Start with the vcpu in a dirty state */\n-\t\tif (!kvm_vm_is_protected(vcpu-\u003ekvm))\n+\t\tif (kvm_vm_is_unprotected_pkvm(vcpu-\u003ekvm))\n \t\t\tvcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);\n \t\tret = pkvm_create_hyp_vm(kvm);\n \t\tif (ret)\n@@ -1852,6 +2017,19 @@ 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 only after the Realm descriptor is created.\n+ * But in order to seal the SVE VL, we need to allow the userspace to read/write\n+ * to the SVE_VL, before everything is finalized.\n+ * Allow the register list for RECs before the VCPUs are 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 +2085,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@@ -2037,7 +2215,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)\n \tvoid __user *argp = (void __user *)arg;\n \tstruct kvm_device_attr attr;\n \n-\tif (is_protected_kvm_enabled() \u0026\u0026 !kvm_pkvm_ioctl_allowed(kvm, ioctl))\n+\tif (!kvm_arch_vm_ioctl_allowed(kvm, ioctl))\n \t\treturn -EINVAL;\n \n \tswitch (ioctl) {\n@@ -2105,6 +2283,43 @@ 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+#define KVM_VCPU_OPS(flavor, ops)\t\\\n+\t[(flavor)] = \t(ops)\n+\n+static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {\n+\tKVM_VCPU_OPS(VM_VHE, \u0026vhe_vcpu_ops),\n+\tKVM_VCPU_OPS(VM_NVHE, \u0026nvhe_vcpu_ops),\n+\tKVM_VCPU_OPS(VM_PKVM, \u0026pkvm_vcpu_ops),\n+\tKVM_VCPU_OPS(VM_PROTECTED_PKVM, \u0026pkvm_vcpu_ops),\n+\tKVM_VCPU_OPS(VM_REALM, \u0026realm_vcpu_ops),\n+};\n+\n+static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)\n+{\n+\tBUILD_BUG_ON(ARRAY_SIZE(arm64_vcpu_ops) != VM_FLAVOR_MAX);\n+\tvcpu-\u003earch.vcpu_ops = arm64_vcpu_ops[vcpu-\u003ekvm-\u003earch.vm_flavor];\n+}\n+\n static unsigned long nvhe_percpu_size(void)\n {\n \treturn (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_end) -\n@@ -3050,6 +3265,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 +3372,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..4ec95810ca89b 100644\n--- a/arch/arm64/kvm/guest.c\n+++ b/arch/arm64/kvm/guest.c\n@@ -73,6 +73,25 @@ static u64 core_reg_offset_from_id(u64 id)\n \treturn id \u0026 ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);\n }\n \n+static bool kvm_realm_validate_core_reg(u64 off)\n+{\n+\t/*\n+\t * Note that GPRs can only sometimes be controlled by the VMM.\n+\t * For PSCI only X0-X6 are used, higher registers are ignored (restored\n+\t * from the REC).\n+\t * For HOST_CALL all of X0-X30 are copied to the RsiHostCall structure.\n+\t * For emulated MMIO X0 is always used.\n+\t * PC can only be set before the realm is activated.\n+\t */\n+\tswitch (off) {\n+\tcase KVM_REG_ARM_CORE_REG(regs.regs[0]) ...\n+\t     KVM_REG_ARM_CORE_REG(regs.regs[30]):\n+\tcase KVM_REG_ARM_CORE_REG(regs.pc):\n+\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)\n {\n \tint size;\n@@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,\n \t\tu64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;\n \t\tint size = core_reg_size_from_offset(vcpu, i);\n \n+\t\tif (vcpu_is_rec(vcpu) \u0026\u0026 !kvm_realm_validate_core_reg(i))\n+\t\t\tcontinue;\n+\n \t\tif (size \u003c 0)\n \t\t\tcontinue;\n \n@@ -598,8 +620,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_vm_is_realm(vcpu-\u003ekvm))\n+\t\treturn 1; /* KVM_REG_ARM64_SVE_VLS */\n+\n+\tif (!kvm_arm_vcpu_sve_finalized(vcpu))\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 +641,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 +650,13 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,\n \t\treturn -EFAULT;\n \t++num_regs;\n \n+\t/* For Realms only support SVE_VLS */\n+\tif (kvm_vm_is_realm(vcpu-\u003ekvm))\n+\t\treturn num_regs;\n+\n+\tif (!kvm_arm_vcpu_sve_finalized(vcpu))\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);\n@@ -705,6 +734,11 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)\n \tif ((reg-\u003eid \u0026 ~KVM_REG_SIZE_MASK) \u003e\u003e 32 != KVM_REG_ARM64 \u003e\u003e 32)\n \t\treturn -EINVAL;\n \n+\t/*\n+\t  * We don't filter out the register reads for Realms, like we do for\n+\t  * the user writes. We expose junk data for the VMM instead of\n+\t  * denying the requests.\n+\t  */\n \tswitch (reg-\u003eid \u0026 KVM_REG_ARM_COPROC_MASK) {\n \tcase KVM_REG_ARM_CORE:\treturn get_core_reg(vcpu, reg);\n \tcase KVM_REG_ARM_FW:\n@@ -716,12 +750,41 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)\n \treturn kvm_arm_sys_reg_get_reg(vcpu, reg);\n }\n \n+#define KVM_REG_ARM_ID_AA64DFR0_EL1\tARM64_SYS_REG(3, 0, 0, 5, 0)\n+/*\n+ * The RMI ABI only enables setting some GPRs and PC. The selection of GPRs\n+ * that are available depends on the Realm state and the reason for the last\n+ * exit.  All other registers are reset to architectural or otherwise defined\n+ * reset values by the RMM, except for a few configuration fields that\n+ * correspond to Realm parameters.\n+ */\n+static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,\n+\t\t\t\t   const struct kvm_one_reg *reg)\n+{\n+\tif ((reg-\u003eid \u0026 KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) {\n+\t\tu64 off = core_reg_offset_from_id(reg-\u003eid);\n+\n+\t\treturn kvm_realm_validate_core_reg(off);\n+\t} else {\n+\t\tswitch (reg-\u003eid) {\n+\t\tcase KVM_REG_ARM_ID_AA64DFR0_EL1:\n+\t\tcase KVM_REG_ARM64_SVE_VLS:\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\n+\treturn false;\n+}\n+\n int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)\n {\n \t/* We currently use nothing arch-specific in upper 32 bits */\n \tif ((reg-\u003eid \u0026 ~KVM_REG_SIZE_MASK) \u003e\u003e 32 != KVM_REG_ARM64 \u003e\u003e 32)\n \t\treturn -EINVAL;\n \n+\tif (kvm_vm_is_realm(vcpu-\u003ekvm) \u0026\u0026 !validate_realm_set_reg(vcpu, reg))\n+\t\treturn -EINVAL;\n+\n \tswitch (reg-\u003eid \u0026 KVM_REG_ARM_COPROC_MASK) {\n \tcase KVM_REG_ARM_CORE:\treturn set_core_reg(vcpu, reg);\n \tcase KVM_REG_ARM_FW:\ndiff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c\nindex db37678dcb05c..384c5d258c7f8 100644\n--- a/arch/arm64/kvm/handle_exit.c\n+++ b/arch/arm64/kvm/handle_exit.c\n@@ -490,7 +490,7 @@ static void handle_exit_pkvm_state(struct kvm_vcpu *vcpu, int exception_index)\n {\n \tint exception_code = ARM_EXCEPTION_CODE(exception_index);\n \n-\tif (!is_protected_kvm_enabled() || kvm_vm_is_protected(vcpu-\u003ekvm))\n+\tif (!kvm_vm_is_unprotected_pkvm(vcpu-\u003ekvm))\n \t\treturn;\n \n \t/*\ndiff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c\nindex 459bd9eb7e4bc..9b69228f8402c 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 \n@@ -528,19 +528,19 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,\n \thyp_vcpu-\u003evcpu.arch.cflags = READ_ONCE(host_vcpu-\u003earch.cflags);\n \thyp_vcpu-\u003evcpu.arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;\n \n-\tif (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {\n-\t\t/*\n-\t\t * Timer offsets are pointing to the untrusted KVM copy,\n-\t\t * which is pinned in __pkvm_init_vm() for the VM life time.\n-\t\t * It is worth noting that hyp_vm-\u003ehost_kvm points to an EL2\n-\t\t * linear map address and timer_get_offset() will use\n-\t\t * kern_hyp_va() which is safe as it is idempotent.\n-\t\t */\n-\t\tvcpu_vtimer(\u0026hyp_vcpu-\u003evcpu)-\u003eoffset.vm_offset =\n-\t\t\t\u0026hyp_vm-\u003ehost_kvm-\u003earch.timer_data.voffset;\n-\t\tvcpu_ptimer(\u0026hyp_vcpu-\u003evcpu)-\u003eoffset.vm_offset =\n-\t\t\t\u0026hyp_vm-\u003ehost_kvm-\u003earch.timer_data.poffset;\n-\t}\n+\t/*\n+\t * Timer offsets are pointing to the untrusted KVM copy,\n+\t * which is pinned in __pkvm_init_vm() for the VM life time.\n+\t * It is worth noting that hyp_vm-\u003ehost_kvm points to an EL2\n+\t * linear map address and timer_get_offset() will use\n+\t * kern_hyp_va() which is safe as it is idempotent.\n+\t * Also for protected VMs the offset is fixed to 0 and is prevented\n+\t * from changing.\n+\t */\n+\tvcpu_vtimer(\u0026hyp_vcpu-\u003evcpu)-\u003eoffset.vm_offset =\n+\t\t\u0026hyp_vm-\u003ehost_kvm-\u003earch.timer_data.voffset;\n+\tvcpu_ptimer(\u0026hyp_vcpu-\u003evcpu)-\u003eoffset.vm_offset =\n+\t\t\u0026hyp_vm-\u003ehost_kvm-\u003earch.timer_data.poffset;\n \n \tret = pkvm_vcpu_init_sysregs(hyp_vcpu);\n \tif (ret)\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/mmu.c b/arch/arm64/kvm/mmu.c\nindex 9ba86450fe4af..cc264726a6df8 100644\n--- a/arch/arm64/kvm/mmu.c\n+++ b/arch/arm64/kvm/mmu.c\n@@ -37,6 +37,8 @@ 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 int kvm_vm_init_vm_s2_ops(struct kvm *kvm);\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 +168,24 @@ 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+static int realm_vm_flush_remote_tlbs(struct kvm *kvm)\n+{\n+\t/* Nothing to do here, RMM takes care of this */\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 +194,43 @@ 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 1;\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+static int realm_vm_flush_remote_tlbs_range(struct kvm *kvm,\n+\t\t\t\t\t   gfn_t gfn, u64 nr_pages)\n+{\n+\t/* Nothing to do here, RMM takes care of this */\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 1;\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 +374,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@@ -983,6 +1027,12 @@ 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\terr = kvm_vm_init_vm_s2_ops(kvm);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\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@@ -1214,7 +1264,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,\n \t\t\t\t     KVM_PGTABLE_PROT_R |\n \t\t\t\t     (writable ? KVM_PGTABLE_PROT_W : 0);\n \n-\tif (is_protected_kvm_enabled())\n+\tif (kvm_vm_hyp_is_distrusting(kvm))\n \t\treturn -EPERM;\n \n \tsize += offset_in_page(guest_ipa);\n@@ -1705,7 +1755,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 +2193,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 +2316,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 +2478,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,45 +2491,57 @@ 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-\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, 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@@ -2624,7 +2691,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,\n \thva_t hva, reg_end;\n \tint ret = 0;\n \n-\tif (kvm_vm_is_protected(kvm)) {\n+\tif (kvm_vm_is_protected_pkvm(kvm)) {\n \t\t/* Cannot modify memslots once a pVM has run. */\n \t\tif (pkvm_hyp_vm_is_created(kvm) \u0026\u0026\n \t\t    (change == KVM_MR_DELETE || change == KVM_MR_MOVE)) {\n@@ -2796,3 +2863,64 @@ 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_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/*\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+};\n+\n+static const struct kvm_vm_s2_ops realm_vm_s2_ops = {\n+\t.vm_flush_remote_tlbs\t\t= realm_vm_flush_remote_tlbs,\n+\t.vm_flush_remote_tlbs_range\t= realm_vm_flush_remote_tlbs_range,\n+\t.vm_mem_abort\t\t\t= kvm_vm_mem_abort,\n+\t/*\n+\t * Not supported for Realms\n+\t *\t.vm_age_gfn\t\t\t= realm_vm_age_gfn,\n+\t *\t.vm_test_age_gfn\t\t= realm_vm_test_age_gfn,\n+\t */\n+};\n+\n+#define KVM_VM_S2_OPS(flavor, ops)\t\t\\\n+\t\t[flavor] = ops\n+static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {\n+\tKVM_VM_S2_OPS(VM_VHE, \u0026kvm_default_vm_s2_ops),\n+\tKVM_VM_S2_OPS(VM_NVHE, \u0026kvm_default_vm_s2_ops),\n+\tKVM_VM_S2_OPS(VM_PKVM, \u0026pkvm_vm_s2_ops),\n+\tKVM_VM_S2_OPS(VM_PROTECTED_PKVM, \u0026protected_pkvm_vm_s2_ops),\n+\tKVM_VM_S2_OPS(VM_REALM, \u0026realm_vm_s2_ops),\n+};\n+\n+static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)\n+{\n+\tBUILD_BUG_ON(ARRAY_SIZE(arm64_vm_s2_ops) != VM_FLAVOR_MAX);\n+\n+\tkvm-\u003earch.vm_s2_ops = arm64_vm_s2_ops[kvm-\u003earch.vm_flavor];\n+\tif (WARN_ON(!kvm-\u003earch.vm_s2_ops))\n+\t\treturn -EINVAL;\n+\treturn 0;\n+}\ndiff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c\nindex 8e4c6e4bec123..8e9176a700926 100644\n--- a/arch/arm64/kvm/pkvm.c\n+++ b/arch/arm64/kvm/pkvm.c\n@@ -229,10 +229,9 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm)\n \tmutex_unlock(\u0026kvm-\u003earch.config_lock);\n }\n \n-int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)\n+int pkvm_init_host_vm(struct kvm *kvm)\n {\n \tint ret;\n-\tbool protected = type \u0026 KVM_VM_TYPE_ARM_PROTECTED;\n \n \t/* Reserve the VM in hyp and obtain a hyp handle for the VM. */\n \tret = kvm_call_hyp_nvhe(__pkvm_reserve_vm);\n@@ -240,8 +239,7 @@ 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+\tif (kvm_vm_is_protected(kvm)) {\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);\n \t}\ndiff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c\nindex 4ceabaa4c30bd..579e0a4720ad2 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_protected(kvm));\n }\n \n int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,\n@@ -81,8 +81,8 @@ 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-\t    attr-\u003eattr != KVM_ARM_VCPU_PVTIME_IPA)\n+\tif (!kvm_arm_pvtime_supported(kvm) ||\n+\t    (attr-\u003eattr != KVM_ARM_VCPU_PVTIME_IPA))\n \t\treturn -ENXIO;\n \n \tif (get_user(ipa, user))\n@@ -110,8 +110,8 @@ 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-\t    attr-\u003eattr != KVM_ARM_VCPU_PVTIME_IPA)\n+\tif (!kvm_arm_pvtime_supported(vcpu-\u003ekvm) ||\n+\t    (attr-\u003eattr != KVM_ARM_VCPU_PVTIME_IPA))\n \t\treturn -ENXIO;\n \n \tipa = vcpu-\u003earch.steal.base;\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..520cec19f49fa 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,28 @@ 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+\tif (!vcpu_is_rec(vcpu))\n+\t\treturn false;\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 (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 +5752,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 +5765,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/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c\nindex 4012df6002ea6..452c085073f3e 100644\n--- a/arch/arm64/kvm/vgic/vgic-init.c\n+++ b/arch/arm64/kvm/vgic/vgic-init.c\n@@ -84,6 +84,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)\n \t\t!kvm_vgic_global_state.can_emulate_gicv2)\n \t\treturn -ENODEV;\n \n+\t/* Realms only support VGIC_V3 */\n+\tif (kvm_vm_is_realm(kvm) \u0026\u0026 type != KVM_DEV_TYPE_ARM_VGIC_V3)\n+\t\treturn -ENODEV;\n \t/*\n \t * Ensure mutual exclusion with vCPU creation and any vCPU ioctls by:\n \t *\ndiff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h\nindex bc6f2fdd7ad33..4f0aa3bb69f45 100644\n--- a/include/kvm/arm_arch_timer.h\n+++ b/include/kvm/arm_arch_timer.h\n@@ -176,8 +176,7 @@ static inline bool has_cntpoff(void)\n \t\tif (__ctxt) {\t\t\t\t\t\t\\\n \t\t\tstruct arch_timer_offset *ato = \u0026__ctxt-\u003eoffset;\\\n \t\t\t\t\t\t\t\t\t\\\n-\t\t\tif (ato-\u003evm_offset)\t\t\t\t\\\n-\t\t\t\toff += *KERN_HYP_VA(ato-\u003evm_offset);\t\\\n+\t\t\toff += *KERN_HYP_VA(ato-\u003evm_offset);\t\t\\\n \t\t\tif (ato-\u003evcpu_offset)\t\t\t\t\\\n \t\t\t\toff += *KERN_HYP_VA(ato-\u003evcpu_offset);\t\\\n \t\t}\t\t\t\t\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/15 23:03 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 / RMM / Realm) support located under arch/arm64/ and include/kvm/arm_*. The target architecture is amd64, so these changes are exclusive to arm64 and are not compiled or reachable 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 13711bcf75083192023594bac660ea2a4647e4e9
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 15 23:03:13 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..90bfa0fa4f3cf 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);
+	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,22 @@ enum fgt_group_id {
 	__NR_FGT_GROUP_IDS__
 };
 
+enum kvm_arm_vm_flavor {
+	VM_NVHE,
+	VM_VHE,
+	/* VMs running on a hyp that doesn't trust */
+	MARKER(__VM_DISTRUSTING_HYP),
+	VM_PKVM,		/* Normal guests on pKVM */
+	MARKER(__VM_PROTECTED),
+	VM_PROTECTED_PKVM,	/* Protected VM */
+	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 +352,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 +453,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 +885,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 +1386,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 +1544,16 @@ struct kvm *kvm_arch_alloc_vm(void);
 
 #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE
 
-#define kvm_vm_is_protected(kvm)	(is_protected_kvm_enabled() && (kvm)->arch.pkvm.is_protected)
+#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor >= __VM_PROTECTED)
+#define kvm_vm_is_protected_pkvm(kvm)	((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM)
+#define kvm_vm_is_unprotected_pkvm(kvm)	((kvm)->arch.vm_flavor == VM_PKVM)
+
+#define kvm_vm_hyp_is_distrusting(kvm)	((kvm)->arch.vm_flavor > __VM_DISTRUSTING_HYP)
+#define kvm_vm_hyp_is_pkvm(kvm)		(is_protected_kvm_enabled())
+#define kvm_vm_is_realm(kvm)		((kvm)->arch.vm_flavor == VM_REALM)
 
 #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..1bc4fe2726e9b 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. */
@@ -17,7 +17,7 @@
 
 #define HYP_MEMBLOCK_REGIONS 128
 
-int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);
+int pkvm_init_host_vm(struct kvm *kvm);
 int pkvm_create_hyp_vm(struct kvm *kvm);
 bool pkvm_hyp_vm_is_created(struct kvm *kvm);
 void pkvm_destroy_hyp_vm(struct kvm *kvm);
@@ -49,29 +49,10 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
 	case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
 		return false;
 	default:
-		return !kvm || !kvm_vm_is_protected(kvm);
+		return !kvm || kvm_vm_is_unprotected_pkvm(kvm);
 	}
 }
 
-/*
- * Check whether the KVM VM IOCTL is allowed in pKVM.
- *
- * Certain features are allowed only for non-protected VMs in pKVM, which is why
- * this takes the VM (kvm) as a parameter.
- */
-static inline bool kvm_pkvm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
-{
-	long ext;
-	int r;
-
-	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
-
-	if (WARN_ON_ONCE(r < 0))
-		return false;
-
-	return kvm_pkvm_ext_allowed(kvm, ext);
-}
-
 extern struct memblock_region kvm_nvhe_sym(hyp_memory)[];
 extern unsigned int kvm_nvhe_sym(hyp_memblock_nr);
 
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
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..ef5690956ee64 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,14 +1093,10 @@ static void timer_context_init(struct kvm_vcpu *vcpu, int timerid)
 
 	ctxt->timer_id = timerid;
 
-	if (!kvm_vm_is_protected(vcpu->kvm)) {
-		if (timerid == TIMER_VTIMER)
-			ctxt->offset.vm_offset = &kvm->arch.timer_data.voffset;
-		else
-			ctxt->offset.vm_offset = &kvm->arch.timer_data.poffset;
-	} else {
-		ctxt->offset.vm_offset = NULL;
-	}
+	if (timerid == TIMER_VTIMER)
+		ctxt->offset.vm_offset = &kvm->arch.timer_data.voffset;
+	else
+		ctxt->offset.vm_offset = &kvm->arch.timer_data.poffset;
 
 	hrtimer_setup(&ctxt->hrtimer, kvm_hrtimer_expire, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
 
@@ -1110,8 +1120,7 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
 		timer_context_init(vcpu, i);
 
 	/* Synchronize offsets across timers of a VM if not already provided */
-	if (!vcpu_is_protected(vcpu) &&
-	    !test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
+	if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
 		timer_set_offset(vcpu_vtimer(vcpu), kvm_phys_timer_read());
 		timer_set_offset(vcpu_ptimer(vcpu), 0);
 	}
@@ -1609,8 +1618,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);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 8b080804bc90b..3d8ede8460e96 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>
 
@@ -93,6 +94,7 @@ static const struct kvm_ioctl_cap_map vm_ioctl_caps[] = {
 	{ KVM_ARM_PREFERRED_TARGET, KVM_CAP_ARM_BASIC },
 };
 
+static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu);
 /*
  * Set *ext to the capability.
  * Return 0 if found, or -EINVAL if no IOCTL matches.
@@ -111,6 +113,8 @@ long kvm_get_cap_for_kvm_ioctl(unsigned int ioctl, long *ext)
 	return -EINVAL;
 }
 
+DEFINE_STATIC_KEY_FALSE(kvm_rmi_is_available);
+
 DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);
 
 DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_base);
@@ -132,6 +136,58 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
 }
 
+static inline bool kvm_realm_ext_allowed(long ext)
+{
+	switch (ext) {
+	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_SVE:
+	case KVM_CAP_ONE_REG:
+	case KVM_CAP_ARM_PTRAUTH_ADDRESS:
+	case KVM_CAP_ARM_PTRAUTH_GENERIC:
+	case KVM_CAP_SYNC_MMU:
+		return true;
+	}
+	return false;
+}
+
+static inline bool kvm_arch_vm_ext_allowed(struct kvm *kvm, long ext)
+{
+	/*
+	 * We could be called with kvm as NULL, so can't use kvm_vm_* for pKVM
+	 * flavors
+	 */
+	if (is_protected_kvm_enabled())
+		return kvm_pkvm_ext_allowed(kvm, ext);
+	else if (kvm && kvm_vm_is_realm(kvm))
+		return kvm_realm_ext_allowed(ext);
+	else
+		return true;
+}
+
+/*
+ * Check whether the KVM VM IOCTL is allowed.
+ *
+ * Certain features are allowed only for non-protected VMs in pKVM, which is why
+ * this takes the VM (kvm) as a parameter.
+ */
+static inline bool kvm_arch_vm_ioctl_allowed(struct kvm *kvm, unsigned int ioctl)
+{
+	long ext;
+	int r;
+
+	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
+	if (WARN_ON_ONCE(r < 0))
+		return false;
+
+	return kvm_arch_vm_ext_allowed(kvm, ext);
+}
+
 int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 			    struct kvm_enable_cap *cap)
 {
@@ -140,7 +196,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 	if (cap->flags)
 		return -EINVAL;
 
-	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, cap->cap))
+	if (!kvm_arch_vm_ext_allowed(kvm, cap->cap))
 		return -EINVAL;
 
 	switch (cap->cap) {
@@ -214,6 +270,40 @@ static int kvm_arm_default_max_vcpus(void)
 	return vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
 }
 
+/*
+ * Fix the counter offset to 0 for Protected VMs and mark the
+ * offset flag. The user can't set the offset via KVM_ARM_SET_COUNTER_OFFSET.
+ */
+static void kvm_arch_fix_timer_offsets(struct kvm *kvm)
+{
+	if (!kvm_vm_is_protected(kvm))
+		return;
+
+	/* Fix the counter offset to 0 and mark the offset initialised */
+	kvm->arch.timer_data.poffset = kvm->arch.timer_data.voffset = 0;
+	set_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &kvm->arch.flags);
+}
+
+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 +326,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);
@@ -252,21 +346,19 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 	if (ret)
 		goto err_free_cpumask;
 
-	if (is_protected_kvm_enabled()) {
+	if (kvm_vm_hyp_is_pkvm(kvm)) {
 		/*
 		 * If any failures occur after this is successful, make sure to
 		 * call __pkvm_unreserve_vm to unreserve the VM in hyp.
 		 */
-		ret = pkvm_init_host_vm(kvm, type);
+		ret = pkvm_init_host_vm(kvm);
 		if (ret)
 			goto err_uninit_mmu;
-	} else if (type & KVM_VM_TYPE_ARM_PROTECTED) {
-		ret = -EINVAL;
-		goto err_uninit_mmu;
 	}
 
 	kvm_vgic_early_init(kvm);
 
+	kvm_arch_fix_timer_offsets(kvm);
 	kvm_timer_init_vm(kvm);
 
 	/* The maximum number of VCPUs is limited by the host's GIC model */
@@ -326,7 +418,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 
 	kvm_vgic_destroy(kvm);
 
-	if (is_protected_kvm_enabled())
+	if (kvm_vm_hyp_is_pkvm(kvm))
 		pkvm_destroy_hyp_vm(kvm);
 
 	kvm_uninit_stage2_mmu(kvm);
@@ -378,7 +470,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 {
 	int r;
 
-	if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
+	if (!kvm_arch_vm_ext_allowed(kvm, ext))
 		return 0;
 
 	switch (ext) {
@@ -447,7 +539,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 +640,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
 	mutex_unlock(&vcpu->mutex);
 #endif
 
+	kvm_init_vcpu_ops(vcpu);
+
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
 
@@ -588,7 +682,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
 
 void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 {
-	if (!is_protected_kvm_enabled())
+	if (!kvm_vm_hyp_is_pkvm(vcpu->kvm))
 		kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
 	else
 		free_hyp_memcache(&vcpu->arch.pkvm_memcache);
@@ -662,14 +756,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 +790,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)
+{
+	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 +822,60 @@ 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)
+{
+	vcpu_prepare_mmu(vcpu);
+	kvm_timer_vcpu_load(vcpu);
+	kvm_vgic_load(vcpu);
+	kvm_vcpu_load_debug(vcpu);
+	kvm_vcpu_load_fgt(vcpu);
+	kvm_arch_vcpu_load_fp(vcpu);
+	kvm_vcpu_pmu_restore_guest(vcpu);
 
+	vcpu_load_pvtime(vcpu);
+	vcpu_set_wfx_traps(vcpu);
 	vcpu_set_pauth_traps(vcpu);
+}
 
-	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)
+{
+	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_nvhe(__vgic_v3_restore_vmcr_aprs,
+			  &vcpu->arch.vgic_cpu.vgic_v3);
+}
+
+static void realm_vcpu_load(struct kvm_vcpu *vcpu)
+{
+	kvm_timer_vcpu_load(vcpu);
+	kvm_vgic_load(vcpu);
+	vcpu_set_wfx_traps(vcpu);
+}
+
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+	vcpu->cpu = cpu;
+	vcpu->arch.vcpu_ops->vcpu_load(vcpu);
 
 	if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus))
 		vcpu_set_on_unsupported_cpu(vcpu);
@@ -744,28 +883,54 @@ 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_nvhe(__vgic_v3_save_aprs, &vcpu->arch.vgic_cpu.vgic_v3);
+	kvm_call_hyp_nvhe(__pkvm_vcpu_put);
+
+	/* __pkvm_vcpu_put implies a sync of the state */
+	if (kvm_vm_is_unprotected_pkvm(vcpu->kvm))
+		vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
+
+	kvm_vcpu_put_debug(vcpu);
+	kvm_arch_vcpu_put_fp(vcpu);
+	kvm_timer_vcpu_put(vcpu);
+	kvm_vgic_put(vcpu);
+	kvm_vcpu_pmu_restore_host(vcpu);
+}
+
+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;
 }
@@ -985,7 +1150,7 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
 
 	if (is_protected_kvm_enabled()) {
 		/* Start with the vcpu in a dirty state */
-		if (!kvm_vm_is_protected(vcpu->kvm))
+		if (kvm_vm_is_unprotected_pkvm(vcpu->kvm))
 			vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
 		ret = pkvm_create_hyp_vm(kvm);
 		if (ret)
@@ -1852,6 +2017,19 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 	return __kvm_arm_vcpu_set_events(vcpu, events);
 }
 
+/*
+ * Realm VCPUs can be finalized only after the Realm descriptor is created.
+ * But in order to seal the SVE VL, we need to allow the userspace to read/write
+ * to the SVE_VL, before everything is finalized.
+ * Allow the register list for RECs before the VCPUs are finalized.
+ */
+static bool kvm_arm_vcpu_reg_list_allowed(struct kvm_vcpu *vcpu)
+{
+	if (kvm_arm_vcpu_is_finalized(vcpu))
+		return true;
+	return vcpu_is_rec(vcpu);
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -1907,7 +2085,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;
@@ -2037,7 +2215,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
 	void __user *argp = (void __user *)arg;
 	struct kvm_device_attr attr;
 
-	if (is_protected_kvm_enabled() && !kvm_pkvm_ioctl_allowed(kvm, ioctl))
+	if (!kvm_arch_vm_ioctl_allowed(kvm, ioctl))
 		return -EINVAL;
 
 	switch (ioctl) {
@@ -2105,6 +2283,43 @@ 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,
+};
+
+#define KVM_VCPU_OPS(flavor, ops)	\
+	[(flavor)] = 	(ops)
+
+static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {
+	KVM_VCPU_OPS(VM_VHE, &vhe_vcpu_ops),
+	KVM_VCPU_OPS(VM_NVHE, &nvhe_vcpu_ops),
+	KVM_VCPU_OPS(VM_PKVM, &pkvm_vcpu_ops),
+	KVM_VCPU_OPS(VM_PROTECTED_PKVM, &pkvm_vcpu_ops),
+	KVM_VCPU_OPS(VM_REALM, &realm_vcpu_ops),
+};
+
+static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)
+{
+	BUILD_BUG_ON(ARRAY_SIZE(arm64_vcpu_ops) != VM_FLAVOR_MAX);
+	vcpu->arch.vcpu_ops = arm64_vcpu_ops[vcpu->kvm->arch.vm_flavor];
+}
+
 static unsigned long nvhe_percpu_size(void)
 {
 	return (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_end) -
@@ -3050,6 +3265,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 +3372,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..4ec95810ca89b 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -73,6 +73,25 @@ static u64 core_reg_offset_from_id(u64 id)
 	return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
 }
 
+static bool kvm_realm_validate_core_reg(u64 off)
+{
+	/*
+	 * Note that GPRs can only sometimes be controlled by the VMM.
+	 * For PSCI only X0-X6 are used, higher registers are ignored (restored
+	 * from the REC).
+	 * For HOST_CALL all of X0-X30 are copied to the RsiHostCall structure.
+	 * For emulated MMIO X0 is always used.
+	 * PC can only be set before the realm is activated.
+	 */
+	switch (off) {
+	case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
+	     KVM_REG_ARM_CORE_REG(regs.regs[30]):
+	case KVM_REG_ARM_CORE_REG(regs.pc):
+		return true;
+	}
+	return false;
+}
+
 static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
 {
 	int size;
@@ -553,6 +572,9 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,
 		u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
 		int size = core_reg_size_from_offset(vcpu, i);
 
+		if (vcpu_is_rec(vcpu) && !kvm_realm_validate_core_reg(i))
+			continue;
+
 		if (size < 0)
 			continue;
 
@@ -598,8 +620,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_vm_is_realm(vcpu->kvm))
+		return 1; /* KVM_REG_ARM64_SVE_VLS */
+
+	if (!kvm_arm_vcpu_sve_finalized(vcpu))
+		return 1; /* KVM_REG_ARM64_SVE_VLS */
 
 	return slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */)
 		+ 1; /* KVM_REG_ARM64_SVE_VLS */
@@ -616,9 +641,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 +650,13 @@ static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
 		return -EFAULT;
 	++num_regs;
 
+	/* For Realms only support SVE_VLS */
+	if (kvm_vm_is_realm(vcpu->kvm))
+		return num_regs;
+
+	if (!kvm_arm_vcpu_sve_finalized(vcpu))
+		return num_regs;
+
 	for (i = 0; i < slices; i++) {
 		for (n = 0; n < SVE_NUM_ZREGS; n++) {
 			reg = KVM_REG_ARM64_SVE_ZREG(n, i);
@@ -705,6 +734,11 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
 		return -EINVAL;
 
+	/*
+	  * We don't filter out the register reads for Realms, like we do for
+	  * the user writes. We expose junk data for the VMM instead of
+	  * denying the requests.
+	  */
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return get_core_reg(vcpu, reg);
 	case KVM_REG_ARM_FW:
@@ -716,12 +750,41 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return kvm_arm_sys_reg_get_reg(vcpu, reg);
 }
 
+#define KVM_REG_ARM_ID_AA64DFR0_EL1	ARM64_SYS_REG(3, 0, 0, 5, 0)
+/*
+ * The RMI ABI only enables setting some GPRs and PC. The selection of GPRs
+ * that are available depends on the Realm state and the reason for the last
+ * exit.  All other registers are reset to architectural or otherwise defined
+ * reset values by the RMM, except for a few configuration fields that
+ * correspond to Realm parameters.
+ */
+static bool validate_realm_set_reg(struct kvm_vcpu *vcpu,
+				   const struct kvm_one_reg *reg)
+{
+	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) {
+		u64 off = core_reg_offset_from_id(reg->id);
+
+		return kvm_realm_validate_core_reg(off);
+	} else {
+		switch (reg->id) {
+		case KVM_REG_ARM_ID_AA64DFR0_EL1:
+		case KVM_REG_ARM64_SVE_VLS:
+			return true;
+		}
+	}
+
+	return false;
+}
+
 int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	/* We currently use nothing arch-specific in upper 32 bits */
 	if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
 		return -EINVAL;
 
+	if (kvm_vm_is_realm(vcpu->kvm) && !validate_realm_set_reg(vcpu, reg))
+		return -EINVAL;
+
 	switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
 	case KVM_REG_ARM_CORE:	return set_core_reg(vcpu, reg);
 	case KVM_REG_ARM_FW:
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index db37678dcb05c..384c5d258c7f8 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -490,7 +490,7 @@ static void handle_exit_pkvm_state(struct kvm_vcpu *vcpu, int exception_index)
 {
 	int exception_code = ARM_EXCEPTION_CODE(exception_index);
 
-	if (!is_protected_kvm_enabled() || kvm_vm_is_protected(vcpu->kvm))
+	if (!kvm_vm_is_unprotected_pkvm(vcpu->kvm))
 		return;
 
 	/*
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 459bd9eb7e4bc..9b69228f8402c 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);
 
@@ -528,19 +528,19 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
 	hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
 	hyp_vcpu->vcpu.arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;
 
-	if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
-		/*
-		 * Timer offsets are pointing to the untrusted KVM copy,
-		 * which is pinned in __pkvm_init_vm() for the VM life time.
-		 * It is worth noting that hyp_vm->host_kvm points to an EL2
-		 * linear map address and timer_get_offset() will use
-		 * kern_hyp_va() which is safe as it is idempotent.
-		 */
-		vcpu_vtimer(&hyp_vcpu->vcpu)->offset.vm_offset =
-			&hyp_vm->host_kvm->arch.timer_data.voffset;
-		vcpu_ptimer(&hyp_vcpu->vcpu)->offset.vm_offset =
-			&hyp_vm->host_kvm->arch.timer_data.poffset;
-	}
+	/*
+	 * Timer offsets are pointing to the untrusted KVM copy,
+	 * which is pinned in __pkvm_init_vm() for the VM life time.
+	 * It is worth noting that hyp_vm->host_kvm points to an EL2
+	 * linear map address and timer_get_offset() will use
+	 * kern_hyp_va() which is safe as it is idempotent.
+	 * Also for protected VMs the offset is fixed to 0 and is prevented
+	 * from changing.
+	 */
+	vcpu_vtimer(&hyp_vcpu->vcpu)->offset.vm_offset =
+		&hyp_vm->host_kvm->arch.timer_data.voffset;
+	vcpu_ptimer(&hyp_vcpu->vcpu)->offset.vm_offset =
+		&hyp_vm->host_kvm->arch.timer_data.poffset;
 
 	ret = pkvm_vcpu_init_sysregs(hyp_vcpu);
 	if (ret)
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/mmu.c b/arch/arm64/kvm/mmu.c
index 9ba86450fe4af..cc264726a6df8 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -37,6 +37,8 @@ static unsigned long __ro_after_init io_map_base;
 
 #define KVM_PGT_FN(fn)		(!is_protected_kvm_enabled() ? fn : p ## fn)
 
+static int kvm_vm_init_vm_s2_ops(struct kvm *kvm);
+
 static phys_addr_t __stage2_range_addr_end(phys_addr_t addr, phys_addr_t end,
 					   phys_addr_t size)
 {
@@ -166,6 +168,24 @@ 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;
+}
+
+static int realm_vm_flush_remote_tlbs(struct kvm *kvm)
+{
+	/* Nothing to do here, RMM takes care of this */
+	return 0;
+}
+
 /**
  * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8
  * @kvm:	pointer to kvm structure.
@@ -174,26 +194,43 @@ 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 1;
+	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;
 }
 
+static int realm_vm_flush_remote_tlbs_range(struct kvm *kvm,
+					   gfn_t gfn, u64 nr_pages)
+{
+	/* Nothing to do here, RMM takes care of this */
+	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 1;
+
+	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 +374,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)
@@ -983,6 +1027,12 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
 	int cpu, err;
 	struct kvm_pgtable *pgt;
 
+	/* Initialize the VM ops for the VM instance for the first time */
+	if (mmu == &kvm->arch.mmu) {
+		err = kvm_vm_init_vm_s2_ops(kvm);
+		if (err)
+			return err;
+	}
 	/*
 	 * If we already have our page tables in place, and that the
 	 * MMU context is the canonical one, we have a bug somewhere,
@@ -1214,7 +1264,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
 				     KVM_PGTABLE_PROT_R |
 				     (writable ? KVM_PGTABLE_PROT_W : 0);
 
-	if (is_protected_kvm_enabled())
+	if (kvm_vm_hyp_is_distrusting(kvm))
 		return -EPERM;
 
 	size += offset_in_page(guest_ipa);
@@ -1705,7 +1755,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 +2193,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 +2316,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 +2478,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,45 +2491,57 @@ 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))
-		return false;
 
 	return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
 						   range->start << PAGE_SHIFT,
 						   size, false);
 }
 
+bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+
+	if (!kvm->arch.mmu.pgt || !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);
@@ -2624,7 +2691,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
 	hva_t hva, reg_end;
 	int ret = 0;
 
-	if (kvm_vm_is_protected(kvm)) {
+	if (kvm_vm_is_protected_pkvm(kvm)) {
 		/* Cannot modify memslots once a pVM has run. */
 		if (pkvm_hyp_vm_is_created(kvm) &&
 		    (change == KVM_MR_DELETE || change == KVM_MR_MOVE)) {
@@ -2796,3 +2863,64 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)
 
 	trace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);
 }
+
+static const struct kvm_vm_s2_ops protected_pkvm_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= pkvm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= pkvm_flush_remote_tlbs_range,
+	/*
+	 * 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,
+};
+
+static const struct kvm_vm_s2_ops realm_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= realm_vm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= realm_vm_flush_remote_tlbs_range,
+	.vm_mem_abort			= kvm_vm_mem_abort,
+	/*
+	 * Not supported for Realms
+	 *	.vm_age_gfn			= realm_vm_age_gfn,
+	 *	.vm_test_age_gfn		= realm_vm_test_age_gfn,
+	 */
+};
+
+#define KVM_VM_S2_OPS(flavor, ops)		\
+		[flavor] = ops
+static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {
+	KVM_VM_S2_OPS(VM_VHE, &kvm_default_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_NVHE, &kvm_default_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_PKVM, &pkvm_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_PROTECTED_PKVM, &protected_pkvm_vm_s2_ops),
+	KVM_VM_S2_OPS(VM_REALM, &realm_vm_s2_ops),
+};
+
+static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)
+{
+	BUILD_BUG_ON(ARRAY_SIZE(arm64_vm_s2_ops) != VM_FLAVOR_MAX);
+
+	kvm->arch.vm_s2_ops = arm64_vm_s2_ops[kvm->arch.vm_flavor];
+	if (WARN_ON(!kvm->arch.vm_s2_ops))
+		return -EINVAL;
+	return 0;
+}
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 8e4c6e4bec123..8e9176a700926 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -229,10 +229,9 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm)
 	mutex_unlock(&kvm->arch.config_lock);
 }
 
-int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)
+int pkvm_init_host_vm(struct kvm *kvm)
 {
 	int ret;
-	bool protected = type & KVM_VM_TYPE_ARM_PROTECTED;
 
 	/* Reserve the VM in hyp and obtain a hyp handle for the VM. */
 	ret = kvm_call_hyp_nvhe(__pkvm_reserve_vm);
@@ -240,8 +239,7 @@ int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)
 		return ret;
 
 	kvm->arch.pkvm.handle = ret;
-	kvm->arch.pkvm.is_protected = protected;
-	if (protected) {
+	if (kvm_vm_is_protected(kvm)) {
 		pr_warn_once("kvm: protected VMs are experimental and for development only, tainting kernel\n");
 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
 	}
diff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c
index 4ceabaa4c30bd..579e0a4720ad2 100644
--- a/arch/arm64/kvm/pvtime.c
+++ b/arch/arm64/kvm/pvtime.c
@@ -67,9 +67,9 @@ gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)
 	return base;
 }
 
-bool kvm_arm_pvtime_supported(void)
+bool kvm_arm_pvtime_supported(struct kvm *kvm)
 {
-	return !!sched_info_on();
+	return !!sched_info_on() && (!kvm || !kvm_vm_is_protected(kvm));
 }
 
 int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
@@ -81,8 +81,8 @@ int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
 	int ret = 0;
 	int idx;
 
-	if (!kvm_arm_pvtime_supported() ||
-	    attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
+	if (!kvm_arm_pvtime_supported(kvm) ||
+	    (attr->attr != KVM_ARM_VCPU_PVTIME_IPA))
 		return -ENXIO;
 
 	if (get_user(ipa, user))
@@ -110,8 +110,8 @@ int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
 	u64 __user *user = (u64 __user *)attr->addr;
 	u64 ipa;
 
-	if (!kvm_arm_pvtime_supported() ||
-	    attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
+	if (!kvm_arm_pvtime_supported(vcpu->kvm) ||
+	    (attr->attr != KVM_ARM_VCPU_PVTIME_IPA))
 		return -ENXIO;
 
 	ipa = vcpu->arch.steal.base;
@@ -126,7 +126,7 @@ int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
 {
 	switch (attr->attr) {
 	case KVM_ARM_VCPU_PVTIME_IPA:
-		if (kvm_arm_pvtime_supported())
+		if (kvm_arm_pvtime_supported(vcpu->kvm))
 			return 0;
 	}
 	return -ENXIO;
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..520cec19f49fa 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,28 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
 	return true;
 }
 
+static inline bool kvm_realm_sys_reg_hidden_user(const struct kvm_vcpu *vcpu,
+						 u64 reg)
+{
+	if (!vcpu_is_rec(vcpu))
+		return false;
+
+	switch (reg) {
+	case SYS_ID_AA64DFR0_EL1:
+	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 (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 +5752,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 +5765,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/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 4012df6002ea6..452c085073f3e 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -84,6 +84,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
 		!kvm_vgic_global_state.can_emulate_gicv2)
 		return -ENODEV;
 
+	/* Realms only support VGIC_V3 */
+	if (kvm_vm_is_realm(kvm) && type != KVM_DEV_TYPE_ARM_VGIC_V3)
+		return -ENODEV;
 	/*
 	 * Ensure mutual exclusion with vCPU creation and any vCPU ioctls by:
 	 *
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index bc6f2fdd7ad33..4f0aa3bb69f45 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -176,8 +176,7 @@ static inline bool has_cntpoff(void)
 		if (__ctxt) {						\
 			struct arch_timer_offset *ato = &__ctxt->offset;\
 									\
-			if (ato->vm_offset)				\
-				off += *KERN_HYP_VA(ato->vm_offset);	\
+			off += *KERN_HYP_VA(ato->vm_offset);		\
 			if (ato->vcpu_offset)				\
 				off += *KERN_HYP_VA(ato->vcpu_offset);	\
 		}							\
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/15 23:03 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27926
							output: 27
							thoughts: 548
						

Thoughts:
**Analyzing This Patch Series**

Okay, so the user wants me to dive into this patch series, targeting the `amd64` architecture. Right off the bat, I see something that's making me pause. The target architecture is *amd64*, but the listed modified files are almost *exclusively* in the `arch/arm64/` directory, along with some `include/kvm/arm_*.h` headers. That's a red flag. It's almost certain this code doesn't apply to the target architecture.

Let's quickly run down the files, though. We have some documentation, `Documentation/admin-guide/kernel-parameters.txt`, which is a common inclusion. Then it's a deep dive into the ARM64 KVM internals: headers like `kvm_emulate.h`, `kvm_host.h`, `kvm_pgtable.h`, and `kvm_pkvm.h`; `kvm/Makefile`, core KVM files like `arch_timer.c`, `arm.c`, `guest.c`, `handle_exit.c`, `hyp/nvhe/pkvm.c`, `hyp/pgtable.c`, `hypercalls.c`, `inject_fault.c`, `mmu.c`, `pkvm.c`, `pvtime.c`, `rmi.c`, `sys_regs.c`, `vgic/vgic-init.c`. Then, crucially, the `include/kvm/` directory with `arm_arch_timer.h` and `arm_psci.h`.

The alarm bells are getting louder. This *screams* ARM64-specific code. There isn't a single file that looks like it could affect x86 architecture.

I need to confirm the obvious, are these headers actually compiled on `amd64`? We need to use `grepper` to find out. There's almost certainly a misunderstanding or a mistake here. This patch series is probably *not* relevant to the target architecture.



4/2 2026/09/15 23:03 tool grepper
0m Args:
{
  "Expression": "include \u003ckvm/arm_arch_timer.h\u003e"
}

Results:
{
  "Output": "arch/arm64/include/asm/kvm_host.h-37-#include \u003ckvm/arm_vgic.h\u003e\narch/arm64/include/asm/kvm_host.h:38:#include \u003ckvm/arm_arch_timer.h\u003e\narch/arm64/include/asm/kvm_host.h-39-#include \u003ckvm/arm_pmu.h\u003e\n--\narch/arm64/kvm/arch_timer.c-21-#include \u003ckvm/arm_vgic.h\u003e\narch/arm64/kvm/arch_timer.c:22:#include \u003ckvm/arm_arch_timer.h\u003e\narch/arm64/kvm/arch_timer.c-23-\n--\narch/arm64/kvm/reset.c-19-\narch/arm64/kvm/reset.c:20:#include \u003ckvm/arm_arch_timer.h\u003e\narch/arm64/kvm/reset.c-21-\n--\narch/arm64/kvm/trace_arm.h-5-#include \u003casm/kvm_emulate.h\u003e\narch/arm64/kvm/trace_arm.h:6:#include \u003ckvm/arm_arch_timer.h\u003e\narch/arm64/kvm/trace_arm.h-7-#include \u003clinux/tracepoint.h\u003e\n--\narch/arm64/kvm/vgic/vgic-mmio.c-12-#include \u003ckvm/iodev.h\u003e\narch/arm64/kvm/vgic/vgic-mmio.c:13:#include \u003ckvm/arm_arch_timer.h\u003e\narch/arm64/kvm/vgic/vgic-mmio.c-14-#include \u003ckvm/arm_vgic.h\u003e\n"
}

5/2 2026/09/15 23:03 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29410
							output: 26
							thoughts: 209
						

Thoughts:
**WorthFuzzing=false: Negative Criteria Review**

Okay, let's break this down. We're looking at reasons why a change *wouldn't* be worth fuzzing, specifically when `WorthFuzzing` is set to false. Right, the notes outline some negative criteria: non-kernel and non-executable changes, structurally unreachable hardware, and unreachable execution paths. The unreachable paths are interesting. The document points out driver teardown callbacks are only run during hot-unplug or manual driver unbinding, which seems pretty specific. Then there is this one: "Code paths exclusive to architectures other than the target architecture".

Aha! Now we know the target architecture is amd64. Let's see... looking at the commit, it appears all the code modifications are exclusively ARM64. We've got changes in `arch/arm64/...`, that's an immediate red flag. The headers `include/kvm/arm_arch_timer.h` and `include/kvm/arm_psci.h` are likely only included in ARM64 code. The kernel-parameters file in Documentation, that's not executable, no worries there. This firmly falls under the "architecture exclusive" criteria. So, based on this criteria, no fuzzing is necessary here. The ARM64 specific code changes aren't applicable to the amd64 architecture, so we mark it as "not worth fuzzing".



6/2 2026/09/15 23:03 tool grepper
0m Args:
{
  "Expression": "include \u003ckvm/arm_psci.h\u003e"
}

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"
}

7/2 2026/09/15 23:03 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30452
							output: 99
							thoughts: 318
						

Thoughts:
**My Analysis of This Patch for Fuzzing Worthiness**

Okay, so I've just gone through this patch and I'm pretty confident in my assessment. Looking at the file inclusions, `include/kvm/arm_psci.h` and `include/kvm/arm_arch_timer.h` are *only* included by the `arch/arm64` directory, and that's the only architecture that contains any of the modified code. The other changes are nestled within `arch/arm64/` and `Documentation/`. This is all pretty straightforward; the amd64 architecture, my target, doesn't even touch any of this. It's simply not compiled or executed there.

Given the negative criteria – specifically, "Code paths exclusive to architectures other than the target architecture" – this looks like a clear case of `WorthFuzzing: false`.

Now, let's just make sure I've got all my ducks in a row and that I'm doing things right. I need to use the `set-results` tool, and I need to be sure that it's called just once, before the final reply. The tool has these properties: `EnableConfigs` and `FocusSymbols` can be null or empty lists, `Reasoning` must be a concise explanation, and, of course, the key: `WorthFuzzing` which will be `false`.

Just to reiterate the core reasoning for my own sanity: my target architecture is amd64, aka x86_64. *All* the code modified by this patch series is exclusively related to ARM64 KVM and Arm CCA (Confidential Compute Architecture / Realm Management Interface), and everything is in the `arch/arm64/`, `include/kvm/arm_*`, and `Documentation/` directories. This code is *structurally* unreachable on amd64. Therefore, it's not worth fuzzing.

Alright, I'm confident in my conclusion. Time to call `set-results`.



8/2 2026/09/15 23:03 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies ARM64 KVM and Arm Confidential Compute Architecture (CCA / RMM / Realm) support located under arch/arm64/ and include/kvm/arm_*. The target architecture is amd64, so these changes are exclusive to arm64 and are not compiled or reachable on amd64.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies ARM64 KVM and Arm Confidential Compute Architecture (CCA / RMM / Realm) support located under arch/arm64/ and include/kvm/arm_*. The target architecture is amd64, so these changes are exclusive to arm64 and are not compiled or reachable on amd64.",
  "WorthFuzzing": false
}

9/1 2026/09/15 23:03 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)