AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/11/03 03:50 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "All changes in this patch series are confined strictly to the ARM64 architecture (under arch/arm64/), adding support for Arm CCA / RMM (Realm Management Interface) to KVM. As the target architecture is amd64, the modified code is exclusive to ARM64 and cannot be compiled or executed on amd64.",
  "WorthFuzzing": false
}

1/1 2026/11/03 03:50 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit ed0dd90af477374dd53c089f8f6afb0a5022104d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Nov 3 03:50:43 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h\nindex 334211b8b2ee49..8a71fddfdb8502 100644\n--- a/arch/arm64/include/asm/kvm_emulate.h\n+++ b/arch/arm64/include/asm/kvm_emulate.h\n@@ -831,4 +831,33 @@ 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+static inline gpa_t kvm_gpa_from_fault(struct kvm *kvm, phys_addr_t ipa)\n+{\n+\tif (!kvm_vm_is_realm(kvm))\n+\t\treturn ipa;\n+\n+\treturn ipa \u0026 ~BIT(kvm-\u003earch.realm.ia_bits - 1);\n+}\n+\n+static inline gfn_t kvm_gfn_from_fault(struct kvm *kvm, phys_addr_t ipa)\n+{\n+\treturn gpa_to_gfn(kvm_gpa_from_fault(kvm, ipa));\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 97332ffcb1300e..41e15230263d86 100644\n--- a/arch/arm64/include/asm/kvm_host.h\n+++ b/arch/arm64/include/asm/kvm_host.h\n@@ -29,6 +29,7 @@\n #include \u003casm/kvm.h\u003e\n #include \u003casm/kvm_asm.h\u003e\n #include \u003casm/kvm_hcall.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@@ -48,6 +49,13 @@\n #define KVM_VCPU_MAX_FEATURES 10\n #define KVM_VCPU_VALID_FEATURES\t(BIT(KVM_VCPU_MAX_FEATURES) - 1)\n \n+#define KVM_REALM_VCPU_VALID_FEATURES\t\t\t\\\n+\t(BIT(KVM_ARM_VCPU_POWER_OFF)\t\t|\t\\\n+\t BIT(KVM_ARM_VCPU_PSCI_0_2)\t\t|\t\\\n+\t BIT(KVM_ARM_VCPU_PTRAUTH_ADDRESS)\t|\t\\\n+\t BIT(KVM_ARM_VCPU_SVE)\t\t\t|\t\\\n+\t BIT(KVM_ARM_VCPU_PTRAUTH_GENERIC))\n+\n #define KVM_REQ_SLEEP \\\n \tKVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)\n #define KVM_REQ_IRQ_PENDING\t\tKVM_ARCH_REQ(1)\n@@ -120,6 +128,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@@ -232,6 +241,7 @@ struct kvm_vcpu_ops {\n };\n \n struct kvm_gfn_range;\n+enum kvm_gfn_range_filter;\n struct kvm_s2_fault_desc;\n \n struct kvm_vm_s2_ops {\n@@ -242,7 +252,8 @@ struct kvm_vm_s2_ops {\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+\t\t\t\t      bool may_block,\n+\t\t\t\t      enum kvm_gfn_range_filter filter);\n \tint (*vm_mem_abort)(const struct kvm_s2_fault_desc *s2fd);\n };\n \n@@ -408,6 +419,7 @@ enum kvm_arm_vm_flavor {\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@@ -501,11 +513,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@@ -1571,30 +1586,43 @@ struct kvm *kvm_arch_alloc_vm(void);\n \n #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE\n \n-#ifdef __KVM_NVHE_HYPERVISOR__\n+#define kvm_vm_is_protected(kvm)\t((kvm)-\u003earch.vm_flavor \u003e= __VM_PROTECTED)\n \n-#define kvm_vm_is_protected(kvm)\t\t\t\\\n-\t(is_protected_kvm_enabled() \u0026\u0026 ((kvm)-\u003earch.vm_flavor == VM_PROTECTED_PKVM))\n+#ifdef __KVM_NVHE_HYPERVISOR__\n /*\n  * Accessing vcpu-\u003ekvm from nVHE hyp stub is tricky, as we need to convert the\n  * pointer to the hyp VA. With pKVM, the nVHE code runs with the hyp_vcpu,\n- * which is populated correctly and is gated on is_protected_kvm_enabled().\n+ * which is populated correctly. Always vcpu_is_protected_pkvm(), which is\n+ * gated on is_protected_kvm_enabled().\n+ */\n+#define vcpu_is_protected(vcpu)\t\tBUILD_BUG_ON(1)\n+#else\n+#define vcpu_is_protected(vcpu)\t\tkvm_vm_is_protected((vcpu)-\u003ekvm)\n+#endif\n+\n+#define kvm_vm_is_protected_pkvm(kvm)\t\t\t\t\t\\\n+\t(is_protected_kvm_enabled() \u0026\u0026 ((kvm)-\u003earch.vm_flavor == VM_PROTECTED_PKVM))\n+\n+/*\n+ * Rely on is_protected_kvm_enabled() check in kvm_vm_is_protected_pkvm() to\n+ * make sure the vcpu-\u003ekvm is always valid VA in the context\n  */\n-#define vcpu_is_protected(vcpu)\t\t\t\t\t\t\\\n+#define vcpu_is_protected_pkvm(vcpu)\t\t\t\t\t\\\n \t({\t\t\t\t\t\t\t\t\\\n \t\tstruct kvm *__kvm = READ_ONCE((vcpu)-\u003ekvm);\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n-\t\t(__kvm \u0026\u0026 kvm_vm_is_protected(__kvm));\t\t\t\\\n+\t\t(__kvm \u0026\u0026 kvm_vm_is_protected_pkvm(__kvm));\t\t\\\n \t})\n \n-#else\n-\n-#define kvm_vm_is_protected(kvm)\t((kvm)-\u003earch.vm_flavor \u003e= __VM_PROTECTED)\n-#define vcpu_is_protected(vcpu)\t\tkvm_vm_is_protected((vcpu)-\u003ekvm)\n+#define kvm_vm_is_realm(kvm)\t\t((kvm)-\u003earch.vm_flavor == VM_REALM)\n+#define vcpu_is_rec(vcpu)\t\tkvm_vm_is_realm((vcpu)-\u003ekvm)\n \n #define kvm_vm_hyp_is_distrusting(kvm)\t((kvm)-\u003earch.vm_flavor \u003e= __VM_DISTRUSTING_HYP)\n \n-#endif\t/* __KVM_NVHE_HYPERVISOR__ */\n+#define kvm_arch_has_private_mem(kvm) kvm_vm_is_realm(kvm)\n+\n+int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);\n+bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);\n \n #define kvm_has_mte(kvm)\t\t\t\t\t\\\n \t(system_supports_mte() \u0026\u0026\t\t\t\t\\\ndiff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h\nnew file mode 100644\nindex 00000000000000..105660d4d56a50\n--- /dev/null\n+++ b/arch/arm64/include/asm/kvm_rmi.h\n@@ -0,0 +1,109 @@\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+#include \u003casm/kvm_pgtable.h\u003e\n+#include \u003clinux/arm-smccc-rmi.h\u003e\n+#include \u003clinux/kvm.h\u003e\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, Realm Descriptor has been created,\n+\t *      but it is not yet activated. Memory 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(). Memory may no longer be populated with known\n+\t *\tcontents.\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+ * @rd: Kernel mapping of the RMM-managed Realm Descriptor (RD) granule\n+ * @sro: Preallocated SRO state context for Realm MMU operations\n+ * @state: The lifetime state machine for the realm\n+ * @ia_bits: Number of valid Input Address bits in the IPA\n+ * @stage2_unmapped: The Realm stage-2 mappings have been removed\n+ */\n+struct realm {\n+\tvoid *rd;\n+\t/*\n+\t * Reused by RTT map/unmap SRO commands. Those commands are only\n+\t * issued from Realm stage-2 map/unmap paths while kvm-\u003emmu_lock is\n+\t * held for write, including Realm fault handling where\n+\t * kvm_fault_lock() takes the write side, so concurrent use is\n+\t * serialized.\n+\t */\n+\tstruct rmi_sro_state *sro;\n+\tenum realm_state state;\n+\tunsigned int ia_bits;\n+\tbool stage2_unmapped;\n+};\n+\n+void kvm_init_rmi(void);\n+u32 kvm_rmm_ipa_limit(void);\n+\n+int kvm_init_realm(struct kvm *kvm);\n+void kvm_destroy_realm(struct kvm *kvm);\n+int kvm_realm_teardown_stage2(struct kvm *kvm);\n+\n+enum kvm_gfn_range_filter;\n+\n+void kvm_realm_unmap_range_filter(struct kvm *kvm,\n+\t\t\t\t  unsigned long ipa,\n+\t\t\t\t  unsigned long size,\n+\t\t\t\t  bool may_block,\n+\t\t\t\t  enum kvm_gfn_range_filter attr_filter);\n+\n+void kvm_realm_unmap_range(struct kvm *kvm,\n+\t\t\t   unsigned long ipa,\n+\t\t\t   unsigned long size,\n+\t\t\t   bool may_block);\n+\n+int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,\n+\t\t  kvm_pfn_t pfn, unsigned long map_size,\n+\t\t  enum kvm_pgtable_prot prot,\n+\t\t  struct kvm_mmu_memory_cache *memcache);\n+\n+static inline bool kvm_realm_is_private_address(struct realm *realm,\n+\t\t\t\t\t\tunsigned long addr)\n+{\n+\treturn !(addr \u0026 BIT(realm-\u003eia_bits - 1));\n+}\n+\n+#endif /* __ASM_KVM_RMI_H */\ndiff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h\nindex b546703c3ab9a8..92cec42952f42c 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/Kconfig b/arch/arm64/kvm/Kconfig\nindex cecea0820e523c..d067063a36868c 100644\n--- a/arch/arm64/kvm/Kconfig\n+++ b/arch/arm64/kvm/Kconfig\n@@ -34,6 +34,7 @@ menuconfig KVM\n \tselect GUEST_PERF_EVENTS if PERF_EVENTS\n \tselect KVM_GUEST_MEMFD\n \tselect KVM_GENERIC_PRE_FAULT_MEMORY\n+\tselect ARM_RMM_RMI\n \thelp\n \t  Support hosting virtualized guest machines.\n \ndiff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile\nindex 7aaeeb84e788ec..66a719ab5b6b35 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/arm.c b/arch/arm64/kvm/arm.c\nindex 157bf4b8120201..9a7bbfaf9675c3 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@@ -112,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@@ -300,6 +303,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n \n \tbitmap_zero(kvm-\u003earch.vcpu_features, KVM_VCPU_MAX_FEATURES);\n \n+\t/* Initialise the realm bits after the generic bits are enabled */\n+\tif (kvm_vm_is_realm(kvm)) {\n+\t\tret = kvm_init_realm(kvm);\n+\t\tif (ret)\n+\t\t\tgoto err_uninit_mmu;\n+\t}\n+\n \treturn 0;\n \n err_uninit_mmu:\n@@ -364,6 +374,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)\n \n \tkvm_destroy_nested(kvm);\n \tkvm_arm_teardown_hypercalls(kvm);\n+\tif (kvm_vm_is_realm(kvm))\n+\t\tkvm_destroy_realm(kvm);\n }\n \n static bool kvm_has_full_ptr_auth(void)\n@@ -1732,10 +1744,19 @@ static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)\n {\n \tunsigned long features = KVM_VCPU_VALID_FEATURES;\n \n-\tif (vcpu-\u003ekvm-\u003earch.vm_flavor == VM_PROTECTED_PKVM)\n+\tswitch (vcpu-\u003ekvm-\u003earch.vm_flavor) {\n+\tcase VM_PROTECTED_PKVM:\n \t\tkvm_pkvm_vcpu_allowed_features(vcpu-\u003ekvm, \u0026features);\n+\t\tbreak;\n+\tcase VM_REALM:\n+\t\tfeatures = KVM_REALM_VCPU_VALID_FEATURES;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n \n-\tif (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))\n+\tif (vcpu_is_protected(vcpu) ||\n+\t    !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))\n \t\tclear_bit(KVM_ARM_VCPU_EL1_32BIT, \u0026features);\n \n \tif (!kvm_supports_guest_pmuv3()) {\n@@ -1751,7 +1772,8 @@ static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)\n \t\tclear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, \u0026features);\n \t}\n \n-\tif (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))\n+\tif (vcpu_is_protected(vcpu) ||\n+\t    !cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))\n \t\tclear_bit(KVM_ARM_VCPU_HAS_EL2, \u0026features);\n \n \treturn features;\n@@ -1775,7 +1797,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,\n \tif (features \u0026 ~system_supported_vcpu_features(vcpu))\n \t\treturn -EINVAL;\n \n-\t/* EL2 implements PSCI 1.1; the host must not dispatch as 0.1. */\n+\t/* Both pKVM and RMM implements PSCI 1.1; the host must not dispatch as 0.1. */\n \tif (vcpu_is_protected(vcpu) \u0026\u0026\n \t    !test_bit(KVM_ARM_VCPU_PSCI_0_2, \u0026features))\n \t\treturn -EINVAL;\n@@ -2312,6 +2334,7 @@ static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {\n \tKVM_VCPU_OPS(VM_VHE, vhe_vcpu_ops),\n \tKVM_VCPU_OPS(VM_PKVM, pkvm_vcpu_ops),\n \tKVM_VCPU_OPS(VM_PROTECTED_PKVM, pkvm_vcpu_ops),\n+\t[VM_REALM] = NULL,\n };\n \n static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)\n@@ -3269,6 +3292,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\" \\\ndiff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h\nindex 0261b2bc681f77..ae6ced6d7b9ea7 100644\n--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h\n+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h\n@@ -84,7 +84,7 @@ pkvm_hyp_vcpu_to_hyp_vm(struct pkvm_hyp_vcpu *hyp_vcpu)\n \n static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu)\n {\n-\treturn vcpu_is_protected(\u0026hyp_vcpu-\u003evcpu);\n+\treturn vcpu_is_protected_pkvm(\u0026hyp_vcpu-\u003evcpu);\n }\n \n static inline bool pkvm_hyp_vm_is_protected(struct pkvm_hyp_vm *hyp_vm)\ndiff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c\nindex 89fb9dd351ad5d..6c03f48e6be344 100644\n--- a/arch/arm64/kvm/hyp/nvhe/switch.c\n+++ b/arch/arm64/kvm/hyp/nvhe/switch.c\n@@ -238,7 +238,7 @@ static const exit_handler_fn pvm_exit_handlers[] = {\n \n static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)\n {\n-\tif (unlikely(vcpu_is_protected(vcpu)))\n+\tif (unlikely(vcpu_is_protected_pkvm(vcpu)))\n \t\treturn pvm_exit_handlers;\n \n \treturn hyp_exit_handlers;\n@@ -259,7 +259,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)\n \t * it.  The check below is based on the one in\n \t * kvm_arch_vcpu_ioctl_run().\n \t */\n-\tif (unlikely(vcpu_is_protected(vcpu) \u0026\u0026 vcpu_mode_is_32bit(vcpu))) {\n+\tif (unlikely(vcpu_is_protected_pkvm(vcpu) \u0026\u0026 vcpu_mode_is_32bit(vcpu))) {\n \t\t/*\n \t\t * As we have caught the guest red-handed, decide that it isn't\n \t\t * fit for purpose anymore by making the vcpu invalid. The VMM\ndiff --git a/arch/arm64/kvm/hyp/nvhe/timer-sr.c b/arch/arm64/kvm/hyp/nvhe/timer-sr.c\nindex 99306571691331..17bd1ff621ab69 100644\n--- a/arch/arm64/kvm/hyp/nvhe/timer-sr.c\n+++ b/arch/arm64/kvm/hyp/nvhe/timer-sr.c\n@@ -48,7 +48,7 @@ void __timer_enable_traps(struct kvm_vcpu *vcpu)\n \t * or running a protected VM (we don't offset anything in this case).\n \t */\n \tclr = CNTHCTL_EL1PCEN;\n-\tif (vcpu_is_protected(vcpu) ||\n+\tif (vcpu_is_protected_pkvm(vcpu) ||\n \t    !timer_get_offset(vcpu_ptimer(vcpu)))\n \t\tset |= CNTHCTL_EL1PCTEN;\n \telse\ndiff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c\nindex 1db10d8d6db737..bb33c07569e3be 100644\n--- a/arch/arm64/kvm/mmu.c\n+++ b/arch/arm64/kvm/mmu.c\n@@ -191,6 +191,12 @@ static int kvm_vm_flush_remote_tlbs(struct kvm *kvm)\n \treturn 0;\n }\n \n+static int realm_flush_remote_tlbs(struct kvm *kvm)\n+{\n+\t/* Nothing to do here, RMM does the job */\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@@ -218,6 +224,13 @@ static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,\n \treturn 0;\n }\n \n+static int realm_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 does the job */\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@@ -324,7 +337,8 @@ static int kvm_pgtable_stage2_unmap_tracked(struct kvm_pgtable *pgt,\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+\t\t\t\t      u64 size, bool may_block,\n+\t\t\t\t      enum kvm_gfn_range_filter filter)\n {\n \tstruct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);\n \tint (*fn)(struct kvm_pgtable *, u64, u64);\n@@ -339,15 +353,26 @@ static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,\n \n static void pkvm_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+\t\t\t\t    u64 size, bool may_block,\n+\t\t\t\t    enum kvm_gfn_range_filter filter)\n {\n \tWARN_ON(stage2_apply_range(mmu, start, start + size,\n \t\t\t\t   pkvm_pgtable_stage2_unmap, may_block));\n }\n \n static void no_stage2_unmap_range(struct kvm_s2_mmu *mmu,\n-\t\t\t\t  phys_addr_t start, u64 size, bool may_block)\n+\t\t\t\t  phys_addr_t start, u64 size, bool may_block,\n+\t\t\t\t  enum kvm_gfn_range_filter filter)\n+{\n+}\n+\n+static void realm_stage2_unmap_range(struct kvm_s2_mmu *mmu,\n+\t\t\t\t     phys_addr_t start, u64 size, bool may_block,\n+\t\t\t\t     enum kvm_gfn_range_filter filter)\n {\n+\tstruct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);\n+\n+\tkvm_realm_unmap_range_filter(kvm, start, size, may_block, filter);\n }\n \n /*\n@@ -388,25 +413,36 @@ static int kvm_pgtable_stage2_unmap_tracked(struct kvm_pgtable *pgt, u64 addr, u\n }\n \n /**\n- * kvm_stage2_unmap_range -- Clear stage2 page table entries to unmap a range\n+ * kvm_stage2_unmap_range_filter -- Clear stage2 page table entries to unmap a\n+ * range\n  * @mmu:   The KVM stage-2 MMU pointer\n  * @start: The intermediate physical base address of the range to unmap\n  * @size:  The size of the area to unmap\n  * @may_block: Whether or not we are permitted to block\n+ * @filter: Filter for the ipa range (private and/or shared)\n  *\n  * Clear a range of stage-2 mappings, lowering the various ref-counts.  Must\n  * be called while holding mmu_lock otherwise another faulting VCPU may\n  * come in and mess with things behind our backs.\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+static void kvm_stage2_unmap_range_filter(struct kvm_s2_mmu *mmu, phys_addr_t start,\n+\t\t\t\t\t  u64 size, bool may_block,\n+\t\t\t\t\t  enum kvm_gfn_range_filter filter)\n {\n \tstruct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);\n \n \tlockdep_assert_held_write(\u0026kvm-\u003emmu_lock);\n \tWARN_ON(size \u0026 ~PAGE_MASK);\n \n-\tkvm-\u003earch.vm_s2_ops-\u003evm_stage2_unmap_range(mmu, start, size, may_block);\n+\tkvm-\u003earch.vm_s2_ops-\u003evm_stage2_unmap_range(mmu, start, size,\n+\t\t\t\t\t\t   may_block, filter);\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+\tkvm_stage2_unmap_range_filter(mmu, start, size, may_block,\n+\t\t\t\t      KVM_FILTER_PRIVATE | KVM_FILTER_SHARED);\n }\n \n void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)\n@@ -420,7 +456,11 @@ static void stage2_flush_memslot(struct kvm *kvm,\n \tphys_addr_t addr = memslot-\u003ebase_gfn \u003c\u003c PAGE_SHIFT;\n \tphys_addr_t end = addr + PAGE_SIZE * memslot-\u003enpages;\n \n-\tkvm_stage2_flush_range(\u0026kvm-\u003earch.mmu, addr, end);\n+\tif (kvm_vm_is_realm(kvm))\n+\t\tkvm_realm_unmap_range_filter(kvm, addr, end - addr, true,\n+\t\t\t\t\t     KVM_FILTER_SHARED);\n+\telse\n+\t\tkvm_stage2_flush_range(\u0026kvm-\u003earch.mmu, addr, end);\n }\n \n /**\n@@ -967,10 +1007,14 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {\n \n static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)\n {\n+\tstruct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);\n \tu32 kvm_ipa_limit = get_kvm_ipa_limit();\n \tu64 mmfr0, mmfr1;\n \tu32 phys_shift;\n \n+\tif (kvm_vm_is_realm(kvm))\n+\t\tkvm_ipa_limit = kvm_rmm_ipa_limit();\n+\n \tphys_shift = KVM_VM_TYPE_ARM_IPA_SIZE(type);\n \tif (is_protected_kvm_enabled()) {\n \t\tphys_shift = kvm_ipa_limit;\n@@ -1020,9 +1064,18 @@ static void stage2_destroy_range(struct kvm_pgtable *pgt, phys_addr_t addr,\n \n static void kvm_stage2_destroy(struct kvm_pgtable *pgt)\n {\n+\tstruct kvm *kvm = kvm_s2_mmu_to_kvm(pgt-\u003emmu);\n \tunsigned int ia_bits = VTCR_EL2_IPA(pgt-\u003emmu-\u003evtcr);\n \n-\tstage2_destroy_range(pgt, 0, BIT(ia_bits));\n+\t/*\n+\t * Realm RTTs are inaccessible to the host and may contain stale data\n+\t * after the RMM has released them. The non-root RTTs are explicitly\n+\t * destroyed through RMI before the RD is destroyed; only the root PGD\n+\t * pages remain to be freed here.\n+\t */\n+\tif (!kvm_vm_is_realm(kvm))\n+\t\tstage2_destroy_range(pgt, 0, BIT(ia_bits));\n+\n \tKVM_PGT_FN(kvm_pgtable_stage2_destroy_pgd)(pgt);\n }\n \n@@ -1070,6 +1123,8 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t\n \t\treturn -EINVAL;\n \t}\n \n+\tmmu-\u003earch = \u0026kvm-\u003earch;\n+\n \terr = kvm_init_ipa_range(mmu, type);\n \tif (err)\n \t\treturn err;\n@@ -1078,7 +1133,6 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t\n \tif (!pgt)\n \t\treturn -ENOMEM;\n \n-\tmmu-\u003earch = \u0026kvm-\u003earch;\n \terr = KVM_PGT_FN(kvm_pgtable_stage2_init)(pgt, mmu, \u0026kvm_s2_mm_ops);\n \tif (err)\n \t\tgoto out_free_pgtable;\n@@ -1117,9 +1171,24 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t\n \treturn err;\n }\n \n+static void kvm_realm_uninit_stage2(struct kvm_s2_mmu *mmu)\n+{\n+\tstruct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);\n+\n+\tmutex_lock(\u0026kvm-\u003earch.config_lock);\n+\tif (kvm_realm_state(kvm) == REALM_STATE_ACTIVE) {\n+\t\tkvm_set_realm_state(kvm, REALM_STATE_DYING);\n+\t\tWARN_ON(kvm_realm_teardown_stage2(kvm));\n+\t}\n+\tmutex_unlock(\u0026kvm-\u003earch.config_lock);\n+}\n+\n void kvm_uninit_stage2_mmu(struct kvm *kvm)\n {\n-\tkvm_free_stage2_pgd(\u0026kvm-\u003earch.mmu);\n+\tif (kvm_vm_is_realm(kvm))\n+\t\tkvm_realm_uninit_stage2(\u0026kvm-\u003earch.mmu);\n+\telse\n+\t\tkvm_free_stage2_pgd(\u0026kvm-\u003earch.mmu);\n \tkvm_mmu_free_memory_cache(\u0026kvm-\u003earch.mmu.split_page_cache);\n }\n \n@@ -1178,6 +1247,10 @@ void stage2_unmap_vm(struct kvm *kvm)\n \tstruct kvm_memory_slot *memslot;\n \tint idx, bkt;\n \n+\t/* For realms this is handled by the RMM so nothing to do here */\n+\tif (kvm_vm_is_realm(kvm))\n+\t\treturn;\n+\n \tidx = srcu_read_lock(\u0026kvm-\u003esrcu);\n \tmmap_read_lock(current-\u003emm);\n \twrite_lock(\u0026kvm-\u003emmu_lock);\n@@ -1747,6 +1820,10 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,\n \tvoid *memcache = NULL;\n \tgfn_t canonical_gfn;\n \tint ret;\n+\tbool is_realm_private = false;\n+\n+\tif (KVM_BUG_ON(!kvm_slot_has_gmem(s2fd-\u003ememslot), kvm))\n+\t\treturn -EINVAL;\n \n \tif (!perm_fault) {\n \t\tmemcache = get_mmu_memcache(s2fd-\u003evcpu);\n@@ -1761,7 +1838,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,\n \t}\n \n \ts2vi.vma_pagesize = PAGE_SIZE;\n-\ts2vi.gfn = ALIGN_DOWN(s2fd-\u003efault_ipa, s2vi.vma_pagesize) \u003e\u003e PAGE_SHIFT;\n+\ts2vi.gfn = gpa_to_gfn(kvm_gpa_from_fault(kvm, s2fd-\u003efault_ipa));\n \tcanonical_gfn = get_canonical_gfn(s2fd, \u0026s2vi);\n \n \twrite_fault = kvm_s2_fault_is_write(s2fd);\n@@ -1773,13 +1850,28 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,\n \t/* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */\n \tsmp_rmb();\n \n+\tif (kvm_vm_is_realm(kvm)) {\n+\t\tis_realm_private = kvm_realm_is_private_address(\u0026kvm-\u003earch.realm,\n+\t\t\t\t\t\t\t\ts2fd-\u003efault_ipa);\n+\n+\t\tif (is_realm_private != kvm_gmem_is_private_gfn(kvm, canonical_gfn)) {\n+\t\t\tkvm_prepare_memory_fault_exit(s2fd-\u003evcpu,\n+\t\t\t\t\t\t      gfn_to_gpa(canonical_gfn),\n+\t\t\t\t\t\t      PAGE_SIZE,\n+\t\t\t\t\t\t      write_fault, exec_fault,\n+\t\t\t\t\t\t      is_realm_private);\n+\t\t\tkfree(mapping);\n+\t\t\treturn -EFAULT;\n+\t\t}\n+\t}\n+\n \tret = kvm_gmem_get_pfn(kvm, s2fd-\u003ememslot, canonical_gfn, \u0026s2vi.pfn, NULL);\n \tif (ret) {\n \t\t/* If result is non-NULL this is a synthetic fault. */\n \t\tif (!result)\n \t\t\tkvm_prepare_memory_fault_exit(s2fd-\u003evcpu, gfn_to_gpa(canonical_gfn),\n \t\t\t\t\t\t      s2vi.vma_pagesize, write_fault, exec_fault,\n-\t\t\t\t\t\t      false);\n+\t\t\t\t\t\t      is_realm_private);\n \t\tkfree(mapping);\n \t\treturn ret;\n \t}\n@@ -1802,6 +1894,13 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,\n \t\tgoto out_unlock;\n \t}\n \n+\tif (kvm_vm_is_realm(kvm)) {\n+\t\tprot \u0026= ~KVM_PGTABLE_PROT_X;\n+\t\tret = realm_map_ipa(kvm, s2fd-\u003efault_ipa, s2vi.pfn,\n+\t\t\t\t    PAGE_SIZE, prot, memcache);\n+\t\tgoto out_unlock;\n+\t}\n+\n \tif (perm_fault) {\n \t\t/*\n \t\t * Drop the SW bits in favour of those stored in the\n@@ -2003,7 +2102,8 @@ static int kvm_s2_fault_get_vma_info(const struct kvm_s2_fault_desc *s2fd,\n \t * mapping size to ensure we find the right PFN and lay down the\n \t * mapping in the right place.\n \t */\n-\ts2vi-\u003egfn = ALIGN_DOWN(s2fd-\u003efault_ipa, s2vi-\u003evma_pagesize) \u003e\u003e PAGE_SHIFT;\n+\ts2vi-\u003egfn = kvm_gfn_from_fault(kvm, ALIGN_DOWN(s2fd-\u003efault_ipa,\n+\t\t\t\t\t\t       s2vi-\u003evma_pagesize));\n \n \ts2vi-\u003emte_allowed = kvm_vma_mte_allowed(vma);\n \n@@ -2198,12 +2298,15 @@ static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd,\n \tif (!perm_fault_granule \u0026\u0026 !s2vi-\u003emap_non_cacheable \u0026\u0026 kvm_has_mte(kvm))\n \t\tsanitise_mte_tags(kvm, pfn, mapping_size);\n \n-\t/*\n-\t * Under the premise of getting a FSC_PERM fault, we just need to relax\n-\t * permissions only if mapping_size equals perm_fault_granule. Otherwise,\n-\t * kvm_pgtable_stage2_map() should be called to change block size.\n-\t */\n-\tif (mapping_size == perm_fault_granule) {\n+\tif (kvm_vm_is_realm(kvm)) {\n+\t\tret = realm_map_ipa(kvm, s2fd-\u003efault_ipa, pfn, mapping_size,\n+\t\t\t\t    prot, memcache);\n+\t} else if (mapping_size == perm_fault_granule) {\n+\t\t/*\n+\t\t * Under the premise of getting a FSC_PERM fault, we just need to relax\n+\t\t * permissions only if mapping_size equals perm_fault_granule. Otherwise,\n+\t\t * kvm_pgtable_stage2_map() should be called to change block size.\n+\t\t */\n \t\t/*\n \t\t * Drop the SW bits in favour of those stored in the\n \t\t * PTE, which will be preserved.\n@@ -2296,18 +2399,43 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd,\n \treturn kvm_s2_fault_map(s2fd, \u0026s2vi, prot, memcache, result);\n }\n \n+static bool private_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa);\n+static bool kvm_fault_is_gmem_abort(struct kvm *kvm,\n+\t\t\t\t    const struct kvm_s2_fault_desc *s2fd);\n+\n static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)\n {\n \tVM_WARN_ON_ONCE(kvm_s2_fault_is_perm(s2fd) \u0026\u0026\n \t\t\t!esr_abt_is_write_fault(s2fd-\u003eesr) \u0026\u0026\n \t\t\t!kvm_s2_fault_is_exec(s2fd));\n \n-\tif (kvm_slot_has_gmem(s2fd-\u003ememslot))\n+\tif (private_ipa_fault(s2fd-\u003evcpu-\u003ekvm, s2fd-\u003efault_ipa) \u0026\u0026\n+\t    !kvm_slot_has_gmem(s2fd-\u003ememslot))\n+\t\treturn -EINVAL;\n+\n+\tif (kvm_fault_is_gmem_abort(s2fd-\u003evcpu-\u003ekvm, s2fd))\n \t\treturn gmem_abort(s2fd, NULL);\n \telse\n \t\treturn user_mem_abort(s2fd, NULL);\n }\n \n+/* Decide if an abort should be handled by the gmem */\n+static bool kvm_fault_is_gmem_abort(struct kvm *kvm,\n+\t\t\t\t    const struct kvm_s2_fault_desc *s2fd)\n+{\n+\tif (!kvm_slot_has_gmem(s2fd-\u003ememslot))\n+\t\treturn false;\n+\tif (kvm_memslot_is_gmem_only(s2fd-\u003ememslot))\n+\t\treturn true;\n+\t/*\n+\t * For Realms, all private faults must be backed by GMEM.\n+\t * TODO: Handle Trusted device private memory mappings.\n+\t */\n+\tif (private_ipa_fault(kvm, s2fd-\u003efault_ipa))\n+\t\treturn true;\n+\treturn false;\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@@ -2401,6 +2529,12 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)\n \treturn 0;\n }\n \n+static bool private_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa)\n+{\n+\treturn kvm_vm_is_realm(kvm) \u0026\u0026\n+\t\tkvm_realm_is_private_address(\u0026kvm-\u003earch.realm, fault_ipa);\n+}\n+\n /**\n  * kvm_handle_guest_abort - handles all 2nd stage aborts\n  * @vcpu:\tthe VCPU pointer\n@@ -2414,10 +2548,10 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)\n  */\n int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)\n {\n+\tstruct kvm *kvm = vcpu-\u003ekvm;\n \tstruct kvm_s2_trans nested_trans, *nested = NULL;\n \tunsigned long esr = kvm_vcpu_get_esr(vcpu);\n \tstruct kvm_s2_mmu *mmu = vcpu-\u003earch.hw_mmu;\n-\tstruct kvm *kvm = vcpu-\u003ekvm;\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 \tstruct kvm_memory_slot *memslot;\n@@ -2510,8 +2644,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)\n \t\tnested = \u0026nested_trans;\n \t}\n \n-\tgfn = ipa \u003e\u003e PAGE_SHIFT;\n+\tgfn = kvm_gfn_from_fault(kvm, ipa);\n \tmemslot = gfn_to_memslot(kvm, gfn);\n+\n \thva = gfn_to_hva_memslot_prot(memslot, gfn, \u0026writable);\n \twrite_fault = esr_abt_is_write_fault(esr);\n \tif (kvm_is_error_hva(hva) || (write_fault \u0026\u0026 !writable)) {\n@@ -2554,7 +2689,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)\n \t\t * of the page size.\n \t\t */\n \t\tipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));\n-\t\tret = io_mem_abort(vcpu, ipa);\n+\t\tret = io_mem_abort(vcpu, kvm_gpa_from_fault(kvm, ipa));\n \t\tgoto out_unlock;\n \t}\n \n@@ -2597,7 +2732,8 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)\n \tif (!kvm-\u003earch.mmu.pgt)\n \t\treturn false;\n \n-\tkvm_stage2_unmap_range(\u0026kvm-\u003earch.mmu, gpa, size, may_block);\n+\tkvm_stage2_unmap_range_filter(\u0026kvm-\u003earch.mmu, gpa, size, may_block,\n+\t\t\t\t      range-\u003eattr_filter);\n \tkvm_nested_unmap_cipa_range(kvm, gpa, size, may_block);\n \n \treturn false;\n@@ -2812,19 +2948,35 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,\n \t\t\t\t   enum kvm_mr_change change)\n {\n \thva_t hva, reg_end;\n+\tgfn_t ipa_limit;\n \tint ret = 0;\n \n-\tif (kvm_vm_is_protected(kvm)) {\n+\tif (kvm_vm_is_protected(kvm) \u0026\u0026 new \u0026\u0026\n+\t    (new-\u003eflags \u0026 (KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY)))\n+\t\treturn -EPERM;\n+\n+\tswitch (kvm-\u003earch.vm_flavor) {\n+\tcase VM_PROTECTED_PKVM:\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+\t\t    (change == KVM_MR_DELETE || change == KVM_MR_MOVE))\n \t\t\treturn -EPERM;\n-\t\t}\n+\t\tbreak;\n+\tcase VM_REALM:\n+\t\t/*\n+\t\t * Once the Realm is created, we cannot modify any slots that\n+\t\t * could be providing private memory. i.e., guest_memfd backed\n+\t\t * slots.\n+\t\t * TODO: Handle trusted device private memory slots\n+\t\t */\n+\t\tif (!kvm_realm_is_created(kvm))\n+\t\t\tbreak;\n \n-\t\tif (new \u0026\u0026\n-\t\t    new-\u003eflags \u0026 (KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY)) {\n+\t\tif (kvm_slot_has_gmem(old) || kvm_slot_has_gmem(new))\n \t\t\treturn -EPERM;\n-\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n \t}\n \n \tif (change != KVM_MR_CREATE \u0026\u0026 change != KVM_MR_MOVE \u0026\u0026\n@@ -2832,17 +2984,21 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,\n \t\treturn 0;\n \n \t/*\n-\t * Prevent userspace from creating a memory region outside of the IPA\n-\t * space addressable by the KVM guest IPA space.\n+\t * Realm shared IPAs alias the corresponding private IPAs, so memslots\n+\t * must reside entirely in the canonical lower half of the IPA space.\n \t */\n-\tif ((new-\u003ebase_gfn + new-\u003enpages) \u003e (kvm_phys_size(\u0026kvm-\u003earch.mmu) \u003e\u003e PAGE_SHIFT))\n+\tipa_limit = kvm_phys_size(\u0026kvm-\u003earch.mmu) \u003e\u003e PAGE_SHIFT;\n+\tif (kvm_vm_is_realm(kvm))\n+\t\tipa_limit \u003e\u003e= 1;\n+\tif (new-\u003ebase_gfn + new-\u003enpages \u003e ipa_limit)\n \t\treturn -EFAULT;\n \n \t/*\n-\t * Only support guest_memfd backed memslots with mappable memory, since\n-\t * there aren't any CoCo VMs that support only private memory on arm64.\n+\t * Only support guest_memfd backed memslots with mappable memory,\n+\t * unless the guest is a CCA realm guest.\n \t */\n-\tif (kvm_slot_has_gmem(new) \u0026\u0026 !kvm_memslot_is_gmem_only(new))\n+\tif (kvm_slot_has_gmem(new) \u0026\u0026 !kvm_memslot_is_gmem_only(new) \u0026\u0026\n+\t    !kvm_vm_is_realm(kvm))\n \t\treturn -EINVAL;\n \n \t/* guest_memfd is incompatible with MTE. */\n@@ -3157,14 +3313,24 @@ static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {\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_flush_remote_tlbs,\n+\t.vm_flush_remote_tlbs_range\t= realm_flush_remote_tlbs_range,\n+\t.vm_age_gfn\t\t\t= no_age_gfn,\n+\t.vm_test_age_gfn\t\t= no_age_gfn,\n+\t.vm_stage2_unmap_range\t\t= realm_stage2_unmap_range,\n+\t.vm_mem_abort\t\t\t= kvm_vm_mem_abort,\n+};\n+\n #define KVM_VM_S2_OPS(flavor, ops)\t\t\\\n-\t\t[(flavor)] = \u0026(ops)\n+\t\t[flavor] = \u0026(ops)\n \n static const struct kvm_vm_s2_ops *arm64_vm_s2_ops[] = {\n \tKVM_VM_S2_OPS(VM_VHE, kvm_default_vm_s2_ops),\n \tKVM_VM_S2_OPS(VM_NVHE, kvm_default_vm_s2_ops),\n \tKVM_VM_S2_OPS(VM_PKVM, pkvm_vm_s2_ops),\n \tKVM_VM_S2_OPS(VM_PROTECTED_PKVM, protected_pkvm_vm_s2_ops),\n+\tKVM_VM_S2_OPS(VM_REALM, realm_vm_s2_ops),\n };\n \n static int kvm_vm_init_vm_s2_ops(struct kvm *kvm)\ndiff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c\nnew file mode 100644\nindex 00000000000000..f89460d8adcba9\n--- /dev/null\n+++ b/arch/arm64/kvm/rmi.c\n@@ -0,0 +1,852 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Copyright (C) 2023-2026 ARM Ltd.\n+ */\n+\n+#include \u003clinux/arm-rmi-cmds.h\u003e\n+#include \u003clinux/kvm_host.h\u003e\n+\n+#include \u003casm/kvm_emulate.h\u003e\n+#include \u003casm/kvm_mmu.h\u003e\n+#include \u003casm/kvm_pgtable.h\u003e\n+#include \u003casm/virt.h\u003e\n+\n+static inline unsigned long rmi_rtt_level_mapsize(int level)\n+{\n+\tif (WARN_ON(level \u003e KVM_PGTABLE_LAST_LEVEL))\n+\t\treturn PAGE_SIZE;\n+\n+\treturn (1UL \u003c\u003c ARM64_HW_PGTABLE_LEVEL_SHIFT(level));\n+}\n+\n+static bool rmi_has_feature(int reg, unsigned long feature)\n+{\n+\treturn !!u64_get_bits(rmi_feat_reg(reg), feature);\n+}\n+\n+u32 kvm_rmm_ipa_limit(void)\n+{\n+\treturn u64_get_bits(rmi_feat_reg(0), RMI_FEATURE_REGISTER_0_S2SZ);\n+}\n+\n+static int get_start_level(struct realm *realm)\n+{\n+\treturn 4 - stage2_pgtable_levels(realm-\u003eia_bits);\n+}\n+\n+static int find_map_level(struct realm *realm,\n+\t\t\t  unsigned long start,\n+\t\t\t  unsigned long end)\n+{\n+\tint level = KVM_PGTABLE_LAST_LEVEL;\n+\n+\twhile (level \u003e get_start_level(realm)) {\n+\t\tunsigned long map_size = rmi_rtt_level_mapsize(level - 1);\n+\n+\t\tif (!IS_ALIGNED(start, map_size) ||\n+\t\t    (start + map_size) \u003e end)\n+\t\t\tbreak;\n+\n+\t\tlevel--;\n+\t}\n+\n+\treturn level;\n+}\n+\n+static unsigned long rmi_range_entry_size(int rmi_size)\n+{\n+\tif (WARN_ON(rmi_size \u003e KVM_PGTABLE_LAST_LEVEL))\n+\t\treturn 0;\n+\n+\treturn kvm_granule_size(KVM_PGTABLE_LAST_LEVEL - rmi_size);\n+}\n+\n+static int undelegate_range_desc(unsigned long desc)\n+{\n+\tunsigned long size = rmi_range_entry_size(RMI_ADDR_RANGE_BLOCK_SIZE(desc));\n+\tunsigned long count = RMI_ADDR_RANGE_COUNT(desc);\n+\tunsigned long addr = RMI_ADDR_RANGE_ADDR(desc);\n+\tunsigned long state = RMI_ADDR_RANGE_STATE(desc);\n+\n+\tif (state == RMI_OP_MEM_UNDELEGATED)\n+\t\treturn 0;\n+\n+\tif (size * count == 0)\n+\t\treturn 0;\n+\n+\treturn rmi_undelegate_range(addr, size * count);\n+}\n+\n+static phys_addr_t alloc_delegated_granule(struct kvm_mmu_memory_cache *mc)\n+{\n+\tphys_addr_t phys;\n+\tvoid *virt;\n+\n+\tif (mc) {\n+\t\tvirt = kvm_mmu_memory_cache_alloc(mc);\n+\t} else {\n+\t\tvirt = (void *)__get_free_page(GFP_ATOMIC | __GFP_ZERO |\n+\t\t\t\t\t       __GFP_ACCOUNT);\n+\t}\n+\n+\tif (!virt)\n+\t\treturn PHYS_ADDR_MAX;\n+\n+\tphys = virt_to_phys(virt);\n+\tif (rmi_delegate_page(phys)) {\n+\t\tfree_page((unsigned long)virt);\n+\t\treturn PHYS_ADDR_MAX;\n+\t}\n+\n+\treturn phys;\n+}\n+\n+static phys_addr_t alloc_rtt(struct kvm_mmu_memory_cache *mc)\n+{\n+\tphys_addr_t phys = alloc_delegated_granule(mc);\n+\n+\tif (phys != PHYS_ADDR_MAX)\n+\t\tkvm_account_pgtable_pages(phys_to_virt(phys), 1);\n+\n+\treturn phys;\n+}\n+\n+static void free_rtt(phys_addr_t phys)\n+{\n+\tif (rmi_free_delegated_page(phys))\n+\t\treturn;\n+\n+\tkvm_account_pgtable_pages(phys_to_virt(phys), -1);\n+}\n+\n+static long realm_rtt_create(struct realm *realm,\n+\t\t\t     unsigned long addr,\n+\t\t\t     int level,\n+\t\t\t     phys_addr_t phys)\n+{\n+\taddr = ALIGN_DOWN(addr, rmi_rtt_level_mapsize(level - 1));\n+\treturn rmi_rtt_create(virt_to_phys(realm-\u003erd), phys, addr, level);\n+}\n+\n+static long realm_rtt_fold(struct realm *realm,\n+\t\t\t   unsigned long addr,\n+\t\t\t   int level,\n+\t\t\t   phys_addr_t *rtt_granule)\n+{\n+\tunsigned long out_rtt;\n+\tlong ret;\n+\n+\taddr = ALIGN_DOWN(addr, rmi_rtt_level_mapsize(level - 1));\n+\tret = rmi_rtt_fold(virt_to_phys(realm-\u003erd), addr, level, \u0026out_rtt);\n+\n+\tif (rtt_granule)\n+\t\t*rtt_granule = out_rtt;\n+\n+\treturn ret;\n+}\n+\n+/*\n+ * realm_rtt_destroy - Destroy an RTT at @level for @addr.\n+ *\n+ * Returns - Result of the RMI_RTT_DESTROY call, and:\n+ * @rtt_granule:\tRTT granule, if the RTT was destroyed.\n+ * @next_addr:\t\tIPA corresponding to the next possible valid entry we\n+ *\t\t\tcan target\n+ */\n+static long realm_rtt_destroy(struct realm *realm, unsigned long addr,\n+\t\t\t      int level, phys_addr_t *rtt_granule,\n+\t\t\t      unsigned long *next_addr)\n+{\n+\tunsigned long out_rtt;\n+\tlong ret;\n+\n+\tret = rmi_rtt_destroy(virt_to_phys(realm-\u003erd), addr, level,\n+\t\t\t      \u0026out_rtt, next_addr);\n+\n+\t*rtt_granule = out_rtt;\n+\n+\treturn ret;\n+}\n+\n+static int realm_create_rtt_levels(struct realm *realm,\n+\t\t\t\t   unsigned long ipa,\n+\t\t\t\t   int level,\n+\t\t\t\t   int max_level,\n+\t\t\t\t   struct kvm_mmu_memory_cache *mc)\n+{\n+\twhile (level++ \u003c max_level) {\n+\t\tphys_addr_t rtt = alloc_rtt(mc);\n+\t\tlong ret;\n+\n+\t\tif (rtt == PHYS_ADDR_MAX)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tret = realm_rtt_create(realm, ipa, level, rtt);\n+\t\tif (ret \u003c 0) {\n+\t\t\tfree_rtt(rtt);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT \u0026\u0026\n+\t\t    RMI_RESULT_DATA_LEVEL(ret) == level - 1) {\n+\t\t\t/* The RTT already exists, continue */\n+\t\t\tfree_rtt(rtt);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\tWARN(1, \"Failed to create RTT at level %d: %ld\\n\",\n+\t\t\t     level, ret);\n+\t\t\tfree_rtt(rtt);\n+\t\t\treturn -ENXIO;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int realm_tear_down_rtt_level(struct realm *realm, int level,\n+\t\t\t\t     unsigned long start, unsigned long end)\n+{\n+\tssize_t map_size;\n+\tunsigned long addr, next_addr;\n+\n+\tif (WARN_ON(level \u003e KVM_PGTABLE_LAST_LEVEL))\n+\t\treturn -EINVAL;\n+\n+\tmap_size = rmi_rtt_level_mapsize(level - 1);\n+\n+\tfor (addr = start; addr \u003c end; addr = next_addr) {\n+\t\tphys_addr_t rtt_granule;\n+\t\tlong ret;\n+\t\tunsigned long align_addr = ALIGN(addr, map_size);\n+\n+\t\tnext_addr = ALIGN(addr + 1, map_size);\n+\n+\t\tif (next_addr \u003e end || align_addr != addr) {\n+\t\t\t/*\n+\t\t\t * The target range is smaller than what this level\n+\t\t\t * covers, recurse deeper.\n+\t\t\t */\n+\t\t\tret = realm_tear_down_rtt_level(realm,\n+\t\t\t\t\t\t\tlevel + 1,\n+\t\t\t\t\t\t\taddr,\n+\t\t\t\t\t\t\tmin(next_addr, end));\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tret = realm_rtt_destroy(realm, addr, level,\n+\t\t\t\t\t\u0026rtt_granule, \u0026next_addr);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\n+\t\tswitch (RMI_RESULT_STATUS(ret)) {\n+\t\tcase RMI_SUCCESS:\n+\t\t\tfree_rtt(rtt_granule);\n+\t\t\tbreak;\n+\t\tcase RMI_ERROR_RTT:\n+\t\t\tif (next_addr \u003e addr) {\n+\t\t\t\t/* Missing RTT, skip */\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * We tear down the RTT range for the full IPA\n+\t\t\t * space, after everything is unmapped. Also we\n+\t\t\t * descend down only if we cannot tear down a\n+\t\t\t * top level RTT. Thus RMM must be able to walk\n+\t\t\t * to the requested level. e.g., a block mapping\n+\t\t\t * exists at L1 or L2.\n+\t\t\t */\n+\t\t\tif (WARN_ON(RMI_RESULT_DATA_LEVEL(ret) != level))\n+\t\t\t\treturn -EBUSY;\n+\t\t\tif (WARN_ON(level == KVM_PGTABLE_LAST_LEVEL))\n+\t\t\t\treturn -EBUSY;\n+\n+\t\t\t/*\n+\t\t\t * The table has active entries in it, recurse deeper\n+\t\t\t * and tear down the RTTs.\n+\t\t\t */\n+\t\t\tnext_addr = ALIGN(addr + 1, map_size);\n+\t\t\tret = realm_tear_down_rtt_level(realm,\n+\t\t\t\t\t\t\tlevel + 1,\n+\t\t\t\t\t\t\taddr,\n+\t\t\t\t\t\t\tnext_addr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t\t/*\n+\t\t\t * Now that the child RTTs are destroyed,\n+\t\t\t * retry at this level.\n+\t\t\t */\n+\t\t\tnext_addr = addr;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tWARN_ON(1);\n+\t\t\treturn -ENXIO;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int realm_tear_down_rtt_range(struct realm *realm,\n+\t\t\t\t     unsigned long start, unsigned long end)\n+{\n+\t/*\n+\t * Root level RTTs can only be destroyed after the RD is destroyed. So\n+\t * tear down everything below the root level\n+\t */\n+\treturn realm_tear_down_rtt_level(realm, get_start_level(realm) + 1,\n+\t\t\t\t\t start, end);\n+}\n+\n+/*\n+ * Returns 0 on successful fold, a negative value on error, a positive value if\n+ * we were not able to fold all tables at this level.\n+ */\n+static int realm_fold_rtt_level(struct realm *realm, int level,\n+\t\t\t\tunsigned long start, unsigned long end)\n+{\n+\tint not_folded = 0;\n+\tssize_t map_size;\n+\tunsigned long addr, next_addr;\n+\n+\tif (WARN_ON(level \u003e KVM_PGTABLE_LAST_LEVEL))\n+\t\treturn -EINVAL;\n+\n+\tmap_size = rmi_rtt_level_mapsize(level - 1);\n+\n+\tfor (addr = start; addr \u003c end; addr = next_addr) {\n+\t\tphys_addr_t rtt_granule;\n+\t\tlong ret;\n+\t\tunsigned long align_addr = ALIGN(addr, map_size);\n+\n+\t\tnext_addr = ALIGN(addr + 1, map_size);\n+\n+\t\tret = realm_rtt_fold(realm, align_addr, level, \u0026rtt_granule);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\n+\t\tswitch (RMI_RESULT_STATUS(ret)) {\n+\t\tcase RMI_SUCCESS:\n+\t\t\tfree_rtt(rtt_granule);\n+\t\t\tbreak;\n+\t\tcase RMI_ERROR_RTT:\n+\t\t\tif (level == KVM_PGTABLE_LAST_LEVEL ||\n+\t\t\t    RMI_RESULT_DATA_LEVEL(ret) \u003c level) {\n+\t\t\t\tnot_folded++;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\t/* Recurse a level deeper */\n+\t\t\tret = realm_fold_rtt_level(realm,\n+\t\t\t\t\t\t   level + 1,\n+\t\t\t\t\t\t   addr,\n+\t\t\t\t\t\t   next_addr);\n+\t\t\tif (ret \u003c 0) {\n+\t\t\t\treturn ret;\n+\t\t\t} else if (ret == 0) {\n+\t\t\t\t/* Try again at this level */\n+\t\t\t\tnext_addr = addr;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tWARN_ON(1);\n+\t\t\treturn -ENXIO;\n+\t\t}\n+\t}\n+\n+\treturn not_folded;\n+}\n+\n+static int realm_destroy_rtts(struct kvm *kvm)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\tunsigned int ia_bits = realm-\u003eia_bits;\n+\n+\tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n+\n+\treturn realm_tear_down_rtt_range(realm, 0, (1UL \u003c\u003c ia_bits));\n+}\n+\n+static void realm_unmap_stage2(struct kvm *kvm)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\n+\tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n+\n+\tif (realm-\u003estage2_unmapped)\n+\t\treturn;\n+\n+\twrite_lock(\u0026kvm-\u003emmu_lock);\n+\tkvm_stage2_unmap_range(\u0026kvm-\u003earch.mmu, 0,\n+\t\t\t       BIT(realm-\u003eia_bits - 1), true);\n+\twrite_unlock(\u0026kvm-\u003emmu_lock);\n+\n+\trealm-\u003estage2_unmapped = true;\n+}\n+\n+int kvm_realm_teardown_stage2(struct kvm *kvm)\n+{\n+\tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n+\n+\trealm_unmap_stage2(kvm);\n+\treturn 0;\n+}\n+\n+static void realm_unmap_shared_range(struct kvm *kvm,\n+\t\t\t\t     unsigned long start,\n+\t\t\t\t     unsigned long end,\n+\t\t\t\t     bool may_block)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\tunsigned long rd = virt_to_phys(realm-\u003erd);\n+\tunsigned long next_addr, addr;\n+\tunsigned long shared_bit = BIT(realm-\u003eia_bits - 1);\n+\n+\tif (start \u003e= end)\n+\t\treturn;\n+\n+\t/*\n+\t * Callers may pass either an IPA range or its shared alias. Add the\n+\t * shared bit only for the former. Use addition so an exclusive end at\n+\t * shared_bit advances to the end of the IPA space.\n+\t */\n+\tif (start \u003c shared_bit) {\n+\t\tstart += shared_bit;\n+\t\tend += shared_bit;\n+\t}\n+\n+\tfor (addr = start; addr \u003c end; addr = next_addr) {\n+\t\tlong ret;\n+\n+\t\tret = rmi_rtt_unprot_unmap(rd, addr, end, RMI_ADDR_TYPE_NONE,\n+\t\t\t\t\t   0, \u0026next_addr, NULL, NULL);\n+\t\tif (WARN_ON(ret \u003c 0))\n+\t\t\treturn;\n+\n+\t\tswitch (RMI_RESULT_STATUS(ret)) {\n+\t\tcase RMI_SUCCESS:\n+\t\t\tbreak;\n+\t\tcase RMI_ERROR_RTT: {\n+\t\t\tint err_level = RMI_RESULT_DATA_LEVEL(ret);\n+\t\t\tint level = find_map_level(realm, addr, end);\n+\n+\t\t\tif (err_level \u003e= level) {\n+\t\t\t\t/* Nothing present, so skip */\n+\t\t\t\tnext_addr = addr + rmi_rtt_level_mapsize(err_level);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tret = realm_create_rtt_levels(realm, addr, err_level,\n+\t\t\t\t\t\t      level, NULL);\n+\t\t\tif (WARN_ON(ret))\n+\t\t\t\treturn;\n+\t\t\t/* Retry with the RTT levels in place */\n+\t\t\tnext_addr = addr;\n+\t\t\tbreak;\n+\t\t}\n+\t\tdefault:\n+\t\t\tWARN_ON(1);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (may_block)\n+\t\t\tcond_resched_rwlock_write(\u0026kvm-\u003emmu_lock);\n+\t}\n+\n+\trealm_fold_rtt_level(realm, get_start_level(realm) + 1,\n+\t\t\t     start, end);\n+}\n+\n+static void realm_unmap_private_range(struct kvm *kvm,\n+\t\t\t\t      unsigned long start,\n+\t\t\t\t      unsigned long end,\n+\t\t\t\t      bool may_block)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\tunsigned long rd = virt_to_phys(realm-\u003erd);\n+\tunsigned long next_addr, addr;\n+\tlong ret;\n+\n+\tfor (addr = start; addr \u003c end; addr = next_addr) {\n+\t\tunsigned long out_range;\n+\t\tunsigned long flags = RMI_ADDR_TYPE_SINGLE;\n+\t\t/* TODO: Optimise using RMI_ADDR_TYPE_LIST */\n+\n+retry:\n+\t\tret = rmi_rtt_data_unmap(rd, addr, end, flags, 0,\n+\t\t\t\t\t \u0026next_addr, \u0026out_range, NULL);\n+\t\tif (WARN_ON(ret \u003c 0))\n+\t\t\treturn;\n+\n+\t\tif (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {\n+\t\t\tphys_addr_t rtt;\n+\n+\t\t\tif (next_addr \u003e addr)\n+\t\t\t\tcontinue; /* UNASSIGNED */\n+\n+\t\t\trtt = alloc_rtt(NULL);\n+\t\t\tif (WARN_ON(rtt == PHYS_ADDR_MAX))\n+\t\t\t\treturn;\n+\t\t\tret = realm_rtt_create(realm, addr,\n+\t\t\t\t\t       RMI_RESULT_DATA_LEVEL(ret) + 1, rtt);\n+\t\t\tif (WARN_ON(ret)) {\n+\t\t\t\tfree_rtt(rtt);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\tgoto retry;\n+\t\t} else if (WARN_ON(ret)) {\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tret = undelegate_range_desc(out_range);\n+\t\tif (WARN_ON(ret))\n+\t\t\tbreak;\n+\n+\t\tif (may_block)\n+\t\t\tcond_resched_rwlock_write(\u0026kvm-\u003emmu_lock);\n+\t}\n+\n+\trealm_fold_rtt_level(realm, get_start_level(realm) + 1,\n+\t\t\t     start, end);\n+}\n+\n+void kvm_realm_unmap_range_filter(struct kvm *kvm, unsigned long start,\n+\t\t\t\t  unsigned long size, bool may_block,\n+\t\t\t\t  enum kvm_gfn_range_filter attr_filter)\n+{\n+\tunsigned long end = start + size;\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\n+\tif (!kvm_realm_is_created(kvm))\n+\t\treturn;\n+\n+\tend = min(BIT(realm-\u003eia_bits - 1), end);\n+\n+\tif (attr_filter \u0026 KVM_FILTER_SHARED)\n+\t\trealm_unmap_shared_range(kvm, start, end, may_block);\n+\tif (attr_filter \u0026 KVM_FILTER_PRIVATE)\n+\t\trealm_unmap_private_range(kvm, start, end, may_block);\n+}\n+\n+void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start,\n+\t\t\t   unsigned long size, bool may_block)\n+{\n+\tkvm_realm_unmap_range_filter(kvm, start, size, may_block,\n+\t\t\t\t     KVM_FILTER_SHARED | KVM_FILTER_PRIVATE);\n+}\n+\n+static unsigned long addr_range_desc(unsigned long phys, unsigned long size,\n+\t\t\t\t     unsigned long state)\n+{\n+\tunsigned long count = 1;\n+\tunsigned long out;\n+\tint rmi_size;\n+\n+\tfor (rmi_size = KVM_PGTABLE_LAST_LEVEL; rmi_size \u003e= 0; rmi_size--) {\n+\t\tunsigned long entry_size = rmi_range_entry_size(rmi_size);\n+\n+\t\tif (size == entry_size)\n+\t\t\tbreak;\n+\t\tif (size \u003e entry_size) {\n+\t\t\trmi_size = -1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (rmi_size \u003c 0) {\n+\t\t/*\n+\t\t * Only support mapping at the page level granularity when\n+\t\t * it's an unusual length. This should get us back onto a larger\n+\t\t * block size for the subsequent mappings.\n+\t\t */\n+\t\trmi_size = 0;\n+\t\tcount = MIN(size \u003e\u003e PAGE_SHIFT, PTRS_PER_PTE - 1);\n+\t}\n+\n+\tWARN_ON(phys \u0026 ~PAGE_MASK);\n+\n+\tout = FIELD_PREP(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, rmi_size) |\n+\t      FIELD_PREP(RMI_ADDR_RANGE_COUNT_MASK, count) |\n+\t      FIELD_PREP(RMI_ADDR_RANGE_STATE_MASK, state);\n+\tout |= phys \u0026 PAGE_MASK;\n+\n+\treturn out;\n+}\n+\n+static int realm_map_protected(struct kvm *kvm,\n+\t\t\t       unsigned long ipa,\n+\t\t\t       kvm_pfn_t pfn,\n+\t\t\t       unsigned long map_size,\n+\t\t\t       struct kvm_mmu_memory_cache *memcache)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\tphys_addr_t phys = __pfn_to_phys(pfn);\n+\tphys_addr_t base_phys = phys;\n+\tphys_addr_t rd = virt_to_phys(realm-\u003erd);\n+\tphys_addr_t delegated_phys;\n+\tunsigned long base_ipa = ipa;\n+\tunsigned long ipa_top;\n+\tlong ret = 0;\n+\n+\tif (WARN_ON(!IS_ALIGNED(map_size, PAGE_SIZE) ||\n+\t\t    !IS_ALIGNED(ipa, map_size)))\n+\t\treturn -EINVAL;\n+\n+\tif (rmi_delegate_range(phys, map_size, \u0026delegated_phys)) {\n+\t\tif (delegated_phys == phys) {\n+\t\t\t/*\n+\t\t\t * It's likely we raced with another VCPU on the same\n+\t\t\t * fault. Assume the other VCPU has handled the fault\n+\t\t\t * and return to the guest.\n+\t\t\t */\n+\t\t\treturn 0;\n+\t\t}\n+\t\t/* Partial delegation - map as much as we can */\n+\t\tmap_size = delegated_phys - phys;\n+\t}\n+\n+\tipa_top = ipa + map_size;\n+\n+\twhile (ipa \u003c ipa_top) {\n+\t\tunsigned long flags = RMI_ADDR_TYPE_SINGLE;\n+\t\tunsigned long range_desc = addr_range_desc(phys, ipa_top - ipa,\n+\t\t\t\t\t\t\t   RMI_OP_MEM_DELEGATED);\n+\t\tunsigned long out_top;\n+\n+\t\tret = rmi_rtt_data_map(rd, ipa, ipa_top, flags, range_desc,\n+\t\t\t\t       \u0026out_top);\n+\t\tif (ret \u003c 0)\n+\t\t\tgoto err_undelegate;\n+\n+\t\tif (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {\n+\t\t\t/* Create missing RTTs and retry */\n+\t\t\tint level = RMI_RESULT_DATA_LEVEL(ret);\n+\n+\t\t\tif (WARN_ON(level \u003e= KVM_PGTABLE_LAST_LEVEL))\n+\t\t\t\tgoto err_undelegate;\n+\t\t\tret = realm_create_rtt_levels(realm, ipa, level,\n+\t\t\t\t\t\t      level + 1,\n+\t\t\t\t\t\t      memcache);\n+\t\t\tif (ret)\n+\t\t\t\tgoto err_undelegate;\n+\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (WARN_ON(ret))\n+\t\t\tgoto err_undelegate;\n+\n+\t\tphys += out_top - ipa;\n+\t\tipa = out_top;\n+\t}\n+\n+\treturn 0;\n+\n+err_undelegate:\n+\trealm_unmap_private_range(kvm, base_ipa, ipa, true);\n+\t/* A range which cannot be undelegated remains owned by the RMM. */\n+\tWARN_ON(rmi_undelegate_range(base_phys, map_size));\n+\treturn ret \u003c 0 ? ret : -ENXIO;\n+}\n+\n+static int realm_map_non_secure(struct kvm *kvm,\n+\t\t\t\tunsigned long ipa,\n+\t\t\t\tkvm_pfn_t pfn,\n+\t\t\t\tunsigned long size,\n+\t\t\t\tenum kvm_pgtable_prot prot,\n+\t\t\t\tstruct kvm_mmu_memory_cache *memcache)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\tunsigned long attr, flags = 0;\n+\tphys_addr_t rd = virt_to_phys(realm-\u003erd);\n+\tphys_addr_t phys = __pfn_to_phys(pfn);\n+\tunsigned long ipa_top = ipa + size;\n+\tlong ret;\n+\n+\tif (WARN_ON(!IS_ALIGNED(size, PAGE_SIZE)))\n+\t\treturn -EINVAL;\n+\n+\tswitch (prot \u0026 (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC)) {\n+\tcase KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC:\n+\t\treturn -EINVAL;\n+\tcase KVM_PGTABLE_PROT_DEVICE:\n+\t\tattr = MT_S2_FWB_DEVICE_nGnRE;\n+\t\tbreak;\n+\tcase KVM_PGTABLE_PROT_NORMAL_NC:\n+\t\tattr = MT_S2_FWB_NORMAL_NC;\n+\t\tbreak;\n+\tdefault:\n+\t\tattr = MT_S2_FWB_NORMAL;\n+\t}\n+\n+\tflags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR_MASK, attr);\n+\n+\tif (prot \u0026 KVM_PGTABLE_PROT_R)\n+\t\tflags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK, RMI_S2AP_DIRECT_READ);\n+\tif (prot \u0026 KVM_PGTABLE_PROT_W)\n+\t\tflags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK, RMI_S2AP_DIRECT_WRITE);\n+\n+\tflags |= RMI_ADDR_TYPE_SINGLE;\n+\n+\twhile (ipa \u003c ipa_top) {\n+\t\tunsigned long range_desc = addr_range_desc(phys, ipa_top - ipa,\n+\t\t\t\t\t\t\t   RMI_OP_MEM_UNDELEGATED);\n+\t\tunsigned long out_top;\n+\n+\t\tret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags, range_desc,\n+\t\t\t\t\t \u0026out_top);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\n+\t\tif (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {\n+\t\t\t/* Create missing RTTs and retry */\n+\t\t\tint level = RMI_RESULT_DATA_LEVEL(ret);\n+\t\t\tint req_level = find_map_level(realm, ipa, ipa_top);\n+\n+\t\t\t/*\n+\t\t\t * There already exists a mapping at the level. May be\n+\t\t\t * we are relaxing a permission for the given range ?\n+\t\t\t */\n+\t\t\tif (level \u003e= req_level) {\n+\t\t\t\trealm_unmap_shared_range(kvm, ipa, ipa_top, true);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tret = realm_create_rtt_levels(realm, ipa, level,\n+\t\t\t\t\t\t      req_level,\n+\t\t\t\t\t\t      memcache);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\tret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags,\n+\t\t\t\t\t\t range_desc, \u0026out_top);\n+\t\t\tif (ret \u003c 0)\n+\t\t\t\treturn ret;\n+\t\t}\n+\n+\t\tif (ret \u0026\u0026 RMI_RESULT_STATUS(ret) != RMI_ERROR_RTT)\n+\t\t\treturn ret;\n+\n+\t\tphys += out_top - ipa;\n+\t\tipa = out_top;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,\n+\t\t  kvm_pfn_t pfn, unsigned long map_size,\n+\t\t  enum kvm_pgtable_prot prot,\n+\t\t  struct kvm_mmu_memory_cache *memcache)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\n+\tif (kvm_realm_state(kvm) != REALM_STATE_ACTIVE)\n+\t\treturn -EBUSY;\n+\n+\tipa = ALIGN_DOWN(ipa, map_size);\n+\tif (!kvm_realm_is_private_address(realm, ipa)) {\n+\t\treturn realm_map_non_secure(kvm, ipa, pfn, map_size, prot,\n+\t\t\t\t\t    memcache);\n+\t}\n+\n+\t/* It's impossible to map protected pages read-only. */\n+\tif (WARN_ON(!(prot \u0026 KVM_PGTABLE_PROT_W)))\n+\t\treturn -EFAULT;\n+\treturn realm_map_protected(kvm, ipa, pfn, map_size, memcache);\n+}\n+\n+void kvm_destroy_realm(struct kvm *kvm)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\tsize_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm-\u003earch.mmu.vtcr);\n+\n+\tguard(mutex)(\u0026kvm-\u003earch.config_lock);\n+\n+\tif (!kvm_realm_is_created(kvm)) {\n+\t\tkfree(realm-\u003esro);\n+\t\trealm-\u003esro = NULL;\n+\t\treturn;\n+\t}\n+\n+\tkvm_set_realm_state(kvm, REALM_STATE_DYING);\n+\n+\t/*\n+\t * REALM_DESTROY requires the realm to be non-live: all RECs must have\n+\t * been destroyed and the root RTTs must be empty. Unmap the IPA space\n+\t * and destroy any non-root RTTs before tearing down the RD. The root\n+\t * RTT pages are still owned by the RMM at this point, so keep the KVM\n+\t * pgtable alive until after REALM_DESTROY and undelegation.\n+\t */\n+\trealm_unmap_stage2(kvm);\n+\n+\tif (realm-\u003erd) {\n+\t\tphys_addr_t rd_phys = virt_to_phys(realm-\u003erd);\n+\n+\t\tif (WARN_ON(rmi_realm_terminate(rd_phys, realm-\u003esro)))\n+\t\t\treturn;\n+\n+\t\tif (WARN_ON(realm_destroy_rtts(kvm)))\n+\t\t\treturn;\n+\n+\t\tif (WARN_ON(rmi_realm_destroy(rd_phys, realm-\u003esro)))\n+\t\t\treturn;\n+\t\trmi_free_delegated_page(rd_phys);\n+\t\trealm-\u003erd = NULL;\n+\t}\n+\n+\tif (WARN_ON(rmi_undelegate_range(kvm-\u003earch.mmu.pgd_phys,\n+\t\t\t\t\t pgd_size)))\n+\t\treturn;\n+\n+\tkvm_set_realm_state(kvm, REALM_STATE_DEAD);\n+\n+\t/* Now that the realm is destroyed, free the entry-level RTTs. */\n+\tkvm_free_stage2_pgd(\u0026kvm-\u003earch.mmu);\n+\n+\tkfree(realm-\u003esro);\n+\trealm-\u003esro = NULL;\n+}\n+\n+int kvm_init_realm(struct kvm *kvm)\n+{\n+\tstruct realm *realm = \u0026kvm-\u003earch.realm;\n+\n+\trealm-\u003esro = kmalloc_obj(*realm-\u003esro);\n+\tif (!realm-\u003esro)\n+\t\treturn -ENOMEM;\n+\n+\treturn 0;\n+}\n+\n+static int rmm_check_features(void)\n+{\n+\tif (kvm_lpa2_is_enabled() \u0026\u0026\n+\t    !rmi_has_feature(0, RMI_FEATURE_REGISTER_0_LPA2)) {\n+\t\tkvm_err(\"RMM doesn't support LPA2\\n\");\n+\t\treturn -ENXIO;\n+\t}\n+\n+\tif (!rmi_has_feature(1, RMI_FEATURE_REGISTER_1_HASH_SHA_256)) {\n+\t\tkvm_err(\"RMM doesn't support SHA-256 measurements\\n\");\n+\t\treturn -ENXIO;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void kvm_init_rmi(void)\n+{\n+\tif (kvm_get_mode() != KVM_MODE_RMM)\n+\t\treturn;\n+\n+\tif (!is_rmi_available())\n+\t\treturn;\n+\n+\tif (rmm_check_features())\n+\t\treturn;\n+\n+\t/* Future patch will enable static branch kvm_rmi_is_available */\n+}\n"
}

2/1 2026/11/03 03:50 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "All changes in this patch series are confined strictly to the ARM64 architecture (under arch/arm64/), adding support for Arm CCA / RMM (Realm Management Interface) to KVM. As the target architecture is amd64, the modified code is exclusive to ARM64 and cannot be compiled or executed 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 ed0dd90af477374dd53c089f8f6afb0a5022104d
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Nov 3 03:50:43 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 334211b8b2ee49..8a71fddfdb8502 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -831,4 +831,33 @@ 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;
+}
+
+static inline gpa_t kvm_gpa_from_fault(struct kvm *kvm, phys_addr_t ipa)
+{
+	if (!kvm_vm_is_realm(kvm))
+		return ipa;
+
+	return ipa & ~BIT(kvm->arch.realm.ia_bits - 1);
+}
+
+static inline gfn_t kvm_gfn_from_fault(struct kvm *kvm, phys_addr_t ipa)
+{
+	return gpa_to_gfn(kvm_gpa_from_fault(kvm, ipa));
+}
+
 #endif /* __ARM64_KVM_EMULATE_H__ */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 97332ffcb1300e..41e15230263d86 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -29,6 +29,7 @@
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_hcall.h>
+#include <asm/kvm_rmi.h>
 #include <asm/vncr_mapping.h>
 
 #define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -48,6 +49,13 @@
 #define KVM_VCPU_MAX_FEATURES 10
 #define KVM_VCPU_VALID_FEATURES	(BIT(KVM_VCPU_MAX_FEATURES) - 1)
 
+#define KVM_REALM_VCPU_VALID_FEATURES			\
+	(BIT(KVM_ARM_VCPU_POWER_OFF)		|	\
+	 BIT(KVM_ARM_VCPU_PSCI_0_2)		|	\
+	 BIT(KVM_ARM_VCPU_PTRAUTH_ADDRESS)	|	\
+	 BIT(KVM_ARM_VCPU_SVE)			|	\
+	 BIT(KVM_ARM_VCPU_PTRAUTH_GENERIC))
+
 #define KVM_REQ_SLEEP \
 	KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
 #define KVM_REQ_IRQ_PENDING		KVM_ARCH_REQ(1)
@@ -120,6 +128,7 @@ enum kvm_mode {
 	KVM_MODE_DEFAULT,
 	KVM_MODE_PROTECTED,
 	KVM_MODE_NV,
+	KVM_MODE_RMM,
 	KVM_MODE_NONE,
 };
 #ifdef CONFIG_KVM
@@ -232,6 +241,7 @@ struct kvm_vcpu_ops {
 };
 
 struct kvm_gfn_range;
+enum kvm_gfn_range_filter;
 struct kvm_s2_fault_desc;
 
 struct kvm_vm_s2_ops {
@@ -242,7 +252,8 @@ struct kvm_vm_s2_ops {
 					  u64 nr_pages);
 	void (*vm_stage2_unmap_range)(struct kvm_s2_mmu *mmu,
 				      phys_addr_t start, u64 size,
-				      bool may_block);
+				      bool may_block,
+				      enum kvm_gfn_range_filter filter);
 	int (*vm_mem_abort)(const struct kvm_s2_fault_desc *s2fd);
 };
 
@@ -408,6 +419,7 @@ enum kvm_arm_vm_flavor {
 	VM_PKVM,		/* Normal guests on pKVM */
 	MARKER(__VM_PROTECTED),
 	VM_PROTECTED_PKVM,	/* Protected VM */
+	VM_REALM,		/* CCA */
 	VM_FLAVOR_MAX
 };
 
@@ -501,11 +513,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 */
@@ -1571,30 +1586,43 @@ struct kvm *kvm_arch_alloc_vm(void);
 
 #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE
 
-#ifdef __KVM_NVHE_HYPERVISOR__
+#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor >= __VM_PROTECTED)
 
-#define kvm_vm_is_protected(kvm)			\
-	(is_protected_kvm_enabled() && ((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM))
+#ifdef __KVM_NVHE_HYPERVISOR__
 /*
  * Accessing vcpu->kvm from nVHE hyp stub is tricky, as we need to convert the
  * pointer to the hyp VA. With pKVM, the nVHE code runs with the hyp_vcpu,
- * which is populated correctly and is gated on is_protected_kvm_enabled().
+ * which is populated correctly. Always vcpu_is_protected_pkvm(), which is
+ * gated on is_protected_kvm_enabled().
+ */
+#define vcpu_is_protected(vcpu)		BUILD_BUG_ON(1)
+#else
+#define vcpu_is_protected(vcpu)		kvm_vm_is_protected((vcpu)->kvm)
+#endif
+
+#define kvm_vm_is_protected_pkvm(kvm)					\
+	(is_protected_kvm_enabled() && ((kvm)->arch.vm_flavor == VM_PROTECTED_PKVM))
+
+/*
+ * Rely on is_protected_kvm_enabled() check in kvm_vm_is_protected_pkvm() to
+ * make sure the vcpu->kvm is always valid VA in the context
  */
-#define vcpu_is_protected(vcpu)						\
+#define vcpu_is_protected_pkvm(vcpu)					\
 	({								\
 		struct kvm *__kvm = READ_ONCE((vcpu)->kvm);		\
 									\
-		(__kvm && kvm_vm_is_protected(__kvm));			\
+		(__kvm && kvm_vm_is_protected_pkvm(__kvm));		\
 	})
 
-#else
-
-#define kvm_vm_is_protected(kvm)	((kvm)->arch.vm_flavor >= __VM_PROTECTED)
-#define vcpu_is_protected(vcpu)		kvm_vm_is_protected((vcpu)->kvm)
+#define kvm_vm_is_realm(kvm)		((kvm)->arch.vm_flavor == VM_REALM)
+#define vcpu_is_rec(vcpu)		kvm_vm_is_realm((vcpu)->kvm)
 
 #define kvm_vm_hyp_is_distrusting(kvm)	((kvm)->arch.vm_flavor >= __VM_DISTRUSTING_HYP)
 
-#endif	/* __KVM_NVHE_HYPERVISOR__ */
+#define kvm_arch_has_private_mem(kvm) kvm_vm_is_realm(kvm)
+
+int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature);
+bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
 
 #define kvm_has_mte(kvm)					\
 	(system_supports_mte() &&				\
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
new file mode 100644
index 00000000000000..105660d4d56a50
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_rmi.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#ifndef __ASM_KVM_RMI_H
+#define __ASM_KVM_RMI_H
+
+#include <asm/kvm_pgtable.h>
+#include <linux/arm-smccc-rmi.h>
+#include <linux/kvm.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, Realm Descriptor has been created,
+	 *      but it is not yet activated. Memory may be populated.
+	 */
+	REALM_STATE_NEW,
+	/**
+	 * @REALM_STATE_ACTIVE:
+	 *      Realm has been created and is eligible for execution with
+	 *      rmi_rec_enter(). Memory may no longer be populated with known
+	 *	contents.
+	 */
+	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
+ *
+ * @rd: Kernel mapping of the RMM-managed Realm Descriptor (RD) granule
+ * @sro: Preallocated SRO state context for Realm MMU operations
+ * @state: The lifetime state machine for the realm
+ * @ia_bits: Number of valid Input Address bits in the IPA
+ * @stage2_unmapped: The Realm stage-2 mappings have been removed
+ */
+struct realm {
+	void *rd;
+	/*
+	 * Reused by RTT map/unmap SRO commands. Those commands are only
+	 * issued from Realm stage-2 map/unmap paths while kvm->mmu_lock is
+	 * held for write, including Realm fault handling where
+	 * kvm_fault_lock() takes the write side, so concurrent use is
+	 * serialized.
+	 */
+	struct rmi_sro_state *sro;
+	enum realm_state state;
+	unsigned int ia_bits;
+	bool stage2_unmapped;
+};
+
+void kvm_init_rmi(void);
+u32 kvm_rmm_ipa_limit(void);
+
+int kvm_init_realm(struct kvm *kvm);
+void kvm_destroy_realm(struct kvm *kvm);
+int kvm_realm_teardown_stage2(struct kvm *kvm);
+
+enum kvm_gfn_range_filter;
+
+void kvm_realm_unmap_range_filter(struct kvm *kvm,
+				  unsigned long ipa,
+				  unsigned long size,
+				  bool may_block,
+				  enum kvm_gfn_range_filter attr_filter);
+
+void kvm_realm_unmap_range(struct kvm *kvm,
+			   unsigned long ipa,
+			   unsigned long size,
+			   bool may_block);
+
+int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,
+		  kvm_pfn_t pfn, unsigned long map_size,
+		  enum kvm_pgtable_prot prot,
+		  struct kvm_mmu_memory_cache *memcache);
+
+static inline bool kvm_realm_is_private_address(struct realm *realm,
+						unsigned long addr)
+{
+	return !(addr & BIT(realm->ia_bits - 1));
+}
+
+#endif /* __ASM_KVM_RMI_H */
diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index b546703c3ab9a8..92cec42952f42c 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/Kconfig b/arch/arm64/kvm/Kconfig
index cecea0820e523c..d067063a36868c 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -34,6 +34,7 @@ menuconfig KVM
 	select GUEST_PERF_EVENTS if PERF_EVENTS
 	select KVM_GUEST_MEMFD
 	select KVM_GENERIC_PRE_FAULT_MEMORY
+	select ARM_RMM_RMI
 	help
 	  Support hosting virtualized guest machines.
 
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 7aaeeb84e788ec..66a719ab5b6b35 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -16,7 +16,7 @@ CFLAGS_handle_exit.o += -Wno-override-init
 kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
 	 inject_fault.o va_layout.o handle_exit.o config.o \
 	 guest.o debug.o reset.o sys_regs.o stacktrace.o \
-	 vgic-sys-reg-v3.o fpsimd.o pkvm.o \
+	 vgic-sys-reg-v3.o fpsimd.o pkvm.o rmi.o \
 	 arch_timer.o trng.o vmid.o emulate-nested.o nested.o at.o \
 	 vgic/vgic.o vgic/vgic-init.o \
 	 vgic/vgic-irqfd.o vgic/vgic-v2.o \
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 157bf4b8120201..9a7bbfaf9675c3 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -42,6 +42,7 @@
 #include <asm/kvm_nested.h>
 #include <asm/kvm_pkvm.h>
 #include <asm/kvm_ptrauth.h>
+#include <asm/kvm_rmi.h>
 #include <asm/sections.h>
 #include <asm/stacktrace/nvhe.h>
 
@@ -112,6 +113,8 @@ long kvm_get_cap_for_kvm_ioctl(unsigned int ioctl, long *ext)
 	return -EINVAL;
 }
 
+DEFINE_STATIC_KEY_FALSE(kvm_rmi_is_available);
+
 DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);
 
 DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_base);
@@ -300,6 +303,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 
 	bitmap_zero(kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES);
 
+	/* Initialise the realm bits after the generic bits are enabled */
+	if (kvm_vm_is_realm(kvm)) {
+		ret = kvm_init_realm(kvm);
+		if (ret)
+			goto err_uninit_mmu;
+	}
+
 	return 0;
 
 err_uninit_mmu:
@@ -364,6 +374,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 
 	kvm_destroy_nested(kvm);
 	kvm_arm_teardown_hypercalls(kvm);
+	if (kvm_vm_is_realm(kvm))
+		kvm_destroy_realm(kvm);
 }
 
 static bool kvm_has_full_ptr_auth(void)
@@ -1732,10 +1744,19 @@ static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
 {
 	unsigned long features = KVM_VCPU_VALID_FEATURES;
 
-	if (vcpu->kvm->arch.vm_flavor == VM_PROTECTED_PKVM)
+	switch (vcpu->kvm->arch.vm_flavor) {
+	case VM_PROTECTED_PKVM:
 		kvm_pkvm_vcpu_allowed_features(vcpu->kvm, &features);
+		break;
+	case VM_REALM:
+		features = KVM_REALM_VCPU_VALID_FEATURES;
+		break;
+	default:
+		break;
+	}
 
-	if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
+	if (vcpu_is_protected(vcpu) ||
+	    !cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
 		clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
 
 	if (!kvm_supports_guest_pmuv3()) {
@@ -1751,7 +1772,8 @@ static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
 		clear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features);
 	}
 
-	if (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
+	if (vcpu_is_protected(vcpu) ||
+	    !cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
 		clear_bit(KVM_ARM_VCPU_HAS_EL2, &features);
 
 	return features;
@@ -1775,7 +1797,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
 	if (features & ~system_supported_vcpu_features(vcpu))
 		return -EINVAL;
 
-	/* EL2 implements PSCI 1.1; the host must not dispatch as 0.1. */
+	/* Both pKVM and RMM implements PSCI 1.1; the host must not dispatch as 0.1. */
 	if (vcpu_is_protected(vcpu) &&
 	    !test_bit(KVM_ARM_VCPU_PSCI_0_2, &features))
 		return -EINVAL;
@@ -2312,6 +2334,7 @@ static const struct kvm_vcpu_ops *arm64_vcpu_ops[] = {
 	KVM_VCPU_OPS(VM_VHE, vhe_vcpu_ops),
 	KVM_VCPU_OPS(VM_PKVM, pkvm_vcpu_ops),
 	KVM_VCPU_OPS(VM_PROTECTED_PKVM, pkvm_vcpu_ops),
+	[VM_REALM] = NULL,
 };
 
 static void kvm_init_vcpu_ops(struct kvm_vcpu *vcpu)
@@ -3269,6 +3292,8 @@ static __init int kvm_arm_init(void)
 
 	in_hyp_mode = is_kernel_in_hyp_mode();
 
+	kvm_init_rmi();
+
 	if (cpus_have_final_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ||
 	    cpus_have_final_cap(ARM64_WORKAROUND_1508412))
 		kvm_info("Guests without required CPU erratum workarounds can deadlock system!\n" \
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index 0261b2bc681f77..ae6ced6d7b9ea7 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -84,7 +84,7 @@ pkvm_hyp_vcpu_to_hyp_vm(struct pkvm_hyp_vcpu *hyp_vcpu)
 
 static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu)
 {
-	return vcpu_is_protected(&hyp_vcpu->vcpu);
+	return vcpu_is_protected_pkvm(&hyp_vcpu->vcpu);
 }
 
 static inline bool pkvm_hyp_vm_is_protected(struct pkvm_hyp_vm *hyp_vm)
diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c
index 89fb9dd351ad5d..6c03f48e6be344 100644
--- a/arch/arm64/kvm/hyp/nvhe/switch.c
+++ b/arch/arm64/kvm/hyp/nvhe/switch.c
@@ -238,7 +238,7 @@ static const exit_handler_fn pvm_exit_handlers[] = {
 
 static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
 {
-	if (unlikely(vcpu_is_protected(vcpu)))
+	if (unlikely(vcpu_is_protected_pkvm(vcpu)))
 		return pvm_exit_handlers;
 
 	return hyp_exit_handlers;
@@ -259,7 +259,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
 	 * it.  The check below is based on the one in
 	 * kvm_arch_vcpu_ioctl_run().
 	 */
-	if (unlikely(vcpu_is_protected(vcpu) && vcpu_mode_is_32bit(vcpu))) {
+	if (unlikely(vcpu_is_protected_pkvm(vcpu) && vcpu_mode_is_32bit(vcpu))) {
 		/*
 		 * As we have caught the guest red-handed, decide that it isn't
 		 * fit for purpose anymore by making the vcpu invalid. The VMM
diff --git a/arch/arm64/kvm/hyp/nvhe/timer-sr.c b/arch/arm64/kvm/hyp/nvhe/timer-sr.c
index 99306571691331..17bd1ff621ab69 100644
--- a/arch/arm64/kvm/hyp/nvhe/timer-sr.c
+++ b/arch/arm64/kvm/hyp/nvhe/timer-sr.c
@@ -48,7 +48,7 @@ void __timer_enable_traps(struct kvm_vcpu *vcpu)
 	 * or running a protected VM (we don't offset anything in this case).
 	 */
 	clr = CNTHCTL_EL1PCEN;
-	if (vcpu_is_protected(vcpu) ||
+	if (vcpu_is_protected_pkvm(vcpu) ||
 	    !timer_get_offset(vcpu_ptimer(vcpu)))
 		set |= CNTHCTL_EL1PCTEN;
 	else
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 1db10d8d6db737..bb33c07569e3be 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -191,6 +191,12 @@ static int kvm_vm_flush_remote_tlbs(struct kvm *kvm)
 	return 0;
 }
 
+static int realm_flush_remote_tlbs(struct kvm *kvm)
+{
+	/* Nothing to do here, RMM does the job */
+	return 0;
+}
+
 /**
  * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8
  * @kvm:	pointer to kvm structure.
@@ -218,6 +224,13 @@ static int kvm_vm_flush_remote_tlbs_range(struct kvm *kvm,
 	return 0;
 }
 
+static int realm_flush_remote_tlbs_range(struct kvm *kvm,
+					 gfn_t gfn, u64 nr_pages)
+{
+	/* Nothing to do here, RMM does the job */
+	return 0;
+}
+
 int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
 				     gfn_t gfn, u64 nr_pages)
 {
@@ -324,7 +337,8 @@ static int kvm_pgtable_stage2_unmap_tracked(struct kvm_pgtable *pgt,
 
 static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
 				      phys_addr_t start,
-				      u64 size, bool may_block)
+				      u64 size, bool may_block,
+				      enum kvm_gfn_range_filter filter)
 {
 	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
 	int (*fn)(struct kvm_pgtable *, u64, u64);
@@ -339,15 +353,26 @@ static void kvm_vm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
 
 static void pkvm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
 				    phys_addr_t start,
-				    u64 size, bool may_block)
+				    u64 size, bool may_block,
+				    enum kvm_gfn_range_filter filter)
 {
 	WARN_ON(stage2_apply_range(mmu, start, start + size,
 				   pkvm_pgtable_stage2_unmap, may_block));
 }
 
 static void no_stage2_unmap_range(struct kvm_s2_mmu *mmu,
-				  phys_addr_t start, u64 size, bool may_block)
+				  phys_addr_t start, u64 size, bool may_block,
+				  enum kvm_gfn_range_filter filter)
+{
+}
+
+static void realm_stage2_unmap_range(struct kvm_s2_mmu *mmu,
+				     phys_addr_t start, u64 size, bool may_block,
+				     enum kvm_gfn_range_filter filter)
 {
+	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+
+	kvm_realm_unmap_range_filter(kvm, start, size, may_block, filter);
 }
 
 /*
@@ -388,25 +413,36 @@ static int kvm_pgtable_stage2_unmap_tracked(struct kvm_pgtable *pgt, u64 addr, u
 }
 
 /**
- * kvm_stage2_unmap_range -- Clear stage2 page table entries to unmap a range
+ * kvm_stage2_unmap_range_filter -- Clear stage2 page table entries to unmap a
+ * range
  * @mmu:   The KVM stage-2 MMU pointer
  * @start: The intermediate physical base address of the range to unmap
  * @size:  The size of the area to unmap
  * @may_block: Whether or not we are permitted to block
+ * @filter: Filter for the ipa range (private and/or shared)
  *
  * Clear a range of stage-2 mappings, lowering the various ref-counts.  Must
  * be called while holding mmu_lock otherwise another faulting VCPU may
  * come in and mess with things behind our backs.
  */
-void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
-			    u64 size, bool may_block)
+static void kvm_stage2_unmap_range_filter(struct kvm_s2_mmu *mmu, phys_addr_t start,
+					  u64 size, bool may_block,
+					  enum kvm_gfn_range_filter filter)
 {
 	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
 
 	lockdep_assert_held_write(&kvm->mmu_lock);
 	WARN_ON(size & ~PAGE_MASK);
 
-	kvm->arch.vm_s2_ops->vm_stage2_unmap_range(mmu, start, size, may_block);
+	kvm->arch.vm_s2_ops->vm_stage2_unmap_range(mmu, start, size,
+						   may_block, filter);
+}
+
+void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
+			    u64 size, bool may_block)
+{
+	kvm_stage2_unmap_range_filter(mmu, start, size, may_block,
+				      KVM_FILTER_PRIVATE | KVM_FILTER_SHARED);
 }
 
 void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
@@ -420,7 +456,11 @@ static void stage2_flush_memslot(struct kvm *kvm,
 	phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
 	phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
 
-	kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
+	if (kvm_vm_is_realm(kvm))
+		kvm_realm_unmap_range_filter(kvm, addr, end - addr, true,
+					     KVM_FILTER_SHARED);
+	else
+		kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
 }
 
 /**
@@ -967,10 +1007,14 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
 
 static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
 {
+	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
 	u32 kvm_ipa_limit = get_kvm_ipa_limit();
 	u64 mmfr0, mmfr1;
 	u32 phys_shift;
 
+	if (kvm_vm_is_realm(kvm))
+		kvm_ipa_limit = kvm_rmm_ipa_limit();
+
 	phys_shift = KVM_VM_TYPE_ARM_IPA_SIZE(type);
 	if (is_protected_kvm_enabled()) {
 		phys_shift = kvm_ipa_limit;
@@ -1020,9 +1064,18 @@ static void stage2_destroy_range(struct kvm_pgtable *pgt, phys_addr_t addr,
 
 static void kvm_stage2_destroy(struct kvm_pgtable *pgt)
 {
+	struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
 	unsigned int ia_bits = VTCR_EL2_IPA(pgt->mmu->vtcr);
 
-	stage2_destroy_range(pgt, 0, BIT(ia_bits));
+	/*
+	 * Realm RTTs are inaccessible to the host and may contain stale data
+	 * after the RMM has released them. The non-root RTTs are explicitly
+	 * destroyed through RMI before the RD is destroyed; only the root PGD
+	 * pages remain to be freed here.
+	 */
+	if (!kvm_vm_is_realm(kvm))
+		stage2_destroy_range(pgt, 0, BIT(ia_bits));
+
 	KVM_PGT_FN(kvm_pgtable_stage2_destroy_pgd)(pgt);
 }
 
@@ -1070,6 +1123,8 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
 		return -EINVAL;
 	}
 
+	mmu->arch = &kvm->arch;
+
 	err = kvm_init_ipa_range(mmu, type);
 	if (err)
 		return err;
@@ -1078,7 +1133,6 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
 	if (!pgt)
 		return -ENOMEM;
 
-	mmu->arch = &kvm->arch;
 	err = KVM_PGT_FN(kvm_pgtable_stage2_init)(pgt, mmu, &kvm_s2_mm_ops);
 	if (err)
 		goto out_free_pgtable;
@@ -1117,9 +1171,24 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
 	return err;
 }
 
+static void kvm_realm_uninit_stage2(struct kvm_s2_mmu *mmu)
+{
+	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+
+	mutex_lock(&kvm->arch.config_lock);
+	if (kvm_realm_state(kvm) == REALM_STATE_ACTIVE) {
+		kvm_set_realm_state(kvm, REALM_STATE_DYING);
+		WARN_ON(kvm_realm_teardown_stage2(kvm));
+	}
+	mutex_unlock(&kvm->arch.config_lock);
+}
+
 void kvm_uninit_stage2_mmu(struct kvm *kvm)
 {
-	kvm_free_stage2_pgd(&kvm->arch.mmu);
+	if (kvm_vm_is_realm(kvm))
+		kvm_realm_uninit_stage2(&kvm->arch.mmu);
+	else
+		kvm_free_stage2_pgd(&kvm->arch.mmu);
 	kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache);
 }
 
@@ -1178,6 +1247,10 @@ void stage2_unmap_vm(struct kvm *kvm)
 	struct kvm_memory_slot *memslot;
 	int idx, bkt;
 
+	/* For realms this is handled by the RMM so nothing to do here */
+	if (kvm_vm_is_realm(kvm))
+		return;
+
 	idx = srcu_read_lock(&kvm->srcu);
 	mmap_read_lock(current->mm);
 	write_lock(&kvm->mmu_lock);
@@ -1747,6 +1820,10 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,
 	void *memcache = NULL;
 	gfn_t canonical_gfn;
 	int ret;
+	bool is_realm_private = false;
+
+	if (KVM_BUG_ON(!kvm_slot_has_gmem(s2fd->memslot), kvm))
+		return -EINVAL;
 
 	if (!perm_fault) {
 		memcache = get_mmu_memcache(s2fd->vcpu);
@@ -1761,7 +1838,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,
 	}
 
 	s2vi.vma_pagesize = PAGE_SIZE;
-	s2vi.gfn = ALIGN_DOWN(s2fd->fault_ipa, s2vi.vma_pagesize) >> PAGE_SHIFT;
+	s2vi.gfn = gpa_to_gfn(kvm_gpa_from_fault(kvm, s2fd->fault_ipa));
 	canonical_gfn = get_canonical_gfn(s2fd, &s2vi);
 
 	write_fault = kvm_s2_fault_is_write(s2fd);
@@ -1773,13 +1850,28 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,
 	/* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */
 	smp_rmb();
 
+	if (kvm_vm_is_realm(kvm)) {
+		is_realm_private = kvm_realm_is_private_address(&kvm->arch.realm,
+								s2fd->fault_ipa);
+
+		if (is_realm_private != kvm_gmem_is_private_gfn(kvm, canonical_gfn)) {
+			kvm_prepare_memory_fault_exit(s2fd->vcpu,
+						      gfn_to_gpa(canonical_gfn),
+						      PAGE_SIZE,
+						      write_fault, exec_fault,
+						      is_realm_private);
+			kfree(mapping);
+			return -EFAULT;
+		}
+	}
+
 	ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, canonical_gfn, &s2vi.pfn, NULL);
 	if (ret) {
 		/* If result is non-NULL this is a synthetic fault. */
 		if (!result)
 			kvm_prepare_memory_fault_exit(s2fd->vcpu, gfn_to_gpa(canonical_gfn),
 						      s2vi.vma_pagesize, write_fault, exec_fault,
-						      false);
+						      is_realm_private);
 		kfree(mapping);
 		return ret;
 	}
@@ -1802,6 +1894,13 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,
 		goto out_unlock;
 	}
 
+	if (kvm_vm_is_realm(kvm)) {
+		prot &= ~KVM_PGTABLE_PROT_X;
+		ret = realm_map_ipa(kvm, s2fd->fault_ipa, s2vi.pfn,
+				    PAGE_SIZE, prot, memcache);
+		goto out_unlock;
+	}
+
 	if (perm_fault) {
 		/*
 		 * Drop the SW bits in favour of those stored in the
@@ -2003,7 +2102,8 @@ static int kvm_s2_fault_get_vma_info(const struct kvm_s2_fault_desc *s2fd,
 	 * mapping size to ensure we find the right PFN and lay down the
 	 * mapping in the right place.
 	 */
-	s2vi->gfn = ALIGN_DOWN(s2fd->fault_ipa, s2vi->vma_pagesize) >> PAGE_SHIFT;
+	s2vi->gfn = kvm_gfn_from_fault(kvm, ALIGN_DOWN(s2fd->fault_ipa,
+						       s2vi->vma_pagesize));
 
 	s2vi->mte_allowed = kvm_vma_mte_allowed(vma);
 
@@ -2198,12 +2298,15 @@ static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd,
 	if (!perm_fault_granule && !s2vi->map_non_cacheable && kvm_has_mte(kvm))
 		sanitise_mte_tags(kvm, pfn, mapping_size);
 
-	/*
-	 * Under the premise of getting a FSC_PERM fault, we just need to relax
-	 * permissions only if mapping_size equals perm_fault_granule. Otherwise,
-	 * kvm_pgtable_stage2_map() should be called to change block size.
-	 */
-	if (mapping_size == perm_fault_granule) {
+	if (kvm_vm_is_realm(kvm)) {
+		ret = realm_map_ipa(kvm, s2fd->fault_ipa, pfn, mapping_size,
+				    prot, memcache);
+	} else if (mapping_size == perm_fault_granule) {
+		/*
+		 * Under the premise of getting a FSC_PERM fault, we just need to relax
+		 * permissions only if mapping_size equals perm_fault_granule. Otherwise,
+		 * kvm_pgtable_stage2_map() should be called to change block size.
+		 */
 		/*
 		 * Drop the SW bits in favour of those stored in the
 		 * PTE, which will be preserved.
@@ -2296,18 +2399,43 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd,
 	return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache, result);
 }
 
+static bool private_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa);
+static bool kvm_fault_is_gmem_abort(struct kvm *kvm,
+				    const struct kvm_s2_fault_desc *s2fd);
+
 static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
 {
 	VM_WARN_ON_ONCE(kvm_s2_fault_is_perm(s2fd) &&
 			!esr_abt_is_write_fault(s2fd->esr) &&
 			!kvm_s2_fault_is_exec(s2fd));
 
-	if (kvm_slot_has_gmem(s2fd->memslot))
+	if (private_ipa_fault(s2fd->vcpu->kvm, s2fd->fault_ipa) &&
+	    !kvm_slot_has_gmem(s2fd->memslot))
+		return -EINVAL;
+
+	if (kvm_fault_is_gmem_abort(s2fd->vcpu->kvm, s2fd))
 		return gmem_abort(s2fd, NULL);
 	else
 		return user_mem_abort(s2fd, NULL);
 }
 
+/* Decide if an abort should be handled by the gmem */
+static bool kvm_fault_is_gmem_abort(struct kvm *kvm,
+				    const struct kvm_s2_fault_desc *s2fd)
+{
+	if (!kvm_slot_has_gmem(s2fd->memslot))
+		return false;
+	if (kvm_memslot_is_gmem_only(s2fd->memslot))
+		return true;
+	/*
+	 * For Realms, all private faults must be backed by GMEM.
+	 * TODO: Handle Trusted device private memory mappings.
+	 */
+	if (private_ipa_fault(kvm, s2fd->fault_ipa))
+		return true;
+	return false;
+}
+
 /* Resolve the access fault by making the page young again. */
 static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
 {
@@ -2401,6 +2529,12 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
+static bool private_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa)
+{
+	return kvm_vm_is_realm(kvm) &&
+		kvm_realm_is_private_address(&kvm->arch.realm, fault_ipa);
+}
+
 /**
  * kvm_handle_guest_abort - handles all 2nd stage aborts
  * @vcpu:	the VCPU pointer
@@ -2414,10 +2548,10 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
  */
 int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 {
+	struct kvm *kvm = vcpu->kvm;
 	struct kvm_s2_trans nested_trans, *nested = NULL;
 	unsigned long esr = kvm_vcpu_get_esr(vcpu);
 	struct kvm_s2_mmu *mmu = vcpu->arch.hw_mmu;
-	struct kvm *kvm = vcpu->kvm;
 	phys_addr_t fault_ipa; /* The address we faulted on */
 	phys_addr_t ipa; /* Always the IPA in the L1 guest phys space */
 	struct kvm_memory_slot *memslot;
@@ -2510,8 +2644,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		nested = &nested_trans;
 	}
 
-	gfn = ipa >> PAGE_SHIFT;
+	gfn = kvm_gfn_from_fault(kvm, ipa);
 	memslot = gfn_to_memslot(kvm, gfn);
+
 	hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
 	write_fault = esr_abt_is_write_fault(esr);
 	if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
@@ -2554,7 +2689,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
 		 * of the page size.
 		 */
 		ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
-		ret = io_mem_abort(vcpu, ipa);
+		ret = io_mem_abort(vcpu, kvm_gpa_from_fault(kvm, ipa));
 		goto out_unlock;
 	}
 
@@ -2597,7 +2732,8 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
 	if (!kvm->arch.mmu.pgt)
 		return false;
 
-	kvm_stage2_unmap_range(&kvm->arch.mmu, gpa, size, may_block);
+	kvm_stage2_unmap_range_filter(&kvm->arch.mmu, gpa, size, may_block,
+				      range->attr_filter);
 	kvm_nested_unmap_cipa_range(kvm, gpa, size, may_block);
 
 	return false;
@@ -2812,19 +2948,35 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
 				   enum kvm_mr_change change)
 {
 	hva_t hva, reg_end;
+	gfn_t ipa_limit;
 	int ret = 0;
 
-	if (kvm_vm_is_protected(kvm)) {
+	if (kvm_vm_is_protected(kvm) && new &&
+	    (new->flags & (KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY)))
+		return -EPERM;
+
+	switch (kvm->arch.vm_flavor) {
+	case VM_PROTECTED_PKVM:
 		/* Cannot modify memslots once a pVM has run. */
 		if (pkvm_hyp_vm_is_created(kvm) &&
-		    (change == KVM_MR_DELETE || change == KVM_MR_MOVE)) {
+		    (change == KVM_MR_DELETE || change == KVM_MR_MOVE))
 			return -EPERM;
-		}
+		break;
+	case VM_REALM:
+		/*
+		 * Once the Realm is created, we cannot modify any slots that
+		 * could be providing private memory. i.e., guest_memfd backed
+		 * slots.
+		 * TODO: Handle trusted device private memory slots
+		 */
+		if (!kvm_realm_is_created(kvm))
+			break;
 
-		if (new &&
-		    new->flags & (KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY)) {
+		if (kvm_slot_has_gmem(old) || kvm_slot_has_gmem(new))
 			return -EPERM;
-		}
+		break;
+	default:
+		break;
 	}
 
 	if (change != KVM_MR_CREATE && change != KVM_MR_MOVE &&
@@ -2832,17 +2984,21 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
 		return 0;
 
 	/*
-	 * Prevent userspace from creating a memory region outside of the IPA
-	 * space addressable by the KVM guest IPA space.
+	 * Realm shared IPAs alias the corresponding private IPAs, so memslots
+	 * must reside entirely in the canonical lower half of the IPA space.
 	 */
-	if ((new->base_gfn + new->npages) > (kvm_phys_size(&kvm->arch.mmu) >> PAGE_SHIFT))
+	ipa_limit = kvm_phys_size(&kvm->arch.mmu) >> PAGE_SHIFT;
+	if (kvm_vm_is_realm(kvm))
+		ipa_limit >>= 1;
+	if (new->base_gfn + new->npages > ipa_limit)
 		return -EFAULT;
 
 	/*
-	 * Only support guest_memfd backed memslots with mappable memory, since
-	 * there aren't any CoCo VMs that support only private memory on arm64.
+	 * Only support guest_memfd backed memslots with mappable memory,
+	 * unless the guest is a CCA realm guest.
 	 */
-	if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new))
+	if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new) &&
+	    !kvm_vm_is_realm(kvm))
 		return -EINVAL;
 
 	/* guest_memfd is incompatible with MTE. */
@@ -3157,14 +3313,24 @@ static const struct kvm_vm_s2_ops kvm_default_vm_s2_ops = {
 	.vm_mem_abort			= kvm_vm_mem_abort,
 };
 
+static const struct kvm_vm_s2_ops realm_vm_s2_ops = {
+	.vm_flush_remote_tlbs		= realm_flush_remote_tlbs,
+	.vm_flush_remote_tlbs_range	= realm_flush_remote_tlbs_range,
+	.vm_age_gfn			= no_age_gfn,
+	.vm_test_age_gfn		= no_age_gfn,
+	.vm_stage2_unmap_range		= realm_stage2_unmap_range,
+	.vm_mem_abort			= kvm_vm_mem_abort,
+};
+
 #define KVM_VM_S2_OPS(flavor, 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)
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
new file mode 100644
index 00000000000000..f89460d8adcba9
--- /dev/null
+++ b/arch/arm64/kvm/rmi.c
@@ -0,0 +1,852 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2026 ARM Ltd.
+ */
+
+#include <linux/arm-rmi-cmds.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_mmu.h>
+#include <asm/kvm_pgtable.h>
+#include <asm/virt.h>
+
+static inline unsigned long rmi_rtt_level_mapsize(int level)
+{
+	if (WARN_ON(level > KVM_PGTABLE_LAST_LEVEL))
+		return PAGE_SIZE;
+
+	return (1UL << ARM64_HW_PGTABLE_LEVEL_SHIFT(level));
+}
+
+static bool rmi_has_feature(int reg, unsigned long feature)
+{
+	return !!u64_get_bits(rmi_feat_reg(reg), feature);
+}
+
+u32 kvm_rmm_ipa_limit(void)
+{
+	return u64_get_bits(rmi_feat_reg(0), RMI_FEATURE_REGISTER_0_S2SZ);
+}
+
+static int get_start_level(struct realm *realm)
+{
+	return 4 - stage2_pgtable_levels(realm->ia_bits);
+}
+
+static int find_map_level(struct realm *realm,
+			  unsigned long start,
+			  unsigned long end)
+{
+	int level = KVM_PGTABLE_LAST_LEVEL;
+
+	while (level > get_start_level(realm)) {
+		unsigned long map_size = rmi_rtt_level_mapsize(level - 1);
+
+		if (!IS_ALIGNED(start, map_size) ||
+		    (start + map_size) > end)
+			break;
+
+		level--;
+	}
+
+	return level;
+}
+
+static unsigned long rmi_range_entry_size(int rmi_size)
+{
+	if (WARN_ON(rmi_size > KVM_PGTABLE_LAST_LEVEL))
+		return 0;
+
+	return kvm_granule_size(KVM_PGTABLE_LAST_LEVEL - rmi_size);
+}
+
+static int undelegate_range_desc(unsigned long desc)
+{
+	unsigned long size = rmi_range_entry_size(RMI_ADDR_RANGE_BLOCK_SIZE(desc));
+	unsigned long count = RMI_ADDR_RANGE_COUNT(desc);
+	unsigned long addr = RMI_ADDR_RANGE_ADDR(desc);
+	unsigned long state = RMI_ADDR_RANGE_STATE(desc);
+
+	if (state == RMI_OP_MEM_UNDELEGATED)
+		return 0;
+
+	if (size * count == 0)
+		return 0;
+
+	return rmi_undelegate_range(addr, size * count);
+}
+
+static phys_addr_t alloc_delegated_granule(struct kvm_mmu_memory_cache *mc)
+{
+	phys_addr_t phys;
+	void *virt;
+
+	if (mc) {
+		virt = kvm_mmu_memory_cache_alloc(mc);
+	} else {
+		virt = (void *)__get_free_page(GFP_ATOMIC | __GFP_ZERO |
+					       __GFP_ACCOUNT);
+	}
+
+	if (!virt)
+		return PHYS_ADDR_MAX;
+
+	phys = virt_to_phys(virt);
+	if (rmi_delegate_page(phys)) {
+		free_page((unsigned long)virt);
+		return PHYS_ADDR_MAX;
+	}
+
+	return phys;
+}
+
+static phys_addr_t alloc_rtt(struct kvm_mmu_memory_cache *mc)
+{
+	phys_addr_t phys = alloc_delegated_granule(mc);
+
+	if (phys != PHYS_ADDR_MAX)
+		kvm_account_pgtable_pages(phys_to_virt(phys), 1);
+
+	return phys;
+}
+
+static void free_rtt(phys_addr_t phys)
+{
+	if (rmi_free_delegated_page(phys))
+		return;
+
+	kvm_account_pgtable_pages(phys_to_virt(phys), -1);
+}
+
+static long realm_rtt_create(struct realm *realm,
+			     unsigned long addr,
+			     int level,
+			     phys_addr_t phys)
+{
+	addr = ALIGN_DOWN(addr, rmi_rtt_level_mapsize(level - 1));
+	return rmi_rtt_create(virt_to_phys(realm->rd), phys, addr, level);
+}
+
+static long realm_rtt_fold(struct realm *realm,
+			   unsigned long addr,
+			   int level,
+			   phys_addr_t *rtt_granule)
+{
+	unsigned long out_rtt;
+	long ret;
+
+	addr = ALIGN_DOWN(addr, rmi_rtt_level_mapsize(level - 1));
+	ret = rmi_rtt_fold(virt_to_phys(realm->rd), addr, level, &out_rtt);
+
+	if (rtt_granule)
+		*rtt_granule = out_rtt;
+
+	return ret;
+}
+
+/*
+ * realm_rtt_destroy - Destroy an RTT at @level for @addr.
+ *
+ * Returns - Result of the RMI_RTT_DESTROY call, and:
+ * @rtt_granule:	RTT granule, if the RTT was destroyed.
+ * @next_addr:		IPA corresponding to the next possible valid entry we
+ *			can target
+ */
+static long realm_rtt_destroy(struct realm *realm, unsigned long addr,
+			      int level, phys_addr_t *rtt_granule,
+			      unsigned long *next_addr)
+{
+	unsigned long out_rtt;
+	long ret;
+
+	ret = rmi_rtt_destroy(virt_to_phys(realm->rd), addr, level,
+			      &out_rtt, next_addr);
+
+	*rtt_granule = out_rtt;
+
+	return ret;
+}
+
+static int realm_create_rtt_levels(struct realm *realm,
+				   unsigned long ipa,
+				   int level,
+				   int max_level,
+				   struct kvm_mmu_memory_cache *mc)
+{
+	while (level++ < max_level) {
+		phys_addr_t rtt = alloc_rtt(mc);
+		long ret;
+
+		if (rtt == PHYS_ADDR_MAX)
+			return -ENOMEM;
+
+		ret = realm_rtt_create(realm, ipa, level, rtt);
+		if (ret < 0) {
+			free_rtt(rtt);
+			return ret;
+		}
+
+		if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT &&
+		    RMI_RESULT_DATA_LEVEL(ret) == level - 1) {
+			/* The RTT already exists, continue */
+			free_rtt(rtt);
+			continue;
+		}
+
+		if (ret) {
+			WARN(1, "Failed to create RTT at level %d: %ld\n",
+			     level, ret);
+			free_rtt(rtt);
+			return -ENXIO;
+		}
+	}
+
+	return 0;
+}
+
+static int realm_tear_down_rtt_level(struct realm *realm, int level,
+				     unsigned long start, unsigned long end)
+{
+	ssize_t map_size;
+	unsigned long addr, next_addr;
+
+	if (WARN_ON(level > KVM_PGTABLE_LAST_LEVEL))
+		return -EINVAL;
+
+	map_size = rmi_rtt_level_mapsize(level - 1);
+
+	for (addr = start; addr < end; addr = next_addr) {
+		phys_addr_t rtt_granule;
+		long ret;
+		unsigned long align_addr = ALIGN(addr, map_size);
+
+		next_addr = ALIGN(addr + 1, map_size);
+
+		if (next_addr > end || align_addr != addr) {
+			/*
+			 * The target range is smaller than what this level
+			 * covers, recurse deeper.
+			 */
+			ret = realm_tear_down_rtt_level(realm,
+							level + 1,
+							addr,
+							min(next_addr, end));
+			if (ret)
+				return ret;
+			continue;
+		}
+
+		ret = realm_rtt_destroy(realm, addr, level,
+					&rtt_granule, &next_addr);
+		if (ret < 0)
+			return ret;
+
+		switch (RMI_RESULT_STATUS(ret)) {
+		case RMI_SUCCESS:
+			free_rtt(rtt_granule);
+			break;
+		case RMI_ERROR_RTT:
+			if (next_addr > addr) {
+				/* Missing RTT, skip */
+				break;
+			}
+			/*
+			 * We tear down the RTT range for the full IPA
+			 * space, after everything is unmapped. Also we
+			 * descend down only if we cannot tear down a
+			 * top level RTT. Thus RMM must be able to walk
+			 * to the requested level. e.g., a block mapping
+			 * exists at L1 or L2.
+			 */
+			if (WARN_ON(RMI_RESULT_DATA_LEVEL(ret) != level))
+				return -EBUSY;
+			if (WARN_ON(level == KVM_PGTABLE_LAST_LEVEL))
+				return -EBUSY;
+
+			/*
+			 * The table has active entries in it, recurse deeper
+			 * and tear down the RTTs.
+			 */
+			next_addr = ALIGN(addr + 1, map_size);
+			ret = realm_tear_down_rtt_level(realm,
+							level + 1,
+							addr,
+							next_addr);
+			if (ret)
+				return ret;
+			/*
+			 * Now that the child RTTs are destroyed,
+			 * retry at this level.
+			 */
+			next_addr = addr;
+			break;
+		default:
+			WARN_ON(1);
+			return -ENXIO;
+		}
+	}
+
+	return 0;
+}
+
+static int realm_tear_down_rtt_range(struct realm *realm,
+				     unsigned long start, unsigned long end)
+{
+	/*
+	 * Root level RTTs can only be destroyed after the RD is destroyed. So
+	 * tear down everything below the root level
+	 */
+	return realm_tear_down_rtt_level(realm, get_start_level(realm) + 1,
+					 start, end);
+}
+
+/*
+ * Returns 0 on successful fold, a negative value on error, a positive value if
+ * we were not able to fold all tables at this level.
+ */
+static int realm_fold_rtt_level(struct realm *realm, int level,
+				unsigned long start, unsigned long end)
+{
+	int not_folded = 0;
+	ssize_t map_size;
+	unsigned long addr, next_addr;
+
+	if (WARN_ON(level > KVM_PGTABLE_LAST_LEVEL))
+		return -EINVAL;
+
+	map_size = rmi_rtt_level_mapsize(level - 1);
+
+	for (addr = start; addr < end; addr = next_addr) {
+		phys_addr_t rtt_granule;
+		long ret;
+		unsigned long align_addr = ALIGN(addr, map_size);
+
+		next_addr = ALIGN(addr + 1, map_size);
+
+		ret = realm_rtt_fold(realm, align_addr, level, &rtt_granule);
+		if (ret < 0)
+			return ret;
+
+		switch (RMI_RESULT_STATUS(ret)) {
+		case RMI_SUCCESS:
+			free_rtt(rtt_granule);
+			break;
+		case RMI_ERROR_RTT:
+			if (level == KVM_PGTABLE_LAST_LEVEL ||
+			    RMI_RESULT_DATA_LEVEL(ret) < level) {
+				not_folded++;
+				break;
+			}
+			/* Recurse a level deeper */
+			ret = realm_fold_rtt_level(realm,
+						   level + 1,
+						   addr,
+						   next_addr);
+			if (ret < 0) {
+				return ret;
+			} else if (ret == 0) {
+				/* Try again at this level */
+				next_addr = addr;
+			}
+			break;
+		default:
+			WARN_ON(1);
+			return -ENXIO;
+		}
+	}
+
+	return not_folded;
+}
+
+static int realm_destroy_rtts(struct kvm *kvm)
+{
+	struct realm *realm = &kvm->arch.realm;
+	unsigned int ia_bits = realm->ia_bits;
+
+	lockdep_assert_held(&kvm->arch.config_lock);
+
+	return realm_tear_down_rtt_range(realm, 0, (1UL << ia_bits));
+}
+
+static void realm_unmap_stage2(struct kvm *kvm)
+{
+	struct realm *realm = &kvm->arch.realm;
+
+	lockdep_assert_held(&kvm->arch.config_lock);
+
+	if (realm->stage2_unmapped)
+		return;
+
+	write_lock(&kvm->mmu_lock);
+	kvm_stage2_unmap_range(&kvm->arch.mmu, 0,
+			       BIT(realm->ia_bits - 1), true);
+	write_unlock(&kvm->mmu_lock);
+
+	realm->stage2_unmapped = true;
+}
+
+int kvm_realm_teardown_stage2(struct kvm *kvm)
+{
+	lockdep_assert_held(&kvm->arch.config_lock);
+
+	realm_unmap_stage2(kvm);
+	return 0;
+}
+
+static void realm_unmap_shared_range(struct kvm *kvm,
+				     unsigned long start,
+				     unsigned long end,
+				     bool may_block)
+{
+	struct realm *realm = &kvm->arch.realm;
+	unsigned long rd = virt_to_phys(realm->rd);
+	unsigned long next_addr, addr;
+	unsigned long shared_bit = BIT(realm->ia_bits - 1);
+
+	if (start >= end)
+		return;
+
+	/*
+	 * Callers may pass either an IPA range or its shared alias. Add the
+	 * shared bit only for the former. Use addition so an exclusive end at
+	 * shared_bit advances to the end of the IPA space.
+	 */
+	if (start < shared_bit) {
+		start += shared_bit;
+		end += shared_bit;
+	}
+
+	for (addr = start; addr < end; addr = next_addr) {
+		long ret;
+
+		ret = rmi_rtt_unprot_unmap(rd, addr, end, RMI_ADDR_TYPE_NONE,
+					   0, &next_addr, NULL, NULL);
+		if (WARN_ON(ret < 0))
+			return;
+
+		switch (RMI_RESULT_STATUS(ret)) {
+		case RMI_SUCCESS:
+			break;
+		case RMI_ERROR_RTT: {
+			int err_level = RMI_RESULT_DATA_LEVEL(ret);
+			int level = find_map_level(realm, addr, end);
+
+			if (err_level >= level) {
+				/* Nothing present, so skip */
+				next_addr = addr + rmi_rtt_level_mapsize(err_level);
+				break;
+			}
+
+			ret = realm_create_rtt_levels(realm, addr, err_level,
+						      level, NULL);
+			if (WARN_ON(ret))
+				return;
+			/* Retry with the RTT levels in place */
+			next_addr = addr;
+			break;
+		}
+		default:
+			WARN_ON(1);
+			return;
+		}
+
+		if (may_block)
+			cond_resched_rwlock_write(&kvm->mmu_lock);
+	}
+
+	realm_fold_rtt_level(realm, get_start_level(realm) + 1,
+			     start, end);
+}
+
+static void realm_unmap_private_range(struct kvm *kvm,
+				      unsigned long start,
+				      unsigned long end,
+				      bool may_block)
+{
+	struct realm *realm = &kvm->arch.realm;
+	unsigned long rd = virt_to_phys(realm->rd);
+	unsigned long next_addr, addr;
+	long ret;
+
+	for (addr = start; addr < end; addr = next_addr) {
+		unsigned long out_range;
+		unsigned long flags = RMI_ADDR_TYPE_SINGLE;
+		/* TODO: Optimise using RMI_ADDR_TYPE_LIST */
+
+retry:
+		ret = rmi_rtt_data_unmap(rd, addr, end, flags, 0,
+					 &next_addr, &out_range, NULL);
+		if (WARN_ON(ret < 0))
+			return;
+
+		if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {
+			phys_addr_t rtt;
+
+			if (next_addr > addr)
+				continue; /* UNASSIGNED */
+
+			rtt = alloc_rtt(NULL);
+			if (WARN_ON(rtt == PHYS_ADDR_MAX))
+				return;
+			ret = realm_rtt_create(realm, addr,
+					       RMI_RESULT_DATA_LEVEL(ret) + 1, rtt);
+			if (WARN_ON(ret)) {
+				free_rtt(rtt);
+				return;
+			}
+			goto retry;
+		} else if (WARN_ON(ret)) {
+			continue;
+		}
+
+		ret = undelegate_range_desc(out_range);
+		if (WARN_ON(ret))
+			break;
+
+		if (may_block)
+			cond_resched_rwlock_write(&kvm->mmu_lock);
+	}
+
+	realm_fold_rtt_level(realm, get_start_level(realm) + 1,
+			     start, end);
+}
+
+void kvm_realm_unmap_range_filter(struct kvm *kvm, unsigned long start,
+				  unsigned long size, bool may_block,
+				  enum kvm_gfn_range_filter attr_filter)
+{
+	unsigned long end = start + size;
+	struct realm *realm = &kvm->arch.realm;
+
+	if (!kvm_realm_is_created(kvm))
+		return;
+
+	end = min(BIT(realm->ia_bits - 1), end);
+
+	if (attr_filter & KVM_FILTER_SHARED)
+		realm_unmap_shared_range(kvm, start, end, may_block);
+	if (attr_filter & KVM_FILTER_PRIVATE)
+		realm_unmap_private_range(kvm, start, end, may_block);
+}
+
+void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start,
+			   unsigned long size, bool may_block)
+{
+	kvm_realm_unmap_range_filter(kvm, start, size, may_block,
+				     KVM_FILTER_SHARED | KVM_FILTER_PRIVATE);
+}
+
+static unsigned long addr_range_desc(unsigned long phys, unsigned long size,
+				     unsigned long state)
+{
+	unsigned long count = 1;
+	unsigned long out;
+	int rmi_size;
+
+	for (rmi_size = KVM_PGTABLE_LAST_LEVEL; rmi_size >= 0; rmi_size--) {
+		unsigned long entry_size = rmi_range_entry_size(rmi_size);
+
+		if (size == entry_size)
+			break;
+		if (size > entry_size) {
+			rmi_size = -1;
+			break;
+		}
+	}
+
+	if (rmi_size < 0) {
+		/*
+		 * Only support mapping at the page level granularity when
+		 * it's an unusual length. This should get us back onto a larger
+		 * block size for the subsequent mappings.
+		 */
+		rmi_size = 0;
+		count = MIN(size >> PAGE_SHIFT, PTRS_PER_PTE - 1);
+	}
+
+	WARN_ON(phys & ~PAGE_MASK);
+
+	out = FIELD_PREP(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, rmi_size) |
+	      FIELD_PREP(RMI_ADDR_RANGE_COUNT_MASK, count) |
+	      FIELD_PREP(RMI_ADDR_RANGE_STATE_MASK, state);
+	out |= phys & PAGE_MASK;
+
+	return out;
+}
+
+static int realm_map_protected(struct kvm *kvm,
+			       unsigned long ipa,
+			       kvm_pfn_t pfn,
+			       unsigned long map_size,
+			       struct kvm_mmu_memory_cache *memcache)
+{
+	struct realm *realm = &kvm->arch.realm;
+	phys_addr_t phys = __pfn_to_phys(pfn);
+	phys_addr_t base_phys = phys;
+	phys_addr_t rd = virt_to_phys(realm->rd);
+	phys_addr_t delegated_phys;
+	unsigned long base_ipa = ipa;
+	unsigned long ipa_top;
+	long ret = 0;
+
+	if (WARN_ON(!IS_ALIGNED(map_size, PAGE_SIZE) ||
+		    !IS_ALIGNED(ipa, map_size)))
+		return -EINVAL;
+
+	if (rmi_delegate_range(phys, map_size, &delegated_phys)) {
+		if (delegated_phys == phys) {
+			/*
+			 * It's likely we raced with another VCPU on the same
+			 * fault. Assume the other VCPU has handled the fault
+			 * and return to the guest.
+			 */
+			return 0;
+		}
+		/* Partial delegation - map as much as we can */
+		map_size = delegated_phys - phys;
+	}
+
+	ipa_top = ipa + map_size;
+
+	while (ipa < ipa_top) {
+		unsigned long flags = RMI_ADDR_TYPE_SINGLE;
+		unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa,
+							   RMI_OP_MEM_DELEGATED);
+		unsigned long out_top;
+
+		ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags, range_desc,
+				       &out_top);
+		if (ret < 0)
+			goto err_undelegate;
+
+		if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {
+			/* Create missing RTTs and retry */
+			int level = RMI_RESULT_DATA_LEVEL(ret);
+
+			if (WARN_ON(level >= KVM_PGTABLE_LAST_LEVEL))
+				goto err_undelegate;
+			ret = realm_create_rtt_levels(realm, ipa, level,
+						      level + 1,
+						      memcache);
+			if (ret)
+				goto err_undelegate;
+
+			continue;
+		}
+
+		if (WARN_ON(ret))
+			goto err_undelegate;
+
+		phys += out_top - ipa;
+		ipa = out_top;
+	}
+
+	return 0;
+
+err_undelegate:
+	realm_unmap_private_range(kvm, base_ipa, ipa, true);
+	/* A range which cannot be undelegated remains owned by the RMM. */
+	WARN_ON(rmi_undelegate_range(base_phys, map_size));
+	return ret < 0 ? ret : -ENXIO;
+}
+
+static int realm_map_non_secure(struct kvm *kvm,
+				unsigned long ipa,
+				kvm_pfn_t pfn,
+				unsigned long size,
+				enum kvm_pgtable_prot prot,
+				struct kvm_mmu_memory_cache *memcache)
+{
+	struct realm *realm = &kvm->arch.realm;
+	unsigned long attr, flags = 0;
+	phys_addr_t rd = virt_to_phys(realm->rd);
+	phys_addr_t phys = __pfn_to_phys(pfn);
+	unsigned long ipa_top = ipa + size;
+	long ret;
+
+	if (WARN_ON(!IS_ALIGNED(size, PAGE_SIZE)))
+		return -EINVAL;
+
+	switch (prot & (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC)) {
+	case KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC:
+		return -EINVAL;
+	case KVM_PGTABLE_PROT_DEVICE:
+		attr = MT_S2_FWB_DEVICE_nGnRE;
+		break;
+	case KVM_PGTABLE_PROT_NORMAL_NC:
+		attr = MT_S2_FWB_NORMAL_NC;
+		break;
+	default:
+		attr = MT_S2_FWB_NORMAL;
+	}
+
+	flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR_MASK, attr);
+
+	if (prot & KVM_PGTABLE_PROT_R)
+		flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK, RMI_S2AP_DIRECT_READ);
+	if (prot & KVM_PGTABLE_PROT_W)
+		flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK, RMI_S2AP_DIRECT_WRITE);
+
+	flags |= RMI_ADDR_TYPE_SINGLE;
+
+	while (ipa < ipa_top) {
+		unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa,
+							   RMI_OP_MEM_UNDELEGATED);
+		unsigned long out_top;
+
+		ret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags, range_desc,
+					 &out_top);
+		if (ret < 0)
+			return ret;
+
+		if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {
+			/* Create missing RTTs and retry */
+			int level = RMI_RESULT_DATA_LEVEL(ret);
+			int req_level = find_map_level(realm, ipa, ipa_top);
+
+			/*
+			 * There already exists a mapping at the level. May be
+			 * we are relaxing a permission for the given range ?
+			 */
+			if (level >= req_level) {
+				realm_unmap_shared_range(kvm, ipa, ipa_top, true);
+				continue;
+			}
+
+			ret = realm_create_rtt_levels(realm, ipa, level,
+						      req_level,
+						      memcache);
+			if (ret)
+				return ret;
+
+			ret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags,
+						 range_desc, &out_top);
+			if (ret < 0)
+				return ret;
+		}
+
+		if (ret && RMI_RESULT_STATUS(ret) != RMI_ERROR_RTT)
+			return ret;
+
+		phys += out_top - ipa;
+		ipa = out_top;
+	}
+
+	return 0;
+}
+
+int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,
+		  kvm_pfn_t pfn, unsigned long map_size,
+		  enum kvm_pgtable_prot prot,
+		  struct kvm_mmu_memory_cache *memcache)
+{
+	struct realm *realm = &kvm->arch.realm;
+
+	if (kvm_realm_state(kvm) != REALM_STATE_ACTIVE)
+		return -EBUSY;
+
+	ipa = ALIGN_DOWN(ipa, map_size);
+	if (!kvm_realm_is_private_address(realm, ipa)) {
+		return realm_map_non_secure(kvm, ipa, pfn, map_size, prot,
+					    memcache);
+	}
+
+	/* It's impossible to map protected pages read-only. */
+	if (WARN_ON(!(prot & KVM_PGTABLE_PROT_W)))
+		return -EFAULT;
+	return realm_map_protected(kvm, ipa, pfn, map_size, memcache);
+}
+
+void kvm_destroy_realm(struct kvm *kvm)
+{
+	struct realm *realm = &kvm->arch.realm;
+	size_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm->arch.mmu.vtcr);
+
+	guard(mutex)(&kvm->arch.config_lock);
+
+	if (!kvm_realm_is_created(kvm)) {
+		kfree(realm->sro);
+		realm->sro = NULL;
+		return;
+	}
+
+	kvm_set_realm_state(kvm, REALM_STATE_DYING);
+
+	/*
+	 * REALM_DESTROY requires the realm to be non-live: all RECs must have
+	 * been destroyed and the root RTTs must be empty. Unmap the IPA space
+	 * and destroy any non-root RTTs before tearing down the RD. The root
+	 * RTT pages are still owned by the RMM at this point, so keep the KVM
+	 * pgtable alive until after REALM_DESTROY and undelegation.
+	 */
+	realm_unmap_stage2(kvm);
+
+	if (realm->rd) {
+		phys_addr_t rd_phys = virt_to_phys(realm->rd);
+
+		if (WARN_ON(rmi_realm_terminate(rd_phys, realm->sro)))
+			return;
+
+		if (WARN_ON(realm_destroy_rtts(kvm)))
+			return;
+
+		if (WARN_ON(rmi_realm_destroy(rd_phys, realm->sro)))
+			return;
+		rmi_free_delegated_page(rd_phys);
+		realm->rd = NULL;
+	}
+
+	if (WARN_ON(rmi_undelegate_range(kvm->arch.mmu.pgd_phys,
+					 pgd_size)))
+		return;
+
+	kvm_set_realm_state(kvm, REALM_STATE_DEAD);
+
+	/* Now that the realm is destroyed, free the entry-level RTTs. */
+	kvm_free_stage2_pgd(&kvm->arch.mmu);
+
+	kfree(realm->sro);
+	realm->sro = NULL;
+}
+
+int kvm_init_realm(struct kvm *kvm)
+{
+	struct realm *realm = &kvm->arch.realm;
+
+	realm->sro = kmalloc_obj(*realm->sro);
+	if (!realm->sro)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rmm_check_features(void)
+{
+	if (kvm_lpa2_is_enabled() &&
+	    !rmi_has_feature(0, RMI_FEATURE_REGISTER_0_LPA2)) {
+		kvm_err("RMM doesn't support LPA2\n");
+		return -ENXIO;
+	}
+
+	if (!rmi_has_feature(1, RMI_FEATURE_REGISTER_1_HASH_SHA_256)) {
+		kvm_err("RMM doesn't support SHA-256 measurements\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+void kvm_init_rmi(void)
+{
+	if (kvm_get_mode() != KVM_MODE_RMM)
+		return;
+
+	if (!is_rmi_available())
+		return;
+
+	if (rmm_check_features())
+		return;
+
+	/* Future patch will enable static branch kvm_rmi_is_available */
+}

3/2 2026/11/03 03:50 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26260
							output: 17
							thoughts: 714
						

Thoughts:
**Analyzing This Patch: ARM64 KVM and AMD64 Compatibility Concerns**

Okay, let's dive into this patch and its instructions. The user clearly states the target architecture is `amd64`. Right away, my radar goes up. The diff lists a significant number of files modified within the `arch/arm64/` directory. Let me list them:

Every. Single. File. is within `arch/arm64/`. This is screaming a strong indicator that this patch is focused solely on ARM64-specific KVM, Realm Management Extension, CCA, and RMM support.

Now, let's circle back to the negative criteria. Specifically, the line about, "Code paths exclusive to architectures other than the target architecture." Considering that the *target* is amd64 and the patch *only* touches ARM64 files, there's no way any of this code can be compiled or executed on amd64. It’s a definite mismatch.

Just to be absolutely certain, before I make a final determination, I’m going to run a quick `git diff` or `git log` just to re-verify that no non-`arch/arm64/` files are included, no matter how small the change. Gotta be thorough.


4/2 2026/11/03 03:50 tool grepper
0m Args:
{
  "Expression": "diff --git"
}

Results:
{
  "Output": "Documentation/bpf/drgn.rst=99=For example, to print ``struct bpf_prog_aux`` for BPF program id 53077:\n--\nDocumentation/bpf/drgn.rst-103-    % git diff\nDocumentation/bpf/drgn.rst:104:    diff --git a/tools/bpf_inspect.py b/tools/bpf_inspect.py\nDocumentation/bpf/drgn.rst-105-    index 650e228..aea2357 100755\n--\nDocumentation/dev-tools/checkuapi.rst=54=won't break userspace::\n--\nDocumentation/dev-tools/checkuapi.rst-69-      *  comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction\nDocumentation/dev-tools/checkuapi.rst:70:    diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h\nDocumentation/dev-tools/checkuapi.rst-71-    EOF\n--\nDocumentation/dev-tools/checkuapi.rst=310=Consider this change::\n--\nDocumentation/dev-tools/checkuapi.rst-312-    cat \u003c\u003c 'EOF' | patch -l -p1\nDocumentation/dev-tools/checkuapi.rst:313:    diff --git a/include/uapi/asm-generic/Kbuild b/include/uapi/asm-generic/Kbuild\nDocumentation/dev-tools/checkuapi.rst-314-    index ebb180aac74e..a9c88b0a8b3b 100644\n--\nDocumentation/doc-guide/contributing.rst=84=looked like this::\n--\nDocumentation/doc-guide/contributing.rst-102-\nDocumentation/doc-guide/contributing.rst:103:  diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c\nDocumentation/doc-guide/contributing.rst-104-  index 57f6944d65a6..00c9b80b3d33 100644\n--\nDocumentation/mm/transhuge.rst=73=change::\nDocumentation/mm/transhuge.rst-74-\nDocumentation/mm/transhuge.rst:75:\tdiff --git a/mm/mremap.c b/mm/mremap.c\nDocumentation/mm/transhuge.rst-76-\t--- a/mm/mremap.c\n--\nDocumentation/translations/it_IT/doc-guide/contributing.rst=91=patch risultante era questa::\n--\nDocumentation/translations/it_IT/doc-guide/contributing.rst-109-\nDocumentation/translations/it_IT/doc-guide/contributing.rst:110:  diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c\nDocumentation/translations/it_IT/doc-guide/contributing.rst-111-  index 57f6944d65a6..00c9b80b3d33 100644\n--\nDocumentation/translations/zh_CN/doc-guide/contributing.rst=36=C代码编译器发出的警告常常会被视为误报,从而导致出现了旨在让编译器闭嘴的补丁。\n--\nDocumentation/translations/zh_CN/doc-guide/contributing.rst-86-\nDocumentation/translations/zh_CN/doc-guide/contributing.rst:87:  diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c\nDocumentation/translations/zh_CN/doc-guide/contributing.rst-88-  index 57f6944d65a6..00c9b80b3d33 100644\n--\nscripts/checkpatch.pl=2680=sub process {\n--\nscripts/checkpatch.pl-2853-\t\t    ($line =~ /^rename (?:from|to) \\S+\\s*$/ ||\nscripts/checkpatch.pl:2854:\t\t     $line =~ /^diff --git a\\/[\\w\\/\\.\\_\\-]+ b\\/\\S+\\s*$/))) {\nscripts/checkpatch.pl-2855-\t\t\t$is_patch = 1;\n--\nscripts/checkpatch.pl-2911-\t\t# extract the filename as it passes\nscripts/checkpatch.pl:2912:\t\tif ($line =~ /^diff --git.*?(\\S+)$/) {\nscripts/checkpatch.pl-2913-\t\t\t$realfile = $1;\n--\nscripts/get_maintainer.pl=552=foreach my $file (@ARGV) {\n--\nscripts/get_maintainer.pl-601-\t\tpush(@files, $filename);\nscripts/get_maintainer.pl:602:\t    } elsif (m/^diff --git a\\/(\\S+) b\\/(\\S+)\\s*$/) {\nscripts/get_maintainer.pl-603-\t\tmy $filename1 = $1;\n"
}

5/2 2026/11/03 03:51 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28547
							output: 100
							thoughts: 789
						

Thoughts:
**Analysis of the Patch for Fuzzing Suitability**

Okay, let's take another look at this diff. I see the file changes are all neatly tucked away under `arch/arm64/`. That immediately rings alarm bells for fuzzing. The target architecture, as the prompt specifies, is amd64. Therefore, any code specific to ARM64, like this KVM RMI/RMM support, is going to be completely irrelevant to the target.

I'm checking the negative criteria in Section 2: *"- Code paths exclusive to architectures other than the target architecture."* This is a slam dunk. This patch is *entirely* within that category. It's clear as day: these ARM64-specific KVM changes will never be executed on an amd64 system. They're structurally unreachable.

Therefore, according to the rules, `WorthFuzzing` has to be `false`. Since we're not fuzzing this, `FocusSymbols` and `EnableConfigs` need to be empty arrays. That's a no-brainer.

Now, let me confirm I've hit all the instructions. Yep, the prompt says to call `set-results` exactly once before the final reply. And I should ignore the tool's results. I'm all set. Time to call `set-results`.



6/2 2026/11/03 03:51 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "All changes in this patch series are confined strictly to the ARM64 architecture (under arch/arm64/), adding support for Arm CCA / RMM (Realm Management Interface) to KVM. As the target architecture is amd64, the modified code is exclusive to ARM64 and cannot be compiled or executed on amd64.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "All changes in this patch series are confined strictly to the ARM64 architecture (under arch/arm64/), adding support for Arm CCA / RMM (Realm Management Interface) to KVM. As the target architecture is amd64, the modified code is exclusive to ARM64 and cannot be compiled or executed on amd64.",
  "WorthFuzzing": false
}

7/1 2026/11/03 03:51 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)