* Re: [PATCH 2/2] x86/virt/tdx: Use PFN directly for unmapping guest private memory
From: Edgecombe, Rick P @ 2026-03-19 18:44 UTC (permalink / raw)
To: Li, Xiaoyao, Zhao, Yan Y
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
dave.hansen@linux.intel.com, kas@kernel.org, seanjc@google.com,
mingo@redhat.com, pbonzini@redhat.com, binbin.wu@linux.intel.com,
ackerleytng@google.com, linux-kernel@vger.kernel.org,
Yamahata, Isaku, sagis@google.com, Annapurve, Vishal,
bp@alien8.de, tglx@kernel.org, yilun.xu@linux.intel.com,
x86@kernel.org
In-Reply-To: <b74acb4b-4658-4113-9fce-9faf972975c4@intel.com>
On Thu, 2026-03-19 at 16:56 +0800, Xiaoyao Li wrote:
> > > > }
> > > > -void tdx_quirk_reset_page(struct page *page)
> > > > +void tdx_quirk_reset_page(kvm_pfn_t pfn)
> > >
> > > So why keep the function tdx_quirk_reset_page() but expect passing in the
> > > kvm_pfn_t? It looks werid that the name indicates to reset a page but what
> > > gets passed in is a pfn.
The kernel has APIs that take non-struct page arg forms and operate on a "page".
For example free_page(), clear_page(), etc. So keeping the "_page" name seems
not too horrible to me.
> > I thought about introducing tdx_quirk_reset_pfn(). But considering
> > tdx_quirk_reset_pfn() has to be an exported API, I'm reluctant to do that.
Yea exporting two functions that do the same thing doesn't seem the right
balance.
> >
> > Given that even with tdx_quirk_reset_pfn(), it still expects TDX convertible
> > RAM, I think having tdx_quirk_reset_page() to take pfn is still acceptable.
> >
> > We just don't want KVM to do pfn --> struct page --> pfn conversions.
We can assume struct pages have pfn's pretty safely. So pfn->page, and
especially allocated from far away code, is the cleanup target here.
>
> Only tdx_sept_remove_private_spte() is doing such conversions. While
> tdx_reclaim_page() and tdx_reclaim_td_control_pages() already have the
> struct page natively.
>
> So why not considering option 2?
>
> 2. keep tdx_quirk_reset_page() as-is for the cases of
> tdx_reclaim_page() and tdx_reclaim_td_control_pages() that have the
> struct page. But only change tdx_sept_remove_private_spte() to use
> tdx_quirk_reset_paddr() directly.
>
> It will need export tdx_quirk_reset_paddr() for KVM. I think it will be OK?
Exporting tdx_quirk_reset_paddr() seems reasonable, except then we have pfn, PA
and struct page across the API. It increases the asymmetry.
We did discuss converting the whole API over to PFN for symmetry. It could
eliminate the control page and guest memory differences.
But this way seems like a more manageable step that addresses the biggest issue.
If we don't want to do a massive cleanup, there will be some stuff left for the
future.
^ permalink raw reply
* Re: [PATCH v13 27/48] arm64: RMI: Runtime faulting of memory
From: Wei-Lin Chang @ 2026-03-19 18:41 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-28-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:51PM +0000, Steven Price wrote:
> At runtime if the realm guest accesses memory which hasn't yet been
> mapped then KVM needs to either populate the region or fault the guest.
>
> For memory in the lower (protected) region of IPA a fresh page is
> provided to the RMM which will zero the contents. For memory in the
> upper (shared) region of IPA, the memory from the memslot is mapped
> into the realm VM non secure.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Switch to RMM v2.0 range based APIs.
> Changes since v11:
> * Adapt to upstream changes.
> Changes since v10:
> * RME->RMI renaming.
> * Adapt to upstream gmem changes.
> Changes since v9:
> * Fix call to kvm_stage2_unmap_range() in kvm_free_stage2_pgd() to set
> may_block to avoid stall warnings.
> * Minor coding style fixes.
> Changes since v8:
> * Propagate the may_block flag.
> * Minor comments and coding style changes.
> Changes since v7:
> * Remove redundant WARN_ONs for realm_create_rtt_levels() - it will
> internally WARN when necessary.
> Changes since v6:
> * Handle PAGE_SIZE being larger than RMM granule size.
> * Some minor renaming following review comments.
> Changes since v5:
> * Reduce use of struct page in preparation for supporting the RMM
> having a different page size to the host.
> * Handle a race when delegating a page where another CPU has faulted on
> a the same page (and already delegated the physical page) but not yet
> mapped it. In this case simply return to the guest to either use the
> mapping from the other CPU (or refault if the race is lost).
> * The changes to populate_par_region() are moved into the previous
> patch where they belong.
> Changes since v4:
> * Code cleanup following review feedback.
> * Drop the PTE_SHARED bit when creating unprotected page table entries.
> This is now set by the RMM and the host has no control of it and the
> spec requires the bit to be set to zero.
> Changes since v2:
> * Avoid leaking memory if failing to map it in the realm.
> * Correctly mask RTT based on LPA2 flag (see rtt_get_phys()).
> * Adapt to changes in previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 8 ++
> arch/arm64/include/asm/kvm_rmi.h | 12 ++
> arch/arm64/kvm/mmu.c | 139 ++++++++++++++++--
> arch/arm64/kvm/rmi.c | 206 +++++++++++++++++++++++++++
> 4 files changed, 351 insertions(+), 14 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index d194d91fbc2a..0734c4a65174 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -706,6 +706,14 @@ static inline bool kvm_realm_is_created(struct kvm *kvm)
> return kvm_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)
I think we should use gpa_t as the type for ipa.
> +{
> + if (!kvm_is_realm(kvm))
> + return ipa;
> +
> + return ipa & ~BIT(kvm->arch.realm.ia_bits - 1);
> +}
> +
> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> {
> return kvm_is_realm(vcpu->kvm);
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index bf663bb240c4..38208be3c602 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -6,6 +6,7 @@
> #ifndef __ASM_KVM_RMI_H
> #define __ASM_KVM_RMI_H
>
> +#include <asm/kvm_pgtable.h>
> #include <asm/rmi_smc.h>
>
> /**
> @@ -105,6 +106,17 @@ void kvm_realm_unmap_range(struct kvm *kvm,
> unsigned long size,
> bool unmap_private,
> bool may_block);
> +int realm_map_protected(struct kvm *kvm,
> + unsigned long base_ipa,
> + kvm_pfn_t pfn,
> + unsigned long size,
> + struct kvm_mmu_memory_cache *memcache);
> +int realm_map_non_secure(struct realm *realm,
> + unsigned long ipa,
> + kvm_pfn_t pfn,
> + unsigned long 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)
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index bad93938acdb..73c18c2861a2 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -334,8 +334,15 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64
>
> lockdep_assert_held_write(&kvm->mmu_lock);
> WARN_ON(size & ~PAGE_MASK);
> - WARN_ON(stage2_apply_range(mmu, start, end, KVM_PGT_FN(kvm_pgtable_stage2_unmap),
> - may_block));
> +
> + if (kvm_is_realm(kvm)) {
> + kvm_realm_unmap_range(kvm, start, size, !only_shared,
> + may_block);
> + } else {
> + WARN_ON(stage2_apply_range(mmu, start, end,
> + KVM_PGT_FN(kvm_pgtable_stage2_unmap),
> + may_block));
> + }
> }
>
> void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
> @@ -355,7 +362,10 @@ 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_is_realm(kvm))
> + kvm_realm_unmap_range(kvm, addr, end - addr, false, true);
> + else
> + kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
> }
>
> /**
> @@ -1081,6 +1091,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_is_realm(kvm))
> + return;
> +
> idx = srcu_read_lock(&kvm->srcu);
> mmap_read_lock(current->mm);
> write_lock(&kvm->mmu_lock);
> @@ -1106,6 +1120,9 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
> if (kvm_is_realm(kvm) &&
> (kvm_realm_state(kvm) != REALM_STATE_DEAD &&
> kvm_realm_state(kvm) != REALM_STATE_NONE)) {
> + struct realm *realm = &kvm->arch.realm;
> +
> + kvm_stage2_unmap_range(mmu, 0, BIT(realm->ia_bits - 1), true);
> write_unlock(&kvm->mmu_lock);
> kvm_realm_destroy_rtts(kvm);
>
> @@ -1516,6 +1533,29 @@ static bool kvm_vma_mte_allowed(struct vm_area_struct *vma)
> return vma->vm_flags & VM_MTE_ALLOWED;
> }
>
> +static 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;
> +
> + /*
> + * Write permission is required for now even though it's possible to
> + * map unprotected pages (granules) as read-only. It's impossible to
> + * map protected pages (granules) as read-only.
> + */
> + if (WARN_ON(!(prot & KVM_PGTABLE_PROT_W)))
> + return -EFAULT;
> +
> + ipa = ALIGN_DOWN(ipa, PAGE_SIZE);
> + if (!kvm_realm_is_private_address(realm, ipa))
> + return realm_map_non_secure(realm, ipa, pfn, map_size, prot,
> + memcache);
> +
> + return realm_map_protected(kvm, ipa, pfn, map_size, memcache);
> +}
> +
> static bool kvm_vma_is_cacheable(struct vm_area_struct *vma)
> {
> switch (FIELD_GET(PTE_ATTRINDX_MASK, pgprot_val(vma->vm_page_prot))) {
> @@ -1588,6 +1628,7 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
> struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
> + gpa_t gpa = kvm_gpa_from_fault(vcpu->kvm, fault_ipa);
> unsigned long mmu_seq;
> struct page *page;
> struct kvm *kvm = vcpu->kvm;
> @@ -1596,6 +1637,29 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> gfn_t gfn;
> int ret;
>
> + if (kvm_is_realm(vcpu->kvm)) {
> + /* check for memory attribute mismatch */
> + bool is_priv_gfn = kvm_mem_is_private(kvm, gpa >> PAGE_SHIFT);
> + /*
> + * For Realms, the shared address is an alias of the private
> + * PA with the top bit set. Thus is the fault address matches
Hi,
Thus is -> Thus if.
> + * the GPA then it is the private alias.
> + */
> + bool is_priv_fault = (gpa == fault_ipa);
> +
> + if (is_priv_gfn != is_priv_fault) {
> + kvm_prepare_memory_fault_exit(vcpu, gpa, PAGE_SIZE,
> + kvm_is_write_fault(vcpu),
> + false,
> + is_priv_fault);
> + /*
> + * KVM_EXIT_MEMORY_FAULT requires an return code of
> + * -EFAULT, see the API documentation
> + */
> + return -EFAULT;
> + }
> + }
> +
> ret = prepare_mmu_memcache(vcpu, true, &memcache);
> if (ret)
> return ret;
> @@ -1603,7 +1667,7 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> if (nested)
> gfn = kvm_s2_trans_output(nested) >> PAGE_SHIFT;
> else
> - gfn = fault_ipa >> PAGE_SHIFT;
> + gfn = gpa >> PAGE_SHIFT;
>
> write_fault = kvm_is_write_fault(vcpu);
> exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
> @@ -1616,7 +1680,7 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>
> ret = kvm_gmem_get_pfn(kvm, memslot, gfn, &pfn, &page, NULL);
> if (ret) {
> - kvm_prepare_memory_fault_exit(vcpu, fault_ipa, PAGE_SIZE,
> + kvm_prepare_memory_fault_exit(vcpu, gpa, PAGE_SIZE,
> write_fault, exec_fault, false);
> return ret;
> }
> @@ -1638,15 +1702,25 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> kvm_fault_lock(kvm);
> if (mmu_invalidate_retry(kvm, mmu_seq)) {
> ret = -EAGAIN;
> - goto out_unlock;
> + goto out_release_page;
> + }
> +
> + if (kvm_is_realm(kvm)) {
> + ret = realm_map_ipa(kvm, fault_ipa, pfn,
> + PAGE_SIZE, KVM_PGTABLE_PROT_W, memcache);
> + /* if successful don't release the page */
> + if (!ret)
> + goto out_unlock;
> + goto out_release_page;
> }
>
> ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, PAGE_SIZE,
> __pfn_to_phys(pfn), prot,
> memcache, flags);
>
> -out_unlock:
> +out_release_page:
> kvm_release_faultin_page(kvm, page, !!ret, writable);
> +out_unlock:
> kvm_fault_unlock(kvm);
>
> if (writable && !ret)
> @@ -1685,6 +1759,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> if (fault_is_perm)
> fault_granule = kvm_vcpu_trap_get_perm_fault_granule(vcpu);
> write_fault = kvm_is_write_fault(vcpu);
> +
> + /*
> + * Realms cannot map protected pages read-only
> + * FIXME: It should be possible to map unprotected pages read-only
> + */
> + if (vcpu_is_rec(vcpu))
> + write_fault = true;
> +
> exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
> VM_WARN_ON_ONCE(write_fault && exec_fault);
>
> @@ -1779,7 +1861,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> ipa &= ~(vma_pagesize - 1);
> }
>
> - gfn = ipa >> PAGE_SHIFT;
> + gfn = kvm_gpa_from_fault(kvm, ipa) >> PAGE_SHIFT;
> mte_allowed = kvm_vma_mte_allowed(vma);
>
> vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED;
> @@ -1855,6 +1937,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> if (exec_fault && s2_force_noncacheable)
> ret = -ENOEXEC;
>
> + /*
> + * For now we shouldn't be hitting protected addresses because they are
> + * handled in gmem_abort(). In the future this check may be relaxed to
> + * support e.g. protected devices.
> + */
> + if (!ret && vcpu_is_rec(vcpu) &&
> + kvm_gpa_from_fault(kvm, fault_ipa) == fault_ipa)
Maybe use !shared_ipa_fault() here?
Thanks,
Wei-Lin Chang
> + ret = -EINVAL;
> +
> if (ret) {
> kvm_release_page_unused(page);
> return ret;
> @@ -1939,6 +2030,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> */
> prot &= ~KVM_NV_GUEST_MAP_SZ;
> ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, fault_ipa, prot, flags);
> + } else if (kvm_is_realm(kvm)) {
> + ret = realm_map_ipa(kvm, fault_ipa, pfn, vma_pagesize,
> + prot, memcache);
> } else {
> ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, vma_pagesize,
> __pfn_to_phys(pfn), prot,
> @@ -2049,6 +2143,13 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
> return 0;
> }
>
> +static bool shared_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa)
> +{
> + gpa_t gpa = kvm_gpa_from_fault(kvm, fault_ipa);
> +
> + return (gpa != fault_ipa);
> +}
> +
> /**
> * kvm_handle_guest_abort - handles all 2nd stage aborts
> * @vcpu: the VCPU pointer
> @@ -2159,8 +2260,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> nested = &nested_trans;
> }
>
> - gfn = ipa >> PAGE_SHIFT;
> + gfn = kvm_gpa_from_fault(vcpu->kvm, ipa) >> PAGE_SHIFT;
> memslot = gfn_to_memslot(vcpu->kvm, gfn);
> +
> hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
> write_fault = kvm_is_write_fault(vcpu);
> if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
> @@ -2203,7 +2305,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(vcpu->kvm, ipa));
> goto out_unlock;
> }
>
> @@ -2219,7 +2321,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) &&
> !write_fault && !kvm_vcpu_trap_is_exec_fault(vcpu));
>
> - if (kvm_slot_has_gmem(memslot))
> + if (kvm_slot_has_gmem(memslot) && !shared_ipa_fault(vcpu->kvm, fault_ipa))
> ret = gmem_abort(vcpu, fault_ipa, nested, memslot,
> esr_fsc_is_permission_fault(esr));
> else
> @@ -2256,6 +2358,10 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> if (!kvm->arch.mmu.pgt)
> return false;
>
> + /* We don't support aging for Realms */
> + if (kvm_is_realm(kvm))
> + return true;
> +
> return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
> range->start << PAGE_SHIFT,
> size, true);
> @@ -2272,6 +2378,10 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
> if (!kvm->arch.mmu.pgt)
> return false;
>
> + /* We don't support aging for Realms */
> + if (kvm_is_realm(kvm))
> + return true;
> +
> return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
> range->start << PAGE_SHIFT,
> size, false);
> @@ -2438,10 +2548,11 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
> 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_is_realm(kvm))
> return -EINVAL;
>
> hva = new->userspace_addr;
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index d5fee203824b..30292814b1ec 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -837,6 +837,212 @@ static int realm_create_protected_data_page(struct kvm *kvm,
> return ret;
> }
>
> +static int fold_rtt(struct realm *realm, unsigned long addr, int level)
> +{
> + phys_addr_t rtt_addr;
> + int ret;
> +
> + ret = realm_rtt_fold(realm, addr, level, &rtt_addr);
> + if (ret)
> + return ret;
> +
> + free_rtt(rtt_addr);
> +
> + return 0;
> +}
> +
> +static unsigned long addr_range_desc(unsigned long phys, unsigned long size)
> +{
> + unsigned long out = 0;
> +
> + switch (size) {
> + case P4D_SIZE:
> + out = 0 | (1 << 2);
> + break;
> + case PUD_SIZE:
> + out = 1 | (1 << 2);
> + break;
> + case PMD_SIZE:
> + out = 2 | (1 << 2);
> + break;
> + case PAGE_SIZE:
> + out = 3 | (1 << 2);
> + break;
> + default:
> + /*
> + * Only support mapping at the page level granulatity when
> + * it's an unusual length. This should get us back onto a larger
> + * block size for the subsequent mappings.
> + */
> + out = 3 | ((MIN(size >> PAGE_SHIFT, PTRS_PER_PTE - 1)) << 2);
> + break;
> + }
> +
> + WARN_ON(phys & ~PAGE_MASK);
> +
> + out |= phys & PAGE_MASK;
> +
> + return out;
> +}
> +
> +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 rd = virt_to_phys(realm->rd);
> + unsigned long base_ipa = ipa;
> + unsigned long ipa_top = ipa + map_size;
> + int map_level = IS_ALIGNED(map_size, RMM_L2_BLOCK_SIZE) ?
> + RMM_RTT_BLOCK_LEVEL : RMM_RTT_MAX_LEVEL;
> + int ret = 0;
> +
> + if (WARN_ON(!IS_ALIGNED(map_size, PAGE_SIZE) ||
> + !IS_ALIGNED(ipa, map_size)))
> + return -EINVAL;
> +
> + if (map_level < RMM_RTT_MAX_LEVEL) {
> + /*
> + * A temporary RTT is needed during the map, precreate it,
> + * however if there is an error (e.g. missing parent tables)
> + * this will be handled below.
> + */
> + realm_create_rtt_levels(realm, ipa, map_level,
> + RMM_RTT_MAX_LEVEL, memcache);
> + }
> +
> + if (delegate_range(phys, map_size)) {
> + /*
> + * 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;
> + }
> +
> + while (ipa < ipa_top) {
> + unsigned long flags = RMI_ADDR_TYPE_SINGLE;
> + unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa);
> + unsigned long out_top;
> +
> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags, range_desc,
> + &out_top);
> +
> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
> + /* Create missing RTTs and retry */
> + int level = RMI_RETURN_INDEX(ret);
> +
> + WARN_ON(level == RMM_RTT_MAX_LEVEL);
> + ret = realm_create_rtt_levels(realm, ipa, level,
> + RMM_RTT_MAX_LEVEL,
> + memcache);
> + if (ret)
> + goto err_undelegate;
> +
> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags,
> + range_desc, &out_top);
> + }
> +
> + if (WARN_ON(ret))
> + goto err_undelegate;
> +
> + phys += out_top - ipa;
> + ipa = out_top;
> + }
> +
> + if (map_size == RMM_L2_BLOCK_SIZE) {
> + ret = fold_rtt(realm, base_ipa, map_level + 1);
> + if (WARN_ON(ret))
> + goto err;
> + }
> +
> + return 0;
> +
> +err_undelegate:
> + if (WARN_ON(undelegate_range(phys, map_size))) {
> + /* Page can't be returned to NS world so is lost */
> + get_page(phys_to_page(phys));
> + }
> +err:
> + realm_unmap_private_range(kvm, base_ipa, ipa, true);
> + return -ENXIO;
> +}
> +
> +int realm_map_non_secure(struct realm *realm,
> + unsigned long ipa,
> + kvm_pfn_t pfn,
> + unsigned long size,
> + enum kvm_pgtable_prot prot,
> + struct kvm_mmu_memory_cache *memcache)
> +{
> + unsigned long attr;
> + phys_addr_t rd = virt_to_phys(realm->rd);
> + phys_addr_t phys = __pfn_to_phys(pfn);
> + unsigned long offset;
> + /* TODO: Support block mappings */
> + int map_level = RMM_RTT_MAX_LEVEL;
> + int map_size = rmi_rtt_level_mapsize(map_level);
> + int ret = 0;
> +
> + if (WARN_ON(!IS_ALIGNED(size, PAGE_SIZE) ||
> + !IS_ALIGNED(ipa, 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 = PTE_S2_MEMATTR(MT_S2_FWB_DEVICE_nGnRE);
> + break;
> + case KVM_PGTABLE_PROT_NORMAL_NC:
> + attr = PTE_S2_MEMATTR(MT_S2_FWB_NORMAL_NC);
> + break;
> + default:
> + attr = PTE_S2_MEMATTR(MT_S2_FWB_NORMAL);
> + }
> +
> + for (offset = 0; offset < size; offset += map_size) {
> + /*
> + * realm_map_ipa() enforces that the memory is writable,
> + * so for now we permit both read and write.
> + */
> + unsigned long desc = kvm_phys_to_pte(phys) | attr |
> + KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R |
> + KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
> + ret = rmi_rtt_map_unprotected(rd, ipa, map_level, desc);
> +
> + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
> + /* Create missing RTTs and retry */
> + int level = RMI_RETURN_INDEX(ret);
> +
> + ret = realm_create_rtt_levels(realm, ipa, level,
> + map_level, memcache);
> + if (ret)
> + return -ENXIO;
> +
> + ret = rmi_rtt_map_unprotected(rd, ipa, map_level, desc);
> + }
> + /*
> + * RMI_ERROR_RTT can be reported for two reasons: either the
> + * RTT tables are not there, or there is an RTTE already
> + * present for the address. The above call to create RTTs
> + * handles the first case, and in the second case this
> + * indicates that another thread has already populated the RTTE
> + * for us, so we can ignore the error and continue.
> + */
> + if (ret && RMI_RETURN_STATUS(ret) != RMI_ERROR_RTT)
> + return -ENXIO;
> +
> + ipa += map_size;
> + phys += map_size;
> + }
> +
> + return 0;
> +}
> +
> static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> struct page *src_page, void *opaque)
> {
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 26/48] arm64: RMI: Create the realm descriptor
From: Wei-Lin Chang @ 2026-03-19 18:25 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-27-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:50PM +0000, Steven Price wrote:
> Creating a realm involves first creating a realm descriptor (RD). This
> involves passing the configuration information to the RMM. Do this as
> part of realm_ensure_created() so that the realm is created when it is
> first needed.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Since RMM page size is now equal to the host's page size various
> calculations are simplified.
> * Switch to using range based APIs to delegate/undelegate.
> * VMID handling is now handled entirely by the RMM.
> ---
> arch/arm64/kvm/rmi.c | 94 +++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 92 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 38349c7b34f4..d5fee203824b 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -649,6 +649,83 @@ static void realm_unmap_shared_range(struct kvm *kvm,
> start, end);
> }
>
> +static int realm_create_rd(struct kvm *kvm)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + struct realm_params *params = realm->params;
> + void *rd = NULL;
> + phys_addr_t rd_phys, params_phys;
> + size_t pgd_size = kvm_pgtable_stage2_pgd_size(kvm->arch.mmu.vtcr);
> + int i, r;
> +
> + realm->ia_bits = VTCR_EL2_IPA(kvm->arch.mmu.vtcr);
> +
> + if (WARN_ON(realm->rd || !realm->params))
> + return -EEXIST;
> +
> + rd = (void *)__get_free_page(GFP_KERNEL);
Hi,
Should this be GFP_KERNEL_ACCOUNT?
> + if (!rd)
> + return -ENOMEM;
> +
> + rd_phys = virt_to_phys(rd);
> + if (delegate_page(rd_phys)) {
> + r = -ENXIO;
> + goto free_rd;
> + }
> +
> + if (delegate_range(kvm->arch.mmu.pgd_phys, pgd_size)) {
> + r = -ENXIO;
> + goto out_undelegate_tables;
> + }
> +
> + params->s2sz = VTCR_EL2_IPA(kvm->arch.mmu.vtcr);
> + params->rtt_level_start = get_start_level(realm);
> + params->rtt_num_start = pgd_size / PAGE_SIZE;
> + params->rtt_base = kvm->arch.mmu.pgd_phys;
> +
> + if (kvm->arch.arm_pmu) {
> + params->pmu_num_ctrs = kvm->arch.nr_pmu_counters;
> + params->flags |= RMI_REALM_PARAM_FLAG_PMU;
> + }
> +
> + if (kvm_lpa2_is_enabled())
> + params->flags |= RMI_REALM_PARAM_FLAG_LPA2;
> +
> + params_phys = virt_to_phys(params);
> +
> + if (rmi_realm_create(rd_phys, params_phys)) {
> + r = -ENXIO;
> + goto out_undelegate_tables;
> + }
> +
> + if (WARN_ON(rmi_rec_aux_count(rd_phys, &realm->num_aux))) {
> + WARN_ON(rmi_realm_destroy(rd_phys));
> + r = -ENXIO;
> + goto out_undelegate_tables;
> + }
> +
> + realm->rd = rd;
> + WRITE_ONCE(realm->state, REALM_STATE_NEW);
> + /* The realm is up, free the parameters. */
> + free_page((unsigned long)realm->params);
> + realm->params = NULL;
> +
> + return 0;
> +
> +out_undelegate_tables:
> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, i))) {
> + /* Leak the pages if they cannot be returned */
> + kvm->arch.mmu.pgt = NULL;
Did you mean kvm->arch.mmu.pgd_phys = NULL; ?
Thanks,
Wei-Lin Chang
> + }
> + if (WARN_ON(undelegate_page(rd_phys))) {
> + /* Leak the page if it isn't returned */
> + return r;
> + }
> +free_rd:
> + free_page((unsigned long)rd);
> + return r;
> +}
> +
> static void realm_unmap_private_range(struct kvm *kvm,
> unsigned long start,
> unsigned long end,
> @@ -893,8 +970,21 @@ static int realm_init_ipa_state(struct kvm *kvm,
>
> static int realm_ensure_created(struct kvm *kvm)
> {
> - /* Provided in later patch */
> - return -ENXIO;
> + int ret;
> +
> + switch (kvm_realm_state(kvm)) {
> + case REALM_STATE_NONE:
> + break;
> + case REALM_STATE_NEW:
> + return 0;
> + case REALM_STATE_DEAD:
> + return -ENXIO;
> + default:
> + return -EBUSY;
> + }
> +
> + ret = realm_create_rd(kvm);
> + return ret;
> }
>
> static int set_ripas_of_protected_regions(struct kvm *kvm)
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 17/48] arm64: RMI: Allocate/free RECs to match vCPUs
From: Wei-Lin Chang @ 2026-03-19 18:10 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-18-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:41PM +0000, Steven Price wrote:
> The RMM maintains a data structure known as the Realm Execution Context
> (or REC). It is similar to struct kvm_vcpu and tracks the state of the
> virtual CPUs. KVM must delegate memory and request the structures are
> created when vCPUs are created, and suitably tear down on destruction.
>
> RECs must also be supplied with addition pages - auxiliary (or AUX)
> granules - for storing the larger registers state (e.g. for SVE). The
> number of AUX granules for a REC depends on the parameters with which
> the Realm was created - the RMM makes this information available via the
> RMI_REC_AUX_COUNT call performed after creating the Realm Descriptor (RD).
>
> Note that only some of register state for the REC can be set by KVM, the
> rest is defined by the RMM (zeroed). The register state then cannot be
> changed by KVM after the REC is created (except when the guest
> explicitly requests this e.g. by performing a PSCI call).
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Use the new range-based delegation RMI.
> Changes since v11:
> * Remove the KVM_ARM_VCPU_REC feature. User space no longer needs to
> configure each VCPU separately, RECs are created on the first VCPU
> run of the guest.
> Changes since v9:
> * Size the aux_pages array according to the PAGE_SIZE of the host.
> Changes since v7:
> * Add comment explaining the aux_pages array.
> * Rename "undeleted_failed" variable to "should_free" to avoid a
> confusing double negative.
> Changes since v6:
> * Avoid reporting the KVM_ARM_VCPU_REC feature if the guest isn't a
> realm guest.
> * Support host page size being larger than RMM's granule size when
> allocating/freeing aux granules.
> Changes since v5:
> * Separate the concept of vcpu_is_rec() and
> kvm_arm_vcpu_rec_finalized() by using the KVM_ARM_VCPU_REC feature as
> the indication that the VCPU is a REC.
> Changes since v2:
> * Free rec->run earlier in kvm_destroy_realm() and adapt to previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 2 +-
> arch/arm64/include/asm/kvm_host.h | 3 +
> arch/arm64/include/asm/kvm_rmi.h | 21 +++
> arch/arm64/kvm/arm.c | 10 +-
> arch/arm64/kvm/reset.c | 1 +
> arch/arm64/kvm/rmi.c | 196 +++++++++++++++++++++++++++
> 6 files changed, 230 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index 39310d9b4e16..d194d91fbc2a 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -708,7 +708,7 @@ static inline bool kvm_realm_is_created(struct kvm *kvm)
>
> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> {
> - return false;
> + return kvm_is_realm(vcpu->kvm);
> }
>
> #endif /* __ARM64_KVM_EMULATE_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 9267a2f2d65b..64304848aad4 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -924,6 +924,9 @@ struct kvm_vcpu_arch {
>
> /* Per-vcpu TLB for VNCR_EL2 -- NULL when !NV */
> struct vncr_tlb *vncr_tlb;
> +
> + /* Realm meta data */
> + struct realm_rec rec;
> };
>
> /*
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 6c13847480f7..4e2c61e71a38 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -63,6 +63,26 @@ struct realm {
> unsigned int ia_bits;
> };
>
> +/**
> + * struct realm_rec - Additional per VCPU data for a Realm
> + *
> + * @mpidr: MPIDR (Multiprocessor Affinity Register) value to identify this VCPU
> + * @rec_page: Kernel VA of the RMM's private page for this REC
> + * @aux_pages: Additional pages private to the RMM for this REC
> + * @run: Kernel VA of the RmiRecRun structure shared with the RMM
> + */
> +struct realm_rec {
> + unsigned long mpidr;
> + void *rec_page;
> + /*
> + * REC_PARAMS_AUX_GRANULES is the maximum number of 4K granules that
> + * the RMM can require. The array is sized to be large enough for the
> + * maximum number of host sized pages that could be required.
> + */
> + struct page *aux_pages[(REC_PARAMS_AUX_GRANULES * SZ_4K) >> PAGE_SHIFT];
> + struct rec_run *run;
> +};
> +
> void kvm_init_rmi(void);
> u32 kvm_realm_ipa_limit(void);
>
> @@ -70,6 +90,7 @@ int kvm_init_realm_vm(struct kvm *kvm);
> int kvm_activate_realm(struct kvm *kvm);
> void kvm_destroy_realm(struct kvm *kvm);
> void kvm_realm_destroy_rtts(struct kvm *kvm);
> +void kvm_destroy_rec(struct kvm_vcpu *vcpu);
>
> static inline bool kvm_realm_is_private_address(struct realm *realm,
> unsigned long addr)
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index c8e51ed009c0..8c50ebd9fba0 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -575,6 +575,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
> /* Force users to call KVM_ARM_VCPU_INIT */
> vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
>
> + vcpu->arch.rec.mpidr = INVALID_HWID;
> +
> vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;
>
> /* Set up the timer */
> @@ -1549,7 +1551,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
> return -EINVAL;
> }
>
> -static unsigned long system_supported_vcpu_features(void)
> +static unsigned long system_supported_vcpu_features(struct kvm *kvm)
> {
> unsigned long features = KVM_VCPU_VALID_FEATURES;
>
> @@ -1587,7 +1589,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
> return -ENOENT;
> }
>
> - if (features & ~system_supported_vcpu_features())
> + if (features & ~system_supported_vcpu_features(vcpu->kvm))
Hi,
Are these two hunks superfluous?
Thanks,
Wei-Lin Chang
> return -EINVAL;
>
> /*
> @@ -1609,6 +1611,10 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
> if (test_bit(KVM_ARM_VCPU_HAS_EL2, &features))
> return -EINVAL;
>
> + /* Realms are incompatible with AArch32 */
> + if (vcpu_is_rec(vcpu))
> + return -EINVAL;
> +
> return 0;
> }
>
> diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
> index 959532422d3a..4bbf58892928 100644
> --- a/arch/arm64/kvm/reset.c
> +++ b/arch/arm64/kvm/reset.c
> @@ -161,6 +161,7 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu)
> free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
> kfree(vcpu->arch.vncr_tlb);
> kfree(vcpu->arch.ccsidr);
> + kvm_destroy_rec(vcpu);
> }
>
> static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 937fababf960..6daf14c4b413 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -207,6 +207,28 @@ static int get_start_level(struct realm *realm)
> return 4 - stage2_pgtable_levels(realm->ia_bits);
> }
>
> +static int delegate_range(phys_addr_t phys, unsigned long size)
> +{
> + unsigned long ret;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_delegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + phys = out_top;
> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int delegate_page(phys_addr_t phys)
> +{
> + return delegate_range(phys, PAGE_SIZE);
> +}
> +
> static int undelegate_range(phys_addr_t phys, unsigned long size)
> {
> unsigned long ret;
> @@ -372,9 +394,177 @@ static int realm_ensure_created(struct kvm *kvm)
> return -ENXIO;
> }
>
> +static void free_rec_aux(struct page **aux_pages,
> + unsigned int num_aux)
> +{
> + unsigned int i;
> + unsigned int page_count = 0;
> +
> + for (i = 0; i < num_aux; i++) {
> + struct page *aux_page = aux_pages[page_count++];
> + phys_addr_t aux_page_phys = page_to_phys(aux_page);
> +
> + if (!WARN_ON(undelegate_page(aux_page_phys)))
> + __free_page(aux_page);
> + aux_page_phys += PAGE_SIZE;
> + }
> +}
> +
> +static int alloc_rec_aux(struct page **aux_pages,
> + u64 *aux_phys_pages,
> + unsigned int num_aux)
> +{
> + struct page *aux_page;
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < num_aux; i++) {
> + phys_addr_t aux_page_phys;
> +
> + aux_page = alloc_page(GFP_KERNEL);
> + if (!aux_page) {
> + ret = -ENOMEM;
> + goto out_err;
> + }
> +
> + aux_page_phys = page_to_phys(aux_page);
> + if (delegate_page(aux_page_phys)) {
> + ret = -ENXIO;
> + goto err_undelegate;
> + }
> + aux_phys_pages[i] = aux_page_phys;
> + aux_pages[i] = aux_page;
> + }
> +
> + return 0;
> +err_undelegate:
> + while (i > 0) {
> + i--;
> + if (WARN_ON(undelegate_page(aux_phys_pages[i]))) {
> + /* Leak the page if the undelegate fails */
> + goto out_err;
> + }
> + }
> + __free_page(aux_page);
> +out_err:
> + free_rec_aux(aux_pages, i);
> + return ret;
> +}
> +
> +static int kvm_create_rec(struct kvm_vcpu *vcpu)
> +{
> + struct user_pt_regs *vcpu_regs = vcpu_gp_regs(vcpu);
> + unsigned long mpidr = kvm_vcpu_get_mpidr_aff(vcpu);
> + struct realm *realm = &vcpu->kvm->arch.realm;
> + struct realm_rec *rec = &vcpu->arch.rec;
> + unsigned long rec_page_phys;
> + struct rec_params *params;
> + int r, i;
> +
> + if (rec->run)
> + return -EBUSY;
> +
> + /*
> + * The RMM will report PSCI v1.0 to Realms and the KVM_ARM_VCPU_PSCI_0_2
> + * flag covers v0.2 and onwards.
> + */
> + if (!vcpu_has_feature(vcpu, KVM_ARM_VCPU_PSCI_0_2))
> + return -EINVAL;
> +
> + BUILD_BUG_ON(sizeof(*params) > PAGE_SIZE);
> + BUILD_BUG_ON(sizeof(*rec->run) > PAGE_SIZE);
> +
> + params = (struct rec_params *)get_zeroed_page(GFP_KERNEL);
> + rec->rec_page = (void *)__get_free_page(GFP_KERNEL);
> + rec->run = (void *)get_zeroed_page(GFP_KERNEL);
> + if (!params || !rec->rec_page || !rec->run) {
> + r = -ENOMEM;
> + goto out_free_pages;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(params->gprs); i++)
> + params->gprs[i] = vcpu_regs->regs[i];
> +
> + params->pc = vcpu_regs->pc;
> +
> + if (vcpu->vcpu_id == 0)
> + params->flags |= REC_PARAMS_FLAG_RUNNABLE;
> +
> + rec_page_phys = virt_to_phys(rec->rec_page);
> +
> + if (delegate_page(rec_page_phys)) {
> + r = -ENXIO;
> + goto out_free_pages;
> + }
> +
> + r = alloc_rec_aux(rec->aux_pages, params->aux, realm->num_aux);
> + if (r)
> + goto out_undelegate_rmm_rec;
> +
> + params->num_rec_aux = realm->num_aux;
> + params->mpidr = mpidr;
> +
> + if (rmi_rec_create(virt_to_phys(realm->rd),
> + rec_page_phys,
> + virt_to_phys(params))) {
> + r = -ENXIO;
> + goto out_free_rec_aux;
> + }
> +
> + rec->mpidr = mpidr;
> +
> + free_page((unsigned long)params);
> + return 0;
> +
> +out_free_rec_aux:
> + free_rec_aux(rec->aux_pages, realm->num_aux);
> +out_undelegate_rmm_rec:
> + if (WARN_ON(undelegate_page(rec_page_phys)))
> + rec->rec_page = NULL;
> +out_free_pages:
> + free_page((unsigned long)rec->run);
> + free_page((unsigned long)rec->rec_page);
> + free_page((unsigned long)params);
> + rec->run = NULL;
> + return r;
> +}
> +
> +void kvm_destroy_rec(struct kvm_vcpu *vcpu)
> +{
> + struct realm *realm = &vcpu->kvm->arch.realm;
> + struct realm_rec *rec = &vcpu->arch.rec;
> + unsigned long rec_page_phys;
> +
> + if (!vcpu_is_rec(vcpu))
> + return;
> +
> + if (!rec->run) {
> + /* Nothing to do if the VCPU hasn't been finalized */
> + return;
> + }
> +
> + free_page((unsigned long)rec->run);
> +
> + rec_page_phys = virt_to_phys(rec->rec_page);
> +
> + /*
> + * The REC and any AUX pages cannot be reclaimed until the REC is
> + * destroyed. So if the REC destroy fails then the REC page and any AUX
> + * pages will be leaked.
> + */
> + if (WARN_ON(rmi_rec_destroy(rec_page_phys)))
> + return;
> +
> + free_rec_aux(rec->aux_pages, realm->num_aux);
> +
> + free_delegated_page(rec_page_phys);
> +}
> +
> int kvm_activate_realm(struct kvm *kvm)
> {
> struct realm *realm = &kvm->arch.realm;
> + struct kvm_vcpu *vcpu;
> + unsigned long i;
> int ret;
>
> if (kvm_realm_state(kvm) >= REALM_STATE_ACTIVE)
> @@ -397,6 +587,12 @@ int kvm_activate_realm(struct kvm *kvm)
> /* Mark state as dead in case we fail */
> WRITE_ONCE(realm->state, REALM_STATE_DEAD);
>
> + kvm_for_each_vcpu(i, vcpu, kvm) {
> + ret = kvm_create_rec(vcpu);
> + if (ret)
> + return ret;
> + }
> +
> ret = rmi_realm_activate(virt_to_phys(realm->rd));
> if (ret)
> return -ENXIO;
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 07/48] arm64: RMI: Check for RMI support at KVM init
From: Wei-Lin Chang @ 2026-03-19 18:05 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-8-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:31PM +0000, Steven Price wrote:
> Query the RMI version number and check if it is a compatible version. A
> static key is also provided to signal that a supported RMM is available.
>
> Functions are provided to query if a VM or VCPU is a realm (or rec)
> which currently will always return false.
>
> Later patches make use of struct realm and the states as the ioctls
> interfaces are added to support realm and REC creation and destruction.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Drop check for 4k page size.
> Changes since v11:
> * Reword slightly the comments on the realm states.
> Changes since v10:
> * kvm_is_realm() no longer has a NULL check.
> * Rename from "rme" to "rmi" when referring to the RMM interface.
> * Check for RME (hardware) support before probing for RMI support.
> Changes since v8:
> * No need to guard kvm_init_rme() behind 'in_hyp_mode'.
> Changes since v6:
> * Improved message for an unsupported RMI ABI version.
> Changes since v5:
> * Reword "unsupported" message from "host supports" to "we want" to
> clarify that 'we' are the 'host'.
> Changes since v2:
> * Drop return value from kvm_init_rme(), it was always 0.
> * Rely on the RMM return value to identify whether the RSI ABI is
> compatible.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 18 +++++++++
> arch/arm64/include/asm/kvm_host.h | 4 ++
> arch/arm64/include/asm/kvm_rmi.h | 56 +++++++++++++++++++++++++++
> arch/arm64/include/asm/virt.h | 1 +
> arch/arm64/kernel/cpufeature.c | 1 +
> arch/arm64/kvm/Makefile | 2 +-
> arch/arm64/kvm/arm.c | 5 +++
> arch/arm64/kvm/rmi.c | 57 ++++++++++++++++++++++++++++
> 8 files changed, 143 insertions(+), 1 deletion(-)
> create mode 100644 arch/arm64/include/asm/kvm_rmi.h
> create mode 100644 arch/arm64/kvm/rmi.c
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index 5bf3d7e1d92c..f38b50151ce8 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -688,4 +688,22 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu)
> vcpu->arch.hcrx_el2 |= HCRX_EL2_EnASR;
> }
> }
> +
> +static inline bool kvm_is_realm(struct kvm *kvm)
> +{
> + if (static_branch_unlikely(&kvm_rmi_is_available))
> + return kvm->arch.is_realm;
> + return false;
> +}
> +
> +static inline enum realm_state kvm_realm_state(struct kvm *kvm)
> +{
> + return READ_ONCE(kvm->arch.realm.state);
> +}
Hi,
Do you think it would be helpful to have a write version of this?
That way we can search for the write version to see all the locations of
realm state changes, instead of having to search through all the
WRITE_ONCE()'s.
Thanks,
Wei-Lin Chang
> +
> +static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> +{
> + return false;
> +}
> +
> #endif /* __ARM64_KVM_EMULATE_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 5d5a3bbdb95e..9267a2f2d65b 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -27,6 +27,7 @@
> #include <asm/fpsimd.h>
> #include <asm/kvm.h>
> #include <asm/kvm_asm.h>
> +#include <asm/kvm_rmi.h>
> #include <asm/vncr_mapping.h>
>
> #define __KVM_HAVE_ARCH_INTC_INITIALIZED
> @@ -405,6 +406,9 @@ struct kvm_arch {
> * the associated pKVM instance in the hypervisor.
> */
> struct kvm_protected_vm pkvm;
> +
> + bool is_realm;
> + struct realm realm;
> };
>
> struct kvm_vcpu_fault_info {
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> new file mode 100644
> index 000000000000..3506f50b05cd
> --- /dev/null
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023-2025 ARM Ltd.
> + */
> +
> +#ifndef __ASM_KVM_RMI_H
> +#define __ASM_KVM_RMI_H
> +
> +/**
> + * enum realm_state - State of a Realm
> + */
> +enum realm_state {
> + /**
> + * @REALM_STATE_NONE:
> + * Realm has not yet been created. rmi_realm_create() has not
> + * yet been called.
> + */
> + REALM_STATE_NONE,
> + /**
> + * @REALM_STATE_NEW:
> + * Realm is under construction, rmi_realm_create() has been
> + * called, but it is not yet activated. Pages may be populated.
> + */
> + REALM_STATE_NEW,
> + /**
> + * @REALM_STATE_ACTIVE:
> + * Realm has been created and is eligible for execution with
> + * rmi_rec_enter(). Pages may no longer be populated with
> + * rmi_data_create().
> + */
> + REALM_STATE_ACTIVE,
> + /**
> + * @REALM_STATE_DYING:
> + * Realm is in the process of being destroyed or has already been
> + * destroyed.
> + */
> + REALM_STATE_DYING,
> + /**
> + * @REALM_STATE_DEAD:
> + * Realm has been destroyed.
> + */
> + REALM_STATE_DEAD
> +};
> +
> +/**
> + * struct realm - Additional per VM data for a Realm
> + *
> + * @state: The lifetime state machine for the realm
> + */
> +struct realm {
> + enum realm_state state;
> +};
> +
> +void kvm_init_rmi(void);
> +
> +#endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
> index b51ab6840f9c..dc9b2899e0b2 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/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index c31f8e17732a..ddf7e57f23e8 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -289,6 +289,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {
> static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),
> + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 3ebc0570345c..e17c4077d8e7 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 29f0326f7e00..274d7866efdc 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -39,6 +39,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>
>
> @@ -104,6 +105,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);
> @@ -2921,6 +2924,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/rmi.c b/arch/arm64/kvm/rmi.c
> new file mode 100644
> index 000000000000..fac151580c01
> --- /dev/null
> +++ b/arch/arm64/kvm/rmi.c
> @@ -0,0 +1,57 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023-2025 ARM Ltd.
> + */
> +
> +#include <linux/kvm_host.h>
> +
> +#include <asm/rmi_cmds.h>
> +#include <asm/virt.h>
> +
> +static int rmi_check_version(void)
> +{
> + struct arm_smccc_res res;
> + unsigned short version_major, version_minor;
> + unsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,
> + RMI_ABI_MINOR_VERSION);
> + unsigned long aa64pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +
> + /* If RME isn't supported, then RMI can't be */
> + if (cpuid_feature_extract_unsigned_field(aa64pfr0, ID_AA64PFR0_EL1_RME_SHIFT) == 0)
> + return -ENXIO;
> +
> + arm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, &res);
> +
> + if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
> + return -ENXIO;
> +
> + version_major = RMI_ABI_VERSION_GET_MAJOR(res.a1);
> + version_minor = RMI_ABI_VERSION_GET_MINOR(res.a1);
> +
> + if (res.a0 != RMI_SUCCESS) {
> + unsigned short high_version_major, high_version_minor;
> +
> + high_version_major = RMI_ABI_VERSION_GET_MAJOR(res.a2);
> + high_version_minor = RMI_ABI_VERSION_GET_MINOR(res.a2);
> +
> + kvm_err("Unsupported RMI ABI (v%d.%d - v%d.%d) we want v%d.%d\n",
> + version_major, version_minor,
> + high_version_major, high_version_minor,
> + RMI_ABI_MAJOR_VERSION,
> + RMI_ABI_MINOR_VERSION);
> + return -ENXIO;
> + }
> +
> + kvm_info("RMI ABI version %d.%d\n", version_major, version_minor);
> +
> + return 0;
> +}
> +
> +void kvm_init_rmi(void)
> +{
> + /* Continue without realm support if we can't agree on a version */
> + if (rmi_check_version())
> + return;
> +
> + /* Future patch will enable static branch kvm_rmi_is_available */
> +}
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH 1/2] x86/virt/tdx: Use PFN directly for mapping guest private memory
From: Dave Hansen @ 2026-03-19 18:05 UTC (permalink / raw)
To: Yan Zhao, seanjc, pbonzini, dave.hansen
Cc: tglx, mingo, bp, kas, x86, linux-kernel, kvm, linux-coco,
kai.huang, rick.p.edgecombe, yilun.xu, vannapurve, ackerleytng,
sagis, binbin.wu, xiaoyao.li, isaku.yamahata
In-Reply-To: <20260319005703.8983-1-yan.y.zhao@intel.com>
On 3/18/26 17:57, Yan Zhao wrote:
> Remove the completely unnecessary assumption that memory mapped into a TDX
> guest is backed by refcounted struct page memory. From KVM's point of view,
> TDH_MEM_PAGE_ADD and TDH_MEM_PAGE_AUG are glorified writes to PTEs, so they
> have no business placing requirements on how KVM and guest_memfd manage
> memory.
I think this goes a bit too far.
It's one thing to say that it's more convenient for KVM to stick with
pfns because it's what KVM uses now. Or, that the goals of using 'struct
page' can be accomplished other ways. It's quite another to say what
other bits of the codebase have "business" doing.
Sean, can we tone this down a _bit_ to help guide folks in the future?
> Rip out the misguided struct page assumptions/constraints and instead have
Could we maybe tone down the editorializing a bit, please? Folks can
have honest disagreements about this stuff while not being "misguided".
> the two SEAMCALL wrapper APIs take PFN directly. This ensures that for
> future huge page support in S-EPT, the kernel doesn't pick up even worse
> assumptions like "a hugepage must be contained in a single folio".
I don't really understand what this is saying.
Is the concern that KVM might want to set up page tables for memory that
differ from how it was allocated? I'm a bit worried that this assumes
something about folios that doesn't always hold.
I think the hugetlbfs gigantic support uses folios in at least a few
spots today.
^ permalink raw reply
* Re: [PATCH] sample/tsm-mr: Use SHA-2 library APIs
From: Dan Williams @ 2026-03-19 17:51 UTC (permalink / raw)
To: Eric Biggers, Arnd Bergmann
Cc: linux-crypto, Arnd Bergmann, Dan Williams, Dionna Amalie Glaze,
Cedric Xing, Andrew Morton, Zi Li, Masami Hiramatsu, Lance Yang,
Zhou Yuhang, Colin Ian King, Ard Biesheuvel, linux-coco,
linux-kernel
In-Reply-To: <20260319173222.GA10208@quark>
Eric Biggers wrote:
> On Wed, Mar 18, 2026 at 08:57:01PM +0100, Arnd Bergmann wrote:
> > On Wed, Mar 18, 2026, at 17:42, Eric Biggers wrote:
> > > Given that tsm_mr_sample has a particular set of algorithms that it
> > > wants, just use the library APIs for those algorithms rather than
> > > crypto_shash. This is more straightforward and a bit more efficient.
> > >
> > > This fixes an issue where this module failed to build due to the kconfig
> > > options CRYPTO and CRYPTO_HASH not being selected. Also, even if it
> > > built, crypto_alloc_shash() could fail at runtime due to the needed
> > > algorithms not being available.
> > >
> > > The library functions simply use direct linking. So if it builds, which
> > > it will due to the kconfig options being enabled, they are available.
> > >
> > > Fixes: f6953f1f9ec4 ("tsm-mr: Add tsm-mr sample code")
> > > Fixes: 44a3873df811 ("coco/guest: Remove unneeded selection of CRYPTO")
> > > Signed-off-by: Eric Biggers <ebiggers@kernel.org>
> > > ---
> > >
> > > I'd like to take this via libcrypto-next, as that is where
> > > "coco/guest: Remove unneeded selection of CRYPTO" is.
> >
> > Thanks for fixing this! It is indeed nicer than the fix
> > I sent earlier today.
> >
> > Acked-by: Arnd Bergmann <arnd@arndb.de>
>
> Thanks. Additional acks from the people owning this code (Dan, Cedric?)
> would be appreciated. But since this fixes a build error and is related
> to the crypto library, I went ahead and applied this to
> https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/log/?h=libcrypto-next
>
> I also found that the build error is pre-existing, as CRYPTO_HASH was
> not being selected. "coco/guest: Remove unneeded selection of CRYPTO"
> just made it a bit easier to encounter, by not selecting CRYPTO either.
>
> So I updated the second paragraph of the commit message to:
>
> This also fixes a bug where this module failed to build if it was
> enabled without CRYPTO_HASH happening to be set elsewhere in the
> kconfig. (With the concurrent change to make TSM_MEASUREMENTS stop
> selecting CRYPTO, this existing build error would have become easier to
> encounter, as well.) Also, even if it built, crypto_alloc_shash() could
> fail at runtime due to the needed algorithms not being available.
>
> I also put this commit before "coco/guest: Remove unneeded selection of
> CRYPTO" and dropped the Fixes reference to that. So now it just has:
>
> Fixes: f6953f1f9ec4 ("tsm-mr: Add tsm-mr sample code")
It looks good to me:
Acked-by: Dan Williams <dan.j.williams@intel.com>
Feel free to take it through your tree since I have nothing immediately
pending for tsm.git.
^ permalink raw reply
* Re: [PATCH v13 46/48] KVM: arm64: Expose KVM_ARM_VCPU_REC to user space
From: Suzuki K Poulose @ 2026-03-19 17:36 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260318155413.793430-47-steven.price@arm.com>
On 18/03/2026 15:54, Steven Price wrote:
> Increment KVM_VCPU_MAX_FEATURES to expose the new capability to user
> space.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Reviewed-by: Gavin Shan <gshan@redhat.com>
Not needed any more as we don't need the VCPU feature.
Cheers
Suzuki
> ---
> Changes since v8:
> * Since NV is now merged and enabled, this no longer conflicts with it.
> ---
> arch/arm64/include/asm/kvm_host.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 1d5fb001408c..b02f97de4436 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -40,7 +40,7 @@
>
> #define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS
>
> -#define KVM_VCPU_MAX_FEATURES 9
> +#define KVM_VCPU_MAX_FEATURES 10
> #define KVM_VCPU_VALID_FEATURES (BIT(KVM_VCPU_MAX_FEATURES) - 1)
>
> #define KVM_REQ_SLEEP \
^ permalink raw reply
* Re: [PATCH v13 15/48] arm64: RMI: RTT tear down
From: Wei-Lin Chang @ 2026-03-19 17:35 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-16-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:39PM +0000, Steven Price wrote:
> The RMM owns the stage 2 page tables for a realm, and KVM must request
> that the RMM creates/destroys entries as necessary. The physical pages
> to store the page tables are delegated to the realm as required, and can
> be undelegated when no longer used.
>
> Creating new RTTs is the easy part, tearing down is a little more
> tricky. The result of realm_rtt_destroy() can be used to effectively
> walk the tree and destroy the entries (undelegating pages that were
> given to the realm).
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Simplify some functions now we know RMM page size is the same as the
> host's.
> Changes since v11:
> * Moved some code from earlier in the series to this one so that it's
> added when it's first used.
> Changes since v10:
> * RME->RMI rename.
> * Some code to handle freeing stage 2 PGD moved into this patch where
> it belongs.
> Changes since v9:
> * Add a comment clarifying that root level RTTs are not destroyed until
> after the RD is destroyed.
> Changes since v8:
> * Introduce free_rtt() wrapper which calls free_delegated_granule()
> followed by kvm_account_pgtable_pages(). This makes it clear where an
> RTT is being freed rather than just a delegated granule.
> Changes since v6:
> * Move rme_rtt_level_mapsize() and supporting defines from kvm_rme.h
> into rme.c as they are only used in that file.
> Changes since v5:
> * Rename some RME_xxx defines to do with page sizes as RMM_xxx - they are
> a property of the RMM specification not the RME architecture.
> Changes since v2:
> * Moved {alloc,free}_delegated_page() and ensure_spare_page() to a
> later patch when they are actually used.
> * Some simplifications now rmi_xxx() functions allow NULL as an output
> parameter.
> * Improved comments and code layout.
> ---
> arch/arm64/include/asm/kvm_rmi.h | 7 ++
> arch/arm64/kvm/mmu.c | 15 +++-
> arch/arm64/kvm/rmi.c | 145 +++++++++++++++++++++++++++++++
> 3 files changed, 166 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 0ada525af18f..16a297f3091a 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -68,5 +68,12 @@ u32 kvm_realm_ipa_limit(void);
>
> int kvm_init_realm_vm(struct kvm *kvm);
> void kvm_destroy_realm(struct kvm *kvm);
> +void kvm_realm_destroy_rtts(struct kvm *kvm);
> +
> +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/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 9dc242c3b9c8..41152abf55b2 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1098,10 +1098,23 @@ void stage2_unmap_vm(struct kvm *kvm)
> void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
> {
> struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
> - struct kvm_pgtable *pgt = NULL;
> + struct kvm_pgtable *pgt;
>
> write_lock(&kvm->mmu_lock);
> pgt = mmu->pgt;
> + if (kvm_is_realm(kvm) &&
> + (kvm_realm_state(kvm) != REALM_STATE_DEAD &&
> + kvm_realm_state(kvm) != REALM_STATE_NONE)) {
> + write_unlock(&kvm->mmu_lock);
> + kvm_realm_destroy_rtts(kvm);
> +
> + /*
> + * The PGD pages can be reclaimed only after the realm (RD) is
> + * destroyed. We call this again from kvm_destroy_realm() after
> + * the RD is destroyed.
> + */
> + return;
> + }
Hi,
I see that kvm_free_stage2_pgd() will be called twice:
kvm_destroy_vm()
mmu_notifier_unregister()
kvm_mmu_notifier_release()
kvm_flush_shadow_all()
kvm_arch_flush_shadow_all()
kvm_uninit_stage2_mmu()
kvm_free_stage2_pgd()
kvm_arch_destroy_vm()
kvm_destroy_realm()
kvm_free_stage2_pgd()
At the first call the realm state is REALM_STATE_ACTIVE, at the second
it is REALM_STATE_DEAD. Reading the comment added to
kvm_free_stage2_pgd() here, does it mean this function is called twice
on purpose? If so do you think it's better to extract this and create
another function instead, then use kvm_is_realm() to choose which to
run? I think it is confusing to have this function run twice for a
realm.
Thanks,
Wei-Lin Chang
> if (pgt) {
> mmu->pgd_phys = 0;
> mmu->pgt = NULL;
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 700b8c935d29..1fd2c18f7381 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -15,6 +15,19 @@
> static unsigned long rmm_feat_reg0;
> static unsigned long rmm_feat_reg1;
>
> +#define RMM_RTT_BLOCK_LEVEL 2
> +#define RMM_RTT_MAX_LEVEL 3
> +
> +#define RMM_L2_BLOCK_SIZE PMD_SIZE
> +
> +static inline unsigned long rmi_rtt_level_mapsize(int level)
> +{
> + if (WARN_ON(level > RMM_RTT_MAX_LEVEL))
> + return PAGE_SIZE;
> +
> + return (1UL << ARM64_HW_PGTABLE_LEVEL_SHIFT(level));
> +}
> +
> static bool rmi_has_feature(unsigned long feature)
> {
> return !!u64_get_bits(rmm_feat_reg0, feature);
> @@ -189,6 +202,11 @@ u32 kvm_realm_ipa_limit(void)
> return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
> }
>
> +static int get_start_level(struct realm *realm)
> +{
> + return 4 - stage2_pgtable_levels(realm->ia_bits);
> +}
> +
> static int undelegate_range(phys_addr_t phys, unsigned long size)
> {
> unsigned long ret;
> @@ -223,6 +241,131 @@ static int free_delegated_page(phys_addr_t phys)
> return 0;
> }
>
> +static void free_rtt(phys_addr_t phys)
> +{
> + if (free_delegated_page(phys))
> + return;
> +
> + kvm_account_pgtable_pages(phys_to_virt(phys), -1);
> +}
> +
> +static int realm_rtt_destroy(struct realm *realm, unsigned long addr,
> + int level, phys_addr_t *rtt_granule,
> + unsigned long *next_addr)
> +{
> + unsigned long out_rtt;
> + int 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_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 > RMM_RTT_MAX_LEVEL))
> + return -EINVAL;
> +
> + map_size = rmi_rtt_level_mapsize(level - 1);
> +
> + for (addr = start; addr < end; addr = next_addr) {
> + phys_addr_t rtt_granule;
> + int 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);
> +
> + switch (RMI_RETURN_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_RETURN_INDEX(ret) != level))
> + return -EBUSY;
> + if (WARN_ON(level == RMM_RTT_MAX_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);
> +}
> +
> +void kvm_realm_destroy_rtts(struct kvm *kvm)
> +{
> + struct realm *realm = &kvm->arch.realm;
> + unsigned int ia_bits = realm->ia_bits;
> +
> + WARN_ON(realm_tear_down_rtt_range(realm, 0, (1UL << ia_bits)));
> +}
> +
> void kvm_destroy_realm(struct kvm *kvm)
> {
> struct realm *realm = &kvm->arch.realm;
> @@ -246,6 +389,8 @@ void kvm_destroy_realm(struct kvm *kvm)
> if (realm->rd) {
> phys_addr_t rd_phys = virt_to_phys(realm->rd);
>
> + kvm_realm_destroy_rtts(kvm);
> +
> if (WARN_ON(rmi_realm_destroy(rd_phys)))
> return;
> free_delegated_page(rd_phys);
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH] sample/tsm-mr: Use SHA-2 library APIs
From: Eric Biggers @ 2026-03-19 17:32 UTC (permalink / raw)
To: Arnd Bergmann
Cc: linux-crypto, Arnd Bergmann, Dan Williams, Dionna Amalie Glaze,
Cedric Xing, Andrew Morton, Zi Li, Masami Hiramatsu, Lance Yang,
Zhou Yuhang, Colin Ian King, Ard Biesheuvel, linux-coco,
linux-kernel
In-Reply-To: <dc124ea8-05b8-42d2-93ad-d265e0ecf585@app.fastmail.com>
On Wed, Mar 18, 2026 at 08:57:01PM +0100, Arnd Bergmann wrote:
> On Wed, Mar 18, 2026, at 17:42, Eric Biggers wrote:
> > Given that tsm_mr_sample has a particular set of algorithms that it
> > wants, just use the library APIs for those algorithms rather than
> > crypto_shash. This is more straightforward and a bit more efficient.
> >
> > This fixes an issue where this module failed to build due to the kconfig
> > options CRYPTO and CRYPTO_HASH not being selected. Also, even if it
> > built, crypto_alloc_shash() could fail at runtime due to the needed
> > algorithms not being available.
> >
> > The library functions simply use direct linking. So if it builds, which
> > it will due to the kconfig options being enabled, they are available.
> >
> > Fixes: f6953f1f9ec4 ("tsm-mr: Add tsm-mr sample code")
> > Fixes: 44a3873df811 ("coco/guest: Remove unneeded selection of CRYPTO")
> > Signed-off-by: Eric Biggers <ebiggers@kernel.org>
> > ---
> >
> > I'd like to take this via libcrypto-next, as that is where
> > "coco/guest: Remove unneeded selection of CRYPTO" is.
>
> Thanks for fixing this! It is indeed nicer than the fix
> I sent earlier today.
>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
Thanks. Additional acks from the people owning this code (Dan, Cedric?)
would be appreciated. But since this fixes a build error and is related
to the crypto library, I went ahead and applied this to
https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/log/?h=libcrypto-next
I also found that the build error is pre-existing, as CRYPTO_HASH was
not being selected. "coco/guest: Remove unneeded selection of CRYPTO"
just made it a bit easier to encounter, by not selecting CRYPTO either.
So I updated the second paragraph of the commit message to:
This also fixes a bug where this module failed to build if it was
enabled without CRYPTO_HASH happening to be set elsewhere in the
kconfig. (With the concurrent change to make TSM_MEASUREMENTS stop
selecting CRYPTO, this existing build error would have become easier to
encounter, as well.) Also, even if it built, crypto_alloc_shash() could
fail at runtime due to the needed algorithms not being available.
I also put this commit before "coco/guest: Remove unneeded selection of
CRYPTO" and dropped the Fixes reference to that. So now it just has:
Fixes: f6953f1f9ec4 ("tsm-mr: Add tsm-mr sample code")
- Eric
^ permalink raw reply
* Re: [PATCH 1/2] x86/virt/tdx: Use PFN directly for mapping guest private memory
From: Edgecombe, Rick P @ 2026-03-19 17:27 UTC (permalink / raw)
To: kas@kernel.org, Zhao, Yan Y
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
Li, Xiaoyao, dave.hansen@linux.intel.com,
linux-kernel@vger.kernel.org, binbin.wu@linux.intel.com,
seanjc@google.com, pbonzini@redhat.com, mingo@redhat.com,
ackerleytng@google.com, Yamahata, Isaku, sagis@google.com,
Annapurve, Vishal, bp@alien8.de, tglx@kernel.org,
yilun.xu@linux.intel.com, x86@kernel.org
In-Reply-To: <abvxiuJfK2eM_1UX@thinkstation>
On Thu, 2026-03-19 at 12:57 +0000, Kiryl Shutsemau wrote:
> > Though tdh_mem_page_aug() is an API, it is currently only exported to KVM
> > and
> > uses type kvm_pfn_t. So, is it still acceptable to assume flush size to be
> > PAGE_SIZE? Honoring level will soon be introduced by huge page patches.
>
> It caught my eye because previously size to flush was passed down to
> tdx_clflush_page() in the struct page (although never used there).
> With switching to pfn, we give up this information and it has to be
> passed separately. It would be easy to miss that in huge page patches,
> if we don't pass down level here.
>
> >
> > If you think it needs to be fixed before huge page series, what about fixing
> > it
> > in a separate cleanup patch? IMO, it would be better placed after Sean's
> > cleanup
> > patch [1], so we can use page_level_size() instead of inventing the wheel.
>
> I am okay with a separate patch.
I feel like we argued about this before. But it would be more correct to just
drop level and make it 4k only until huge pages? Otherwise we are tweaking dead
behavior.
^ permalink raw reply
* Re: [PATCH v13 12/48] arm64: RMI: Basic infrastructure for creating a realm.
From: Wei-Lin Chang @ 2026-03-19 17:17 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-13-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:36PM +0000, Steven Price wrote:
> Introduce the skeleton functions for creating and destroying a realm.
> The IPA size requested is checked against what the RMM supports.
>
> The actual work of constructing the realm will be added in future
> patches.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Drop the RMM_PAGE_{SHIFT,SIZE} defines - the RMM is now configured to
> be the same as the host's page size.
> * Rework delegate/undelegate functions to use the new RMI range based
> operations.
> Changes since v11:
> * Major rework to drop the realm configuration and make the
> construction of realms implicit rather than driven by the VMM
> directly.
> * The code to create RDs, handle VMIDs etc is moved to later patches.
> Changes since v10:
> * Rename from RME to RMI.
> * Move the stage2 cleanup to a later patch.
> Changes since v9:
> * Avoid walking the stage 2 page tables when destroying the realm -
> the real ones are not accessible to the non-secure world, and the RMM
> may leave junk in the physical pages when returning them.
> * Fix an error path in realm_create_rd() to actually return an error value.
> Changes since v8:
> * Fix free_delegated_granule() to not call kvm_account_pgtable_pages();
> a separate wrapper will be introduced in a later patch to deal with
> RTTs.
> * Minor code cleanups following review.
> Changes since v7:
> * Minor code cleanup following Gavin's review.
> Changes since v6:
> * Separate RMM RTT calculations from host PAGE_SIZE. This allows the
> host page size to be larger than 4k while still communicating with an
> RMM which uses 4k granules.
> Changes since v5:
> * Introduce free_delegated_granule() to replace many
> undelegate/free_page() instances and centralise the comment on
> leaking when the undelegate fails.
> * Several other minor improvements suggested by reviews - thanks for
> the feedback!
> Changes since v2:
> * Improved commit description.
> * Improved return failures for rmi_check_version().
> * Clear contents of PGD after it has been undelegated in case the RMM
> left stale data.
> * Minor changes to reflect changes in previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 5 ++
> arch/arm64/include/asm/kvm_rmi.h | 16 +++++
> arch/arm64/kvm/arm.c | 12 ++++
> arch/arm64/kvm/mmu.c | 11 +++-
> arch/arm64/kvm/rmi.c | 88 ++++++++++++++++++++++++++++
> 5 files changed, 129 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index f38b50151ce8..39310d9b4e16 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -701,6 +701,11 @@ static inline enum realm_state kvm_realm_state(struct kvm *kvm)
> return READ_ONCE(kvm->arch.realm.state);
> }
>
> +static inline bool kvm_realm_is_created(struct kvm *kvm)
> +{
> + return kvm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
> +}
> +
> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> {
> return false;
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 3506f50b05cd..0ada525af18f 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -6,6 +6,8 @@
> #ifndef __ASM_KVM_RMI_H
> #define __ASM_KVM_RMI_H
>
> +#include <asm/rmi_smc.h>
> +
> /**
> * enum realm_state - State of a Realm
> */
> @@ -46,11 +48,25 @@ enum realm_state {
> * struct realm - Additional per VM data for a Realm
> *
> * @state: The lifetime state machine for the realm
> + * @rd: Kernel mapping of the Realm Descriptor (RD)
> + * @params: Parameters for the RMI_REALM_CREATE command
> + * @num_aux: The number of auxiliary pages required by the RMM
> + * @ia_bits: Number of valid Input Address bits in the IPA
> */
> struct realm {
> enum realm_state state;
> +
> + void *rd;
> + struct realm_params *params;
> +
> + unsigned long num_aux;
> + unsigned int ia_bits;
> };
>
> void kvm_init_rmi(void);
> +u32 kvm_realm_ipa_limit(void);
> +
> +int kvm_init_realm_vm(struct kvm *kvm);
> +void kvm_destroy_realm(struct kvm *kvm);
Hi,
Sorry I missed one nit: perhaps call this kvm_init_realm()? So these two
look like a pair. There are also no realm_vm in other function names.
Thanks,
Wei-Lin Chang
>
> #endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 274d7866efdc..9b17bdfaf0c2 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -253,6 +253,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_is_realm(kvm)) {
> + ret = kvm_init_realm_vm(kvm);
> + if (ret)
> + goto err_free_cpumask;
> + }
> +
> return 0;
>
> err_free_cpumask:
> @@ -312,6 +319,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
> kvm_unshare_hyp(kvm, kvm + 1);
>
> kvm_arm_teardown_hypercalls(kvm);
> + if (kvm_is_realm(kvm))
> + kvm_destroy_realm(kvm);
> }
>
> static bool kvm_has_full_ptr_auth(void)
> @@ -473,6 +482,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> else
> r = kvm_supports_cacheable_pfnmap();
> break;
> + case KVM_CAP_ARM_RMI:
> + r = static_key_enabled(&kvm_rmi_is_available);
> + break;
>
> default:
> r = 0;
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 070a01e53fcb..d6094b60c4ce 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -872,12 +872,16 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
> .icache_inval_pou = invalidate_icache_guest_page,
> };
>
> -static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
> +static int kvm_init_ipa_range(struct kvm *kvm,
> + struct kvm_s2_mmu *mmu, unsigned long type)
> {
> u32 kvm_ipa_limit = get_kvm_ipa_limit();
> u64 mmfr0, mmfr1;
> u32 phys_shift;
>
> + if (kvm_is_realm(kvm))
> + kvm_ipa_limit = kvm_realm_ipa_limit();
> +
> if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
> return -EINVAL;
>
> @@ -974,7 +978,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
> return -EINVAL;
> }
>
> - err = kvm_init_ipa_range(mmu, type);
> + err = kvm_init_ipa_range(kvm, mmu, type);
> if (err)
> return err;
>
> @@ -1113,7 +1117,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
> write_unlock(&kvm->mmu_lock);
>
> if (pgt) {
> - kvm_stage2_destroy(pgt);
> + if (!kvm_is_realm(kvm))
> + kvm_stage2_destroy(pgt);
> kfree(pgt);
> }
> }
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 80aedc85e94a..700b8c935d29 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -6,6 +6,8 @@
> #include <linux/kvm_host.h>
> #include <linux/memblock.h>
>
> +#include <asm/kvm_emulate.h>
> +#include <asm/kvm_mmu.h>
> #include <asm/kvm_pgtable.h>
> #include <asm/rmi_cmds.h>
> #include <asm/virt.h>
> @@ -182,6 +184,92 @@ static int rmi_init_metadata(void)
> return 0;
> }
>
> +u32 kvm_realm_ipa_limit(void)
> +{
> + return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
> +}
> +
> +static int undelegate_range(phys_addr_t phys, unsigned long size)
> +{
> + unsigned long ret;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + phys = out_top;
> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int undelegate_page(phys_addr_t phys)
> +{
> + return undelegate_range(phys, PAGE_SIZE);
> +}
> +
> +static int free_delegated_page(phys_addr_t phys)
> +{
> + if (WARN_ON(undelegate_page(phys))) {
> + /* Undelegate failed: leak the page */
> + return -EBUSY;
> + }
> +
> + free_page((unsigned long)phys_to_virt(phys));
> +
> + return 0;
> +}
> +
> +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);
> +
> + write_lock(&kvm->mmu_lock);
> + kvm_stage2_unmap_range(&kvm->arch.mmu, 0,
> + BIT(realm->ia_bits - 1), true);
> + write_unlock(&kvm->mmu_lock);
> +
> + if (realm->params) {
> + free_page((unsigned long)realm->params);
> + realm->params = NULL;
> + }
> +
> + if (!kvm_realm_is_created(kvm))
> + return;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_DYING);
> +
> + if (realm->rd) {
> + phys_addr_t rd_phys = virt_to_phys(realm->rd);
> +
> + if (WARN_ON(rmi_realm_destroy(rd_phys)))
> + return;
> + free_delegated_page(rd_phys);
> + realm->rd = NULL;
> + }
> +
> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, pgd_size)))
> + return;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_DEAD);
> +
> + /* Now that the Realm is destroyed, free the entry level RTTs */
> + kvm_free_stage2_pgd(&kvm->arch.mmu);
> +}
> +
> +int kvm_init_realm_vm(struct kvm *kvm)
> +{
> + kvm->arch.realm.params = (void *)get_zeroed_page(GFP_KERNEL);
> +
> + if (!kvm->arch.realm.params)
> + return -ENOMEM;
> + return 0;
> +}
> +
> static int rmm_check_features(void)
> {
> if (kvm_lpa2_is_enabled() && !rmi_has_feature(RMI_FEATURE_REGISTER_0_LPA2)) {
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 07/48] arm64: RMI: Check for RMI support at KVM init
From: Steven Price @ 2026-03-19 16:42 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <yogcawvd7444m6lbk3nz7y57zx6wdxvoxccyaddpkbniug27nw@zdihyc6xvrbx>
On 19/03/2026 16:17, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:53:31PM +0000, Steven Price wrote:
>> Query the RMI version number and check if it is a compatible version. A
>> static key is also provided to signal that a supported RMM is available.
>>
>> Functions are provided to query if a VM or VCPU is a realm (or rec)
>> which currently will always return false.
>>
>> Later patches make use of struct realm and the states as the ioctls
>> interfaces are added to support realm and REC creation and destruction.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v12:
>> * Drop check for 4k page size.
>> Changes since v11:
>> * Reword slightly the comments on the realm states.
>> Changes since v10:
>> * kvm_is_realm() no longer has a NULL check.
>> * Rename from "rme" to "rmi" when referring to the RMM interface.
>> * Check for RME (hardware) support before probing for RMI support.
>> Changes since v8:
>> * No need to guard kvm_init_rme() behind 'in_hyp_mode'.
>> Changes since v6:
>> * Improved message for an unsupported RMI ABI version.
>> Changes since v5:
>> * Reword "unsupported" message from "host supports" to "we want" to
>> clarify that 'we' are the 'host'.
>> Changes since v2:
>> * Drop return value from kvm_init_rme(), it was always 0.
>> * Rely on the RMM return value to identify whether the RSI ABI is
>> compatible.
>> ---
>> arch/arm64/include/asm/kvm_emulate.h | 18 +++++++++
>> arch/arm64/include/asm/kvm_host.h | 4 ++
>> arch/arm64/include/asm/kvm_rmi.h | 56 +++++++++++++++++++++++++++
>> arch/arm64/include/asm/virt.h | 1 +
>> arch/arm64/kernel/cpufeature.c | 1 +
>> arch/arm64/kvm/Makefile | 2 +-
>> arch/arm64/kvm/arm.c | 5 +++
>> arch/arm64/kvm/rmi.c | 57 ++++++++++++++++++++++++++++
>> 8 files changed, 143 insertions(+), 1 deletion(-)
>> create mode 100644 arch/arm64/include/asm/kvm_rmi.h
>> create mode 100644 arch/arm64/kvm/rmi.c
>>
>> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
>> index 5bf3d7e1d92c..f38b50151ce8 100644
>> --- a/arch/arm64/include/asm/kvm_emulate.h
>> +++ b/arch/arm64/include/asm/kvm_emulate.h
>> @@ -688,4 +688,22 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu)
>> vcpu->arch.hcrx_el2 |= HCRX_EL2_EnASR;
>> }
>> }
>> +
>> +static inline bool kvm_is_realm(struct kvm *kvm)
>> +{
>> + if (static_branch_unlikely(&kvm_rmi_is_available))
>> + return kvm->arch.is_realm;
>> + return false;
>> +}
>> +
>> +static inline enum realm_state kvm_realm_state(struct kvm *kvm)
>> +{
>> + return READ_ONCE(kvm->arch.realm.state);
>> +}
>> +
>> +static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
>> +{
>> + return false;
>> +}
>> +
>
> Hi,
>
> Both kvm_vm_is_protected() and vcpu_is_protected() are in kvm_host.h, do
> you think that's a better place? Or is there a reason for this being in
> kvm_emulate.h?
I've no strong opinions, but as usual there are complications with the
header include order.
kvm_vm_is_protected/vcpu_is_protected are defined in asm/kvm_host.h, but
struct kvm is defined in linux/kvm_host.h (which includes
asm/kvm_host.h). The same is true for struct kvm_vcpu. This means that
we can't have a static inline function using the kvm pointer (because
the struct definition hasn't been reached). The 'solution' in the case
of kvm_vm_is_protected/vcpu_is_protected is to use a macro - which works
but has drawbacks (e.g. lack of type checking).
I'll move them if there's a strong feeling they are in the wrong place,
but to me it feels more like the macros in asm/kvm_host.h are just a
hack to get around them being in the wrong place.
Thanks,
Steve
> Thanks,
> Wei-Lin Chang
>
>> #endif /* __ARM64_KVM_EMULATE_H__ */
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>> index 5d5a3bbdb95e..9267a2f2d65b 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -27,6 +27,7 @@
>> #include <asm/fpsimd.h>
>> #include <asm/kvm.h>
>> #include <asm/kvm_asm.h>
>> +#include <asm/kvm_rmi.h>
>> #include <asm/vncr_mapping.h>
>>
>> #define __KVM_HAVE_ARCH_INTC_INITIALIZED
>> @@ -405,6 +406,9 @@ struct kvm_arch {
>> * the associated pKVM instance in the hypervisor.
>> */
>> struct kvm_protected_vm pkvm;
>> +
>> + bool is_realm;
>> + struct realm realm;
>> };
>>
>> struct kvm_vcpu_fault_info {
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
>> new file mode 100644
>> index 000000000000..3506f50b05cd
>> --- /dev/null
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -0,0 +1,56 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023-2025 ARM Ltd.
>> + */
>> +
>> +#ifndef __ASM_KVM_RMI_H
>> +#define __ASM_KVM_RMI_H
>> +
>> +/**
>> + * enum realm_state - State of a Realm
>> + */
>> +enum realm_state {
>> + /**
>> + * @REALM_STATE_NONE:
>> + * Realm has not yet been created. rmi_realm_create() has not
>> + * yet been called.
>> + */
>> + REALM_STATE_NONE,
>> + /**
>> + * @REALM_STATE_NEW:
>> + * Realm is under construction, rmi_realm_create() has been
>> + * called, but it is not yet activated. Pages may be populated.
>> + */
>> + REALM_STATE_NEW,
>> + /**
>> + * @REALM_STATE_ACTIVE:
>> + * Realm has been created and is eligible for execution with
>> + * rmi_rec_enter(). Pages may no longer be populated with
>> + * rmi_data_create().
>> + */
>> + REALM_STATE_ACTIVE,
>> + /**
>> + * @REALM_STATE_DYING:
>> + * Realm is in the process of being destroyed or has already been
>> + * destroyed.
>> + */
>> + REALM_STATE_DYING,
>> + /**
>> + * @REALM_STATE_DEAD:
>> + * Realm has been destroyed.
>> + */
>> + REALM_STATE_DEAD
>> +};
>> +
>> +/**
>> + * struct realm - Additional per VM data for a Realm
>> + *
>> + * @state: The lifetime state machine for the realm
>> + */
>> +struct realm {
>> + enum realm_state state;
>> +};
>> +
>> +void kvm_init_rmi(void);
>> +
>> +#endif /* __ASM_KVM_RMI_H */
>> diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
>> index b51ab6840f9c..dc9b2899e0b2 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/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
>> index c31f8e17732a..ddf7e57f23e8 100644
>> --- a/arch/arm64/kernel/cpufeature.c
>> +++ b/arch/arm64/kernel/cpufeature.c
>> @@ -289,6 +289,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {
>> static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
>> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),
>> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),
>> + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),
>> ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),
>> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),
>> ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),
>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>> index 3ebc0570345c..e17c4077d8e7 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 29f0326f7e00..274d7866efdc 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -39,6 +39,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>
>>
>> @@ -104,6 +105,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);
>> @@ -2921,6 +2924,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/rmi.c b/arch/arm64/kvm/rmi.c
>> new file mode 100644
>> index 000000000000..fac151580c01
>> --- /dev/null
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -0,0 +1,57 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023-2025 ARM Ltd.
>> + */
>> +
>> +#include <linux/kvm_host.h>
>> +
>> +#include <asm/rmi_cmds.h>
>> +#include <asm/virt.h>
>> +
>> +static int rmi_check_version(void)
>> +{
>> + struct arm_smccc_res res;
>> + unsigned short version_major, version_minor;
>> + unsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,
>> + RMI_ABI_MINOR_VERSION);
>> + unsigned long aa64pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
>> +
>> + /* If RME isn't supported, then RMI can't be */
>> + if (cpuid_feature_extract_unsigned_field(aa64pfr0, ID_AA64PFR0_EL1_RME_SHIFT) == 0)
>> + return -ENXIO;
>> +
>> + arm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, &res);
>> +
>> + if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
>> + return -ENXIO;
>> +
>> + version_major = RMI_ABI_VERSION_GET_MAJOR(res.a1);
>> + version_minor = RMI_ABI_VERSION_GET_MINOR(res.a1);
>> +
>> + if (res.a0 != RMI_SUCCESS) {
>> + unsigned short high_version_major, high_version_minor;
>> +
>> + high_version_major = RMI_ABI_VERSION_GET_MAJOR(res.a2);
>> + high_version_minor = RMI_ABI_VERSION_GET_MINOR(res.a2);
>> +
>> + kvm_err("Unsupported RMI ABI (v%d.%d - v%d.%d) we want v%d.%d\n",
>> + version_major, version_minor,
>> + high_version_major, high_version_minor,
>> + RMI_ABI_MAJOR_VERSION,
>> + RMI_ABI_MINOR_VERSION);
>> + return -ENXIO;
>> + }
>> +
>> + kvm_info("RMI ABI version %d.%d\n", version_major, version_minor);
>> +
>> + return 0;
>> +}
>> +
>> +void kvm_init_rmi(void)
>> +{
>> + /* Continue without realm support if we can't agree on a version */
>> + if (rmi_check_version())
>> + return;
>> +
>> + /* Future patch will enable static branch kvm_rmi_is_available */
>> +}
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH v13 12/48] arm64: RMI: Basic infrastructure for creating a realm.
From: Steven Price @ 2026-03-19 16:24 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <lt5yozoeeqld5laooopzjwngqlso2hdpjcwkkyagbcg4vedkd6@potsbanqeb4h>
On 19/03/2026 16:11, Wei-Lin Chang wrote:
> On Wed, Mar 18, 2026 at 03:53:36PM +0000, Steven Price wrote:
>> Introduce the skeleton functions for creating and destroying a realm.
>> The IPA size requested is checked against what the RMM supports.
>>
>> The actual work of constructing the realm will be added in future
>> patches.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v12:
>> * Drop the RMM_PAGE_{SHIFT,SIZE} defines - the RMM is now configured to
>> be the same as the host's page size.
>> * Rework delegate/undelegate functions to use the new RMI range based
>> operations.
>> Changes since v11:
>> * Major rework to drop the realm configuration and make the
>> construction of realms implicit rather than driven by the VMM
>> directly.
>> * The code to create RDs, handle VMIDs etc is moved to later patches.
>> Changes since v10:
>> * Rename from RME to RMI.
>> * Move the stage2 cleanup to a later patch.
>> Changes since v9:
>> * Avoid walking the stage 2 page tables when destroying the realm -
>> the real ones are not accessible to the non-secure world, and the RMM
>> may leave junk in the physical pages when returning them.
>> * Fix an error path in realm_create_rd() to actually return an error value.
>> Changes since v8:
>> * Fix free_delegated_granule() to not call kvm_account_pgtable_pages();
>> a separate wrapper will be introduced in a later patch to deal with
>> RTTs.
>> * Minor code cleanups following review.
>> Changes since v7:
>> * Minor code cleanup following Gavin's review.
>> Changes since v6:
>> * Separate RMM RTT calculations from host PAGE_SIZE. This allows the
>> host page size to be larger than 4k while still communicating with an
>> RMM which uses 4k granules.
>> Changes since v5:
>> * Introduce free_delegated_granule() to replace many
>> undelegate/free_page() instances and centralise the comment on
>> leaking when the undelegate fails.
>> * Several other minor improvements suggested by reviews - thanks for
>> the feedback!
>> Changes since v2:
>> * Improved commit description.
>> * Improved return failures for rmi_check_version().
>> * Clear contents of PGD after it has been undelegated in case the RMM
>> left stale data.
>> * Minor changes to reflect changes in previous patches.
>> ---
>> arch/arm64/include/asm/kvm_emulate.h | 5 ++
>> arch/arm64/include/asm/kvm_rmi.h | 16 +++++
>> arch/arm64/kvm/arm.c | 12 ++++
>> arch/arm64/kvm/mmu.c | 11 +++-
>> arch/arm64/kvm/rmi.c | 88 ++++++++++++++++++++++++++++
>> 5 files changed, 129 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
>> index f38b50151ce8..39310d9b4e16 100644
>> --- a/arch/arm64/include/asm/kvm_emulate.h
>> +++ b/arch/arm64/include/asm/kvm_emulate.h
>> @@ -701,6 +701,11 @@ static inline enum realm_state kvm_realm_state(struct kvm *kvm)
>> return READ_ONCE(kvm->arch.realm.state);
>> }
>>
>> +static inline bool kvm_realm_is_created(struct kvm *kvm)
>> +{
>> + return kvm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
>> +}
>> +
>> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
>> {
>> return false;
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
>> index 3506f50b05cd..0ada525af18f 100644
>> --- a/arch/arm64/include/asm/kvm_rmi.h
>> +++ b/arch/arm64/include/asm/kvm_rmi.h
>> @@ -6,6 +6,8 @@
>> #ifndef __ASM_KVM_RMI_H
>> #define __ASM_KVM_RMI_H
>>
>> +#include <asm/rmi_smc.h>
>> +
>> /**
>> * enum realm_state - State of a Realm
>> */
>> @@ -46,11 +48,25 @@ enum realm_state {
>> * struct realm - Additional per VM data for a Realm
>> *
>> * @state: The lifetime state machine for the realm
>> + * @rd: Kernel mapping of the Realm Descriptor (RD)
>> + * @params: Parameters for the RMI_REALM_CREATE command
>> + * @num_aux: The number of auxiliary pages required by the RMM
>> + * @ia_bits: Number of valid Input Address bits in the IPA
>> */
>> struct realm {
>> enum realm_state state;
>> +
>> + void *rd;
>> + struct realm_params *params;
>> +
>> + unsigned long num_aux;
>> + unsigned int ia_bits;
>> };
>>
>> void kvm_init_rmi(void);
>> +u32 kvm_realm_ipa_limit(void);
>> +
>> +int kvm_init_realm_vm(struct kvm *kvm);
>> +void kvm_destroy_realm(struct kvm *kvm);
>>
>> #endif /* __ASM_KVM_RMI_H */
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 274d7866efdc..9b17bdfaf0c2 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -253,6 +253,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_is_realm(kvm)) {
>> + ret = kvm_init_realm_vm(kvm);
>> + if (ret)
>> + goto err_free_cpumask;
>> + }
>> +
>> return 0;
>>
>> err_free_cpumask:
>> @@ -312,6 +319,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
>> kvm_unshare_hyp(kvm, kvm + 1);
>>
>> kvm_arm_teardown_hypercalls(kvm);
>> + if (kvm_is_realm(kvm))
>> + kvm_destroy_realm(kvm);
>> }
>>
>> static bool kvm_has_full_ptr_auth(void)
>> @@ -473,6 +482,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>> else
>> r = kvm_supports_cacheable_pfnmap();
>> break;
>> + case KVM_CAP_ARM_RMI:
>> + r = static_key_enabled(&kvm_rmi_is_available);
>> + break;
>>
>> default:
>> r = 0;
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index 070a01e53fcb..d6094b60c4ce 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -872,12 +872,16 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
>> .icache_inval_pou = invalidate_icache_guest_page,
>> };
>>
>> -static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
>> +static int kvm_init_ipa_range(struct kvm *kvm,
>> + struct kvm_s2_mmu *mmu, unsigned long type)
>> {
>> u32 kvm_ipa_limit = get_kvm_ipa_limit();
>> u64 mmfr0, mmfr1;
>> u32 phys_shift;
>>
>> + if (kvm_is_realm(kvm))
>> + kvm_ipa_limit = kvm_realm_ipa_limit();
>> +
>
> Hi,
>
> I believe we can do:
>
> struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
>
> to avoid introducing the extra argument.
> But in order for that to work we'll have to initialize mmu->arch
> earlier:
>
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 8c5d259810b2..e98da7bde9a0 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -974,6 +974,7 @@ 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;
> @@ -982,7 +983,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;
Nice - I hadn't noticed that I could just pull the mmu->arch assignment
up and avoid that extra argument.
Thanks,
Steve
>
> Thanks,
> Wei-Lin Chang
>
>> if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
>> return -EINVAL;
>>
>> @@ -974,7 +978,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
>> return -EINVAL;
>> }
>>
>> - err = kvm_init_ipa_range(mmu, type);
>> + err = kvm_init_ipa_range(kvm, mmu, type);
>> if (err)
>> return err;
>>
>> @@ -1113,7 +1117,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
>> write_unlock(&kvm->mmu_lock);
>>
>> if (pgt) {
>> - kvm_stage2_destroy(pgt);
>> + if (!kvm_is_realm(kvm))
>> + kvm_stage2_destroy(pgt);
>> kfree(pgt);
>> }
>> }
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index 80aedc85e94a..700b8c935d29 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -6,6 +6,8 @@
>> #include <linux/kvm_host.h>
>> #include <linux/memblock.h>
>>
>> +#include <asm/kvm_emulate.h>
>> +#include <asm/kvm_mmu.h>
>> #include <asm/kvm_pgtable.h>
>> #include <asm/rmi_cmds.h>
>> #include <asm/virt.h>
>> @@ -182,6 +184,92 @@ static int rmi_init_metadata(void)
>> return 0;
>> }
>>
>> +u32 kvm_realm_ipa_limit(void)
>> +{
>> + return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
>> +}
>> +
>> +static int undelegate_range(phys_addr_t phys, unsigned long size)
>> +{
>> + unsigned long ret;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
>> + return ret;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int undelegate_page(phys_addr_t phys)
>> +{
>> + return undelegate_range(phys, PAGE_SIZE);
>> +}
>> +
>> +static int free_delegated_page(phys_addr_t phys)
>> +{
>> + if (WARN_ON(undelegate_page(phys))) {
>> + /* Undelegate failed: leak the page */
>> + return -EBUSY;
>> + }
>> +
>> + free_page((unsigned long)phys_to_virt(phys));
>> +
>> + return 0;
>> +}
>> +
>> +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);
>> +
>> + write_lock(&kvm->mmu_lock);
>> + kvm_stage2_unmap_range(&kvm->arch.mmu, 0,
>> + BIT(realm->ia_bits - 1), true);
>> + write_unlock(&kvm->mmu_lock);
>> +
>> + if (realm->params) {
>> + free_page((unsigned long)realm->params);
>> + realm->params = NULL;
>> + }
>> +
>> + if (!kvm_realm_is_created(kvm))
>> + return;
>> +
>> + WRITE_ONCE(realm->state, REALM_STATE_DYING);
>> +
>> + if (realm->rd) {
>> + phys_addr_t rd_phys = virt_to_phys(realm->rd);
>> +
>> + if (WARN_ON(rmi_realm_destroy(rd_phys)))
>> + return;
>> + free_delegated_page(rd_phys);
>> + realm->rd = NULL;
>> + }
>> +
>> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, pgd_size)))
>> + return;
>> +
>> + WRITE_ONCE(realm->state, REALM_STATE_DEAD);
>> +
>> + /* Now that the Realm is destroyed, free the entry level RTTs */
>> + kvm_free_stage2_pgd(&kvm->arch.mmu);
>> +}
>> +
>> +int kvm_init_realm_vm(struct kvm *kvm)
>> +{
>> + kvm->arch.realm.params = (void *)get_zeroed_page(GFP_KERNEL);
>> +
>> + if (!kvm->arch.realm.params)
>> + return -ENOMEM;
>> + return 0;
>> +}
>> +
>> static int rmm_check_features(void)
>> {
>> if (kvm_lpa2_is_enabled() && !rmi_has_feature(RMI_FEATURE_REGISTER_0_LPA2)) {
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH v13 07/48] arm64: RMI: Check for RMI support at KVM init
From: Wei-Lin Chang @ 2026-03-19 16:17 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-8-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:31PM +0000, Steven Price wrote:
> Query the RMI version number and check if it is a compatible version. A
> static key is also provided to signal that a supported RMM is available.
>
> Functions are provided to query if a VM or VCPU is a realm (or rec)
> which currently will always return false.
>
> Later patches make use of struct realm and the states as the ioctls
> interfaces are added to support realm and REC creation and destruction.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Drop check for 4k page size.
> Changes since v11:
> * Reword slightly the comments on the realm states.
> Changes since v10:
> * kvm_is_realm() no longer has a NULL check.
> * Rename from "rme" to "rmi" when referring to the RMM interface.
> * Check for RME (hardware) support before probing for RMI support.
> Changes since v8:
> * No need to guard kvm_init_rme() behind 'in_hyp_mode'.
> Changes since v6:
> * Improved message for an unsupported RMI ABI version.
> Changes since v5:
> * Reword "unsupported" message from "host supports" to "we want" to
> clarify that 'we' are the 'host'.
> Changes since v2:
> * Drop return value from kvm_init_rme(), it was always 0.
> * Rely on the RMM return value to identify whether the RSI ABI is
> compatible.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 18 +++++++++
> arch/arm64/include/asm/kvm_host.h | 4 ++
> arch/arm64/include/asm/kvm_rmi.h | 56 +++++++++++++++++++++++++++
> arch/arm64/include/asm/virt.h | 1 +
> arch/arm64/kernel/cpufeature.c | 1 +
> arch/arm64/kvm/Makefile | 2 +-
> arch/arm64/kvm/arm.c | 5 +++
> arch/arm64/kvm/rmi.c | 57 ++++++++++++++++++++++++++++
> 8 files changed, 143 insertions(+), 1 deletion(-)
> create mode 100644 arch/arm64/include/asm/kvm_rmi.h
> create mode 100644 arch/arm64/kvm/rmi.c
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index 5bf3d7e1d92c..f38b50151ce8 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -688,4 +688,22 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu)
> vcpu->arch.hcrx_el2 |= HCRX_EL2_EnASR;
> }
> }
> +
> +static inline bool kvm_is_realm(struct kvm *kvm)
> +{
> + if (static_branch_unlikely(&kvm_rmi_is_available))
> + return kvm->arch.is_realm;
> + return false;
> +}
> +
> +static inline enum realm_state kvm_realm_state(struct kvm *kvm)
> +{
> + return READ_ONCE(kvm->arch.realm.state);
> +}
> +
> +static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> +{
> + return false;
> +}
> +
Hi,
Both kvm_vm_is_protected() and vcpu_is_protected() are in kvm_host.h, do
you think that's a better place? Or is there a reason for this being in
kvm_emulate.h?
Thanks,
Wei-Lin Chang
> #endif /* __ARM64_KVM_EMULATE_H__ */
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 5d5a3bbdb95e..9267a2f2d65b 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -27,6 +27,7 @@
> #include <asm/fpsimd.h>
> #include <asm/kvm.h>
> #include <asm/kvm_asm.h>
> +#include <asm/kvm_rmi.h>
> #include <asm/vncr_mapping.h>
>
> #define __KVM_HAVE_ARCH_INTC_INITIALIZED
> @@ -405,6 +406,9 @@ struct kvm_arch {
> * the associated pKVM instance in the hypervisor.
> */
> struct kvm_protected_vm pkvm;
> +
> + bool is_realm;
> + struct realm realm;
> };
>
> struct kvm_vcpu_fault_info {
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> new file mode 100644
> index 000000000000..3506f50b05cd
> --- /dev/null
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023-2025 ARM Ltd.
> + */
> +
> +#ifndef __ASM_KVM_RMI_H
> +#define __ASM_KVM_RMI_H
> +
> +/**
> + * enum realm_state - State of a Realm
> + */
> +enum realm_state {
> + /**
> + * @REALM_STATE_NONE:
> + * Realm has not yet been created. rmi_realm_create() has not
> + * yet been called.
> + */
> + REALM_STATE_NONE,
> + /**
> + * @REALM_STATE_NEW:
> + * Realm is under construction, rmi_realm_create() has been
> + * called, but it is not yet activated. Pages may be populated.
> + */
> + REALM_STATE_NEW,
> + /**
> + * @REALM_STATE_ACTIVE:
> + * Realm has been created and is eligible for execution with
> + * rmi_rec_enter(). Pages may no longer be populated with
> + * rmi_data_create().
> + */
> + REALM_STATE_ACTIVE,
> + /**
> + * @REALM_STATE_DYING:
> + * Realm is in the process of being destroyed or has already been
> + * destroyed.
> + */
> + REALM_STATE_DYING,
> + /**
> + * @REALM_STATE_DEAD:
> + * Realm has been destroyed.
> + */
> + REALM_STATE_DEAD
> +};
> +
> +/**
> + * struct realm - Additional per VM data for a Realm
> + *
> + * @state: The lifetime state machine for the realm
> + */
> +struct realm {
> + enum realm_state state;
> +};
> +
> +void kvm_init_rmi(void);
> +
> +#endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
> index b51ab6840f9c..dc9b2899e0b2 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/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index c31f8e17732a..ddf7e57f23e8 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -289,6 +289,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar3[] = {
> static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV3_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_CSV2_SHIFT, 4, 0),
> + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_RME_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_DIT_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_AMU_SHIFT, 4, 0),
> ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_MPAM_SHIFT, 4, 0),
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index 3ebc0570345c..e17c4077d8e7 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 29f0326f7e00..274d7866efdc 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -39,6 +39,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>
>
> @@ -104,6 +105,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);
> @@ -2921,6 +2924,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/rmi.c b/arch/arm64/kvm/rmi.c
> new file mode 100644
> index 000000000000..fac151580c01
> --- /dev/null
> +++ b/arch/arm64/kvm/rmi.c
> @@ -0,0 +1,57 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023-2025 ARM Ltd.
> + */
> +
> +#include <linux/kvm_host.h>
> +
> +#include <asm/rmi_cmds.h>
> +#include <asm/virt.h>
> +
> +static int rmi_check_version(void)
> +{
> + struct arm_smccc_res res;
> + unsigned short version_major, version_minor;
> + unsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,
> + RMI_ABI_MINOR_VERSION);
> + unsigned long aa64pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
> +
> + /* If RME isn't supported, then RMI can't be */
> + if (cpuid_feature_extract_unsigned_field(aa64pfr0, ID_AA64PFR0_EL1_RME_SHIFT) == 0)
> + return -ENXIO;
> +
> + arm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, &res);
> +
> + if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
> + return -ENXIO;
> +
> + version_major = RMI_ABI_VERSION_GET_MAJOR(res.a1);
> + version_minor = RMI_ABI_VERSION_GET_MINOR(res.a1);
> +
> + if (res.a0 != RMI_SUCCESS) {
> + unsigned short high_version_major, high_version_minor;
> +
> + high_version_major = RMI_ABI_VERSION_GET_MAJOR(res.a2);
> + high_version_minor = RMI_ABI_VERSION_GET_MINOR(res.a2);
> +
> + kvm_err("Unsupported RMI ABI (v%d.%d - v%d.%d) we want v%d.%d\n",
> + version_major, version_minor,
> + high_version_major, high_version_minor,
> + RMI_ABI_MAJOR_VERSION,
> + RMI_ABI_MINOR_VERSION);
> + return -ENXIO;
> + }
> +
> + kvm_info("RMI ABI version %d.%d\n", version_major, version_minor);
> +
> + return 0;
> +}
> +
> +void kvm_init_rmi(void)
> +{
> + /* Continue without realm support if we can't agree on a version */
> + if (rmi_check_version())
> + return;
> +
> + /* Future patch will enable static branch kvm_rmi_is_available */
> +}
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 12/48] arm64: RMI: Basic infrastructure for creating a realm.
From: Wei-Lin Chang @ 2026-03-19 16:11 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <20260318155413.793430-13-steven.price@arm.com>
On Wed, Mar 18, 2026 at 03:53:36PM +0000, Steven Price wrote:
> Introduce the skeleton functions for creating and destroying a realm.
> The IPA size requested is checked against what the RMM supports.
>
> The actual work of constructing the realm will be added in future
> patches.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v12:
> * Drop the RMM_PAGE_{SHIFT,SIZE} defines - the RMM is now configured to
> be the same as the host's page size.
> * Rework delegate/undelegate functions to use the new RMI range based
> operations.
> Changes since v11:
> * Major rework to drop the realm configuration and make the
> construction of realms implicit rather than driven by the VMM
> directly.
> * The code to create RDs, handle VMIDs etc is moved to later patches.
> Changes since v10:
> * Rename from RME to RMI.
> * Move the stage2 cleanup to a later patch.
> Changes since v9:
> * Avoid walking the stage 2 page tables when destroying the realm -
> the real ones are not accessible to the non-secure world, and the RMM
> may leave junk in the physical pages when returning them.
> * Fix an error path in realm_create_rd() to actually return an error value.
> Changes since v8:
> * Fix free_delegated_granule() to not call kvm_account_pgtable_pages();
> a separate wrapper will be introduced in a later patch to deal with
> RTTs.
> * Minor code cleanups following review.
> Changes since v7:
> * Minor code cleanup following Gavin's review.
> Changes since v6:
> * Separate RMM RTT calculations from host PAGE_SIZE. This allows the
> host page size to be larger than 4k while still communicating with an
> RMM which uses 4k granules.
> Changes since v5:
> * Introduce free_delegated_granule() to replace many
> undelegate/free_page() instances and centralise the comment on
> leaking when the undelegate fails.
> * Several other minor improvements suggested by reviews - thanks for
> the feedback!
> Changes since v2:
> * Improved commit description.
> * Improved return failures for rmi_check_version().
> * Clear contents of PGD after it has been undelegated in case the RMM
> left stale data.
> * Minor changes to reflect changes in previous patches.
> ---
> arch/arm64/include/asm/kvm_emulate.h | 5 ++
> arch/arm64/include/asm/kvm_rmi.h | 16 +++++
> arch/arm64/kvm/arm.c | 12 ++++
> arch/arm64/kvm/mmu.c | 11 +++-
> arch/arm64/kvm/rmi.c | 88 ++++++++++++++++++++++++++++
> 5 files changed, 129 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
> index f38b50151ce8..39310d9b4e16 100644
> --- a/arch/arm64/include/asm/kvm_emulate.h
> +++ b/arch/arm64/include/asm/kvm_emulate.h
> @@ -701,6 +701,11 @@ static inline enum realm_state kvm_realm_state(struct kvm *kvm)
> return READ_ONCE(kvm->arch.realm.state);
> }
>
> +static inline bool kvm_realm_is_created(struct kvm *kvm)
> +{
> + return kvm_is_realm(kvm) && kvm_realm_state(kvm) != REALM_STATE_NONE;
> +}
> +
> static inline bool vcpu_is_rec(struct kvm_vcpu *vcpu)
> {
> return false;
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 3506f50b05cd..0ada525af18f 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -6,6 +6,8 @@
> #ifndef __ASM_KVM_RMI_H
> #define __ASM_KVM_RMI_H
>
> +#include <asm/rmi_smc.h>
> +
> /**
> * enum realm_state - State of a Realm
> */
> @@ -46,11 +48,25 @@ enum realm_state {
> * struct realm - Additional per VM data for a Realm
> *
> * @state: The lifetime state machine for the realm
> + * @rd: Kernel mapping of the Realm Descriptor (RD)
> + * @params: Parameters for the RMI_REALM_CREATE command
> + * @num_aux: The number of auxiliary pages required by the RMM
> + * @ia_bits: Number of valid Input Address bits in the IPA
> */
> struct realm {
> enum realm_state state;
> +
> + void *rd;
> + struct realm_params *params;
> +
> + unsigned long num_aux;
> + unsigned int ia_bits;
> };
>
> void kvm_init_rmi(void);
> +u32 kvm_realm_ipa_limit(void);
> +
> +int kvm_init_realm_vm(struct kvm *kvm);
> +void kvm_destroy_realm(struct kvm *kvm);
>
> #endif /* __ASM_KVM_RMI_H */
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 274d7866efdc..9b17bdfaf0c2 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -253,6 +253,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_is_realm(kvm)) {
> + ret = kvm_init_realm_vm(kvm);
> + if (ret)
> + goto err_free_cpumask;
> + }
> +
> return 0;
>
> err_free_cpumask:
> @@ -312,6 +319,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
> kvm_unshare_hyp(kvm, kvm + 1);
>
> kvm_arm_teardown_hypercalls(kvm);
> + if (kvm_is_realm(kvm))
> + kvm_destroy_realm(kvm);
> }
>
> static bool kvm_has_full_ptr_auth(void)
> @@ -473,6 +482,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> else
> r = kvm_supports_cacheable_pfnmap();
> break;
> + case KVM_CAP_ARM_RMI:
> + r = static_key_enabled(&kvm_rmi_is_available);
> + break;
>
> default:
> r = 0;
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 070a01e53fcb..d6094b60c4ce 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -872,12 +872,16 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
> .icache_inval_pou = invalidate_icache_guest_page,
> };
>
> -static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
> +static int kvm_init_ipa_range(struct kvm *kvm,
> + struct kvm_s2_mmu *mmu, unsigned long type)
> {
> u32 kvm_ipa_limit = get_kvm_ipa_limit();
> u64 mmfr0, mmfr1;
> u32 phys_shift;
>
> + if (kvm_is_realm(kvm))
> + kvm_ipa_limit = kvm_realm_ipa_limit();
> +
Hi,
I believe we can do:
struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
to avoid introducing the extra argument.
But in order for that to work we'll have to initialize mmu->arch
earlier:
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 8c5d259810b2..e98da7bde9a0 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -974,6 +974,7 @@ 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;
@@ -982,7 +983,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;
Thanks,
Wei-Lin Chang
> if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
> return -EINVAL;
>
> @@ -974,7 +978,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
> return -EINVAL;
> }
>
> - err = kvm_init_ipa_range(mmu, type);
> + err = kvm_init_ipa_range(kvm, mmu, type);
> if (err)
> return err;
>
> @@ -1113,7 +1117,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
> write_unlock(&kvm->mmu_lock);
>
> if (pgt) {
> - kvm_stage2_destroy(pgt);
> + if (!kvm_is_realm(kvm))
> + kvm_stage2_destroy(pgt);
> kfree(pgt);
> }
> }
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 80aedc85e94a..700b8c935d29 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -6,6 +6,8 @@
> #include <linux/kvm_host.h>
> #include <linux/memblock.h>
>
> +#include <asm/kvm_emulate.h>
> +#include <asm/kvm_mmu.h>
> #include <asm/kvm_pgtable.h>
> #include <asm/rmi_cmds.h>
> #include <asm/virt.h>
> @@ -182,6 +184,92 @@ static int rmi_init_metadata(void)
> return 0;
> }
>
> +u32 kvm_realm_ipa_limit(void)
> +{
> + return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ);
> +}
> +
> +static int undelegate_range(phys_addr_t phys, unsigned long size)
> +{
> + unsigned long ret;
> + unsigned long top = phys + size;
> + unsigned long out_top;
> +
> + while (phys < top) {
> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
> + if (ret == RMI_SUCCESS)
> + phys = out_top;
> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int undelegate_page(phys_addr_t phys)
> +{
> + return undelegate_range(phys, PAGE_SIZE);
> +}
> +
> +static int free_delegated_page(phys_addr_t phys)
> +{
> + if (WARN_ON(undelegate_page(phys))) {
> + /* Undelegate failed: leak the page */
> + return -EBUSY;
> + }
> +
> + free_page((unsigned long)phys_to_virt(phys));
> +
> + return 0;
> +}
> +
> +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);
> +
> + write_lock(&kvm->mmu_lock);
> + kvm_stage2_unmap_range(&kvm->arch.mmu, 0,
> + BIT(realm->ia_bits - 1), true);
> + write_unlock(&kvm->mmu_lock);
> +
> + if (realm->params) {
> + free_page((unsigned long)realm->params);
> + realm->params = NULL;
> + }
> +
> + if (!kvm_realm_is_created(kvm))
> + return;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_DYING);
> +
> + if (realm->rd) {
> + phys_addr_t rd_phys = virt_to_phys(realm->rd);
> +
> + if (WARN_ON(rmi_realm_destroy(rd_phys)))
> + return;
> + free_delegated_page(rd_phys);
> + realm->rd = NULL;
> + }
> +
> + if (WARN_ON(undelegate_range(kvm->arch.mmu.pgd_phys, pgd_size)))
> + return;
> +
> + WRITE_ONCE(realm->state, REALM_STATE_DEAD);
> +
> + /* Now that the Realm is destroyed, free the entry level RTTs */
> + kvm_free_stage2_pgd(&kvm->arch.mmu);
> +}
> +
> +int kvm_init_realm_vm(struct kvm *kvm)
> +{
> + kvm->arch.realm.params = (void *)get_zeroed_page(GFP_KERNEL);
> +
> + if (!kvm->arch.realm.params)
> + return -ENOMEM;
> + return 0;
> +}
> +
> static int rmm_check_features(void)
> {
> if (kvm_lpa2_is_enabled() && !rmi_has_feature(RMI_FEATURE_REGISTER_0_LPA2)) {
> --
> 2.43.0
>
^ permalink raw reply related
* Re: [PATCH v13 36/48] arm64: RMI: Always use 4k pages for realms
From: Steven Price @ 2026-03-19 16:02 UTC (permalink / raw)
To: Joey Gouly
Cc: kvm, kvmarm, Catalin Marinas, Marc Zyngier, Will Deacon,
James Morse, Oliver Upton, Suzuki K Poulose, Zenghui Yu,
linux-arm-kernel, linux-kernel, Alexandru Elisei,
Christoffer Dall, Fuad Tabba, linux-coco, Ganapatrao Kulkarni,
Gavin Shan, Shanker Donthineni, Alper Gun, Aneesh Kumar K . V,
Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260319102454.GB3942350@e124191.cambridge.arm.com>
On 19/03/2026 10:24, Joey Gouly wrote:
> Hi,
>
> On Wed, Mar 18, 2026 at 03:54:00PM +0000, Steven Price wrote:
>> Guest_memfd doesn't yet natively support huge pages, and there are
>> currently difficulties for a VMM to manage huge pages efficiently so for
>> now always split up mappings to PTE (4k).
>>
>> The two issues that need progressing before supporting huge pages for
>> realms are:
>>
>> 1. guest_memfd needs to be able to allocate from an appropriate
>> allocator which can provide huge pages.
>>
>> 2. The VMM needs to be able to repurpose private memory for a shared
>> mapping when the guest VM requests memory is transitioned. Because
>> this can happen at a 4k granularity it isn't possible to
>> free/reallocate while huge pages are in use. Allowing the VMM to
>> mmap() the shared portion of a huge page would allow the huge page
>> to be recreated when the memory is unshared and made protected again.
>>
>> These two issues are not specific to realms and don't affect the realm
>> API, so for now just break everything down to 4k pages in the RMM
>> controlled stage 2. Future work can add huge page support without
>> changing the uAPI.
>
> The commit title/message mention 4K, but should probably say PAGE_SIZE or
> something now that RMM isn't fixed to 4K.
Indeed - this is all PAGE_SIZE not 4k any more. Also hopefully the
reasons for this patch are also going to disappear soon. (2) above isn't
really very true any more (we do support mmap() from guest_memfd).
Thanks,
Steve
> Thanks,
> Joey
>
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> Reviewed-by: Gavin Shan <gshan@redhat.com>
>> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> Changes since v7:
>> * Rewritten commit message
>> ---
>> arch/arm64/kvm/mmu.c | 7 +++++--
>> 1 file changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index 73c18c2861a2..ad1300f366df 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -1761,11 +1761,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> write_fault = kvm_is_write_fault(vcpu);
>>
>> /*
>> - * Realms cannot map protected pages read-only
>> + * Realms cannot map protected pages read-only, also force PTE mappings
>> + * for Realms.
>> * FIXME: It should be possible to map unprotected pages read-only
>> */
>> - if (vcpu_is_rec(vcpu))
>> + if (vcpu_is_rec(vcpu)) {
>> write_fault = true;
>> + force_pte = true;
>> + }
>>
>> exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
>> VM_WARN_ON_ONCE(write_fault && exec_fault);
>> --
>> 2.43.0
>>
>>
^ permalink raw reply
* Re: [PATCH v2 12/19] x86, ioremap, resource: Support IORES_DESC_ENCRYPTED for encrypted PCI MMIO
From: Borislav Petkov @ 2026-03-19 15:34 UTC (permalink / raw)
To: Dan Williams
Cc: linux-coco, linux-pci, gregkh, aik, aneesh.kumar, yilun.xu,
bhelgaas, alistair23, lukas, jgg, Dave Hansen, Andy Lutomirski,
Peter Zijlstra, Thomas Gleixner, Ingo Molnar, x86, H. Peter Anvin
In-Reply-To: <20260303000207.1836586-13-dan.j.williams@intel.com>
On Mon, Mar 02, 2026 at 04:02:00PM -0800, Dan Williams wrote:
> PCIe Trusted Execution Environment Device Interface Security Protocol
> (TDISP) arranges for a PCI device to support encrypted MMIO. In support of
> that capability, ioremap() needs a mechanism to detect when a PCI device
> has been dynamically transitioned into this secure state and enforce
> encrypted MMIO mappings.
>
> Teach ioremap() about a new IORES_DESC_ENCRYPTED type that supplements the
> existing PCI Memory Space (MMIO) BAR resources. The proposal is that a
> resource, "PCI MMIO Encrypted", with this description type is injected by
> the PCI/TSM core for each PCI device BAR that is to be protected.
>
> Unlike the existing encryption determination which is "implied with a
> silent fallback to an unencrypted mapping", this indication is "explicit
> with an expectation that the request fails instead of fallback".
> IORES_MUST_ENCRYPT is added to manage this expectation.
"Add IORES_MUST_ENCRYPT to manage... "
Pls read section "2) Describe your changes" in
Documentation/process/submitting-patches.rst for more details. Especially the
part about ordering the code to do stuff.
> Given that "PCI MMIO Encrypted" is an additional resource in the tree, the
> IORESOURCE_BUSY flag will only be set on a descendant/child of that
> resource. That means it cannot share the same walk as the check for "System
> RAM". Add walk_iomem_res_desc() to check if any IORES_DESC_ENCRYPTED
> intersects the ioremap() range and set IORES_MUST_ENCRYPT accordingly. When
> IORES_MUST_ENCRYPT is set, the entire ioremap() range must be covered by
> IORES_DESC_ENCRYPTED.
>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: Andy Lutomirski <luto@kernel.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: x86@kernel.org
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
> include/linux/ioport.h | 1 +
> arch/x86/mm/ioremap.c | 49 +++++++++++++++++++++++++++++++-----------
> 2 files changed, 37 insertions(+), 13 deletions(-)
>
> diff --git a/include/linux/ioport.h b/include/linux/ioport.h
> index 1c106608c514..3efd07443c47 100644
> --- a/include/linux/ioport.h
> +++ b/include/linux/ioport.h
> @@ -152,6 +152,7 @@ enum {
> enum {
> IORES_MAP_SYSTEM_RAM = BIT(0),
> IORES_MAP_ENCRYPTED = BIT(1),
> + IORES_MUST_ENCRYPT = BIT(2), /* disable transparent fallback */
> };
>
> /* helpers to define resources */
> diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
> index 12c8180ca1ba..0f300e226a9f 100644
> --- a/arch/x86/mm/ioremap.c
> +++ b/arch/x86/mm/ioremap.c
> @@ -36,6 +36,7 @@
> */
> struct ioremap_desc {
> unsigned int flags;
> + u64 encrypt_size;
> };
>
> /*
> @@ -88,23 +89,35 @@ static unsigned int __ioremap_check_ram(struct resource *res)
> }
>
> /*
> - * In a SEV guest, NONE and RESERVED should not be mapped encrypted because
> - * there the whole memory is already encrypted.
> + * In a encrypted guest, NONE and RESERVED should not be mapped encrypted
"In an encrypted... "
> + * because there the whole memory is already encrypted.
> + *
> + * For the encrypted case the entire range must agree with being mapped
> + * encrypted.
> */
> -static unsigned int __ioremap_check_encrypted(struct resource *res)
> +static unsigned int __ioremap_check_encrypted(struct ioremap_desc *desc,
> + struct resource *res)
> {
> + u32 flags = 0;
> +
> + if (res->desc == IORES_DESC_ENCRYPTED)
> + flags |= IORES_MUST_ENCRYPT;
> +
> if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
> - return 0;
> + return flags;
>
> switch (res->desc) {
> case IORES_DESC_NONE:
> case IORES_DESC_RESERVED:
> break;
> + case IORES_DESC_ENCRYPTED:
> + desc->encrypt_size += resource_size(res);
> + fallthrough;
> default:
> - return IORES_MAP_ENCRYPTED;
> + flags |= IORES_MAP_ENCRYPTED;
> }
>
> - return 0;
> + return flags;
> }
This looks too convoluted. I'm sure we can state the rules for encrypted
ranges first and then can simplify it into something more understandable.
>
> /*
> @@ -134,14 +147,10 @@ static int __ioremap_collect_map_flags(struct resource *res, void *arg)
> {
> struct ioremap_desc *desc = arg;
>
> - if (!(desc->flags & IORES_MAP_SYSTEM_RAM))
> - desc->flags |= __ioremap_check_ram(res);
> -
> - if (!(desc->flags & IORES_MAP_ENCRYPTED))
> - desc->flags |= __ioremap_check_encrypted(res);
> + desc->flags |= __ioremap_check_ram(res);
> + desc->flags |= __ioremap_check_encrypted(desc, res);
>
> - return ((desc->flags & (IORES_MAP_SYSTEM_RAM | IORES_MAP_ENCRYPTED)) ==
> - (IORES_MAP_SYSTEM_RAM | IORES_MAP_ENCRYPTED));
> + return 0;
> }
This change I don't grok. Perhaps it needs to be a separate patch.
> /*
> @@ -162,6 +171,13 @@ static void __ioremap_check_mem(resource_size_t addr, unsigned long size,
> memset(desc, 0, sizeof(struct ioremap_desc));
>
> walk_mem_res(start, end, desc, __ioremap_collect_map_flags);
<---- newline here.
> + /*
> + * Encrypted MMIO may parent a driver's requested region, so it needs a
> + * separate search
> + */
> + desc->encrypt_size = 0;
> + walk_iomem_res_desc(IORES_DESC_ENCRYPTED, IORESOURCE_MEM, start, end,
> + desc, __ioremap_collect_map_flags);
Why is this here and executed unconditionally regardless of coco guest or not?
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply
* Re: [PATCH v13 13/48] kvm: arm64: Don't expose unsupported capabilities for realm guests
From: Steven Price @ 2026-03-19 15:25 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <56fb42ad-eada-40df-8c81-53cb7c0c310e@arm.com>
On 19/03/2026 14:09, Suzuki K Poulose wrote:
> On 18/03/2026 15:53, Steven Price wrote:
>> From: Suzuki K Poulose <suzuki.poulose@arm.com>
>>
>> RMM v1.0 provides no mechanism for the host to perform debug operations
>> on the guest. So limit the extensions that are visible to an allowlist
>> so that only those capabilities we can support are advertised.
>>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v10:
>> * Add a kvm_realm_ext_allowed() function which limits which extensions
>> are exposed to an allowlist. This removes the need for special casing
>> various extensions.
>> Changes since v7:
>> * Remove the helper functions and inline the kvm_is_realm() check with
>> a ternary operator.
>> * Rewrite the commit message to explain this patch.
>> ---
>> arch/arm64/kvm/arm.c | 22 ++++++++++++++++++++++
>> 1 file changed, 22 insertions(+)
>>
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 9b17bdfaf0c2..ddbf080e4f55 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -357,6 +357,25 @@ static bool kvm_has_full_ptr_auth(void)
>> (apa + api + apa3) == 1);
>> }
>> +static bool kvm_realm_ext_allowed(long ext)
>> +{
>> + switch (ext) {
>> + case KVM_CAP_IRQCHIP:
>> + case KVM_CAP_ARM_PSCI:
>> + case KVM_CAP_ARM_PSCI_0_2:
>> + case KVM_CAP_NR_VCPUS:
>> + case KVM_CAP_MAX_VCPUS:
>> + case KVM_CAP_MAX_VCPU_ID:
>> + case KVM_CAP_MSI_DEVID:
>> + case KVM_CAP_ARM_VM_IPA_SIZE:
>> + case KVM_CAP_ARM_PTRAUTH_ADDRESS:
>> + case KVM_CAP_ARM_PTRAUTH_GENERIC:
>> + case KVM_CAP_ARM_RMI:
>> + return true;
>> + }
>> + return false;
>> +}
>> +
>> int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>> {
>> int r;
>> @@ -364,6 +383,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm,
>> long ext)
>> if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
>> return 0;
>> + if (kvm && kvm_is_realm(kvm) && !kvm_realm_ext_allowed(ext))
>> + return 0;
>> +
>
> We need a similar check in in kvm_vm_ioctl_enable_cap() to prevent
> enabling the filtered caps ? Otherwise looks good to me.
Indeed - thanks for spotting.
Thanks,
Steve
> Suzuki
>
>> switch (ext) {
>> case KVM_CAP_IRQCHIP:
>> r = vgic_present;
>
^ permalink raw reply
* Re: [PATCH v13 10/48] arm64: RMI: Ensure that the RMM has GPT entries for memory
From: Steven Price @ 2026-03-19 15:20 UTC (permalink / raw)
To: Suzuki K Poulose, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <b83d601c-c4a0-4e6b-aae0-e49f02e67e30@arm.com>
On 19/03/2026 10:31, Suzuki K Poulose wrote:
> Hi Steven
>
> On 18/03/2026 15:53, Steven Price wrote:
>> The RMM may not be tracking all the memory of the system at boot. Create
>
> Looks good to me. Please find some suggestions below.
>
>
> May be add a bit more context here :
>
> RMM maintains the state of all the granules in the System to make sure
> that the host is abiding by the rules. This state can be maintained at
> different granularity - per PAGE (TRACKING_FINE) or per region (COARSE),
> where the "region size" depends on the underlying "RMI_GRANULE_SIZE".
> The state of the "tracked area" must be the same. This implies, we may
> need to have "FINE" tracking for DRAM, so that we can start delegating
> PAGEs. For now, we only support RMM with statically carved out memory
> for tracking FINE granularity for the tracking regions. We will extend
> the support for modifying the TRACKING region in the future.
>
> Similarly, the firmware may create L0 GPT entries describing the total
> address space (think of this as Block mappings in the page tables). But
> if we change the "PAS" of a granule in the block mapping, we may need
> to create L1 tables to track the PAS at the finer granularity. For now
> we only support a system where the L1 GPTs are created at boot time
> and dynamic GPT support will be added later.
Thanks for the wording - that does indeed make things clearer. SRO
support will effectively enable the "future" items.
>> the necessary tracking state and GPTs within the RMM so that all boot
>> memory can be delegated to the RMM as needed during runtime.
>>
>> Note: support is currently missing for SROs which means that if the RMM
>> needs memory donating this will fail (and render CCA unusable in Linux).
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> New patch for v13
>> ---
>> arch/arm64/kvm/rmi.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 89 insertions(+)
>>
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index 9590dff9a2c1..80aedc85e94a 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -4,6 +4,7 @@
>> */
>> #include <linux/kvm_host.h>
>> +#include <linux/memblock.h>
>> #include <asm/kvm_pgtable.h>
>> #include <asm/rmi_cmds.h>
>> @@ -56,6 +57,18 @@ static int rmi_check_version(void)
>> return 0;
>> }
>> +/*
>> + * These are the 'default' sizes when passing 0 as the
>> tracking_region_size.
>> + * TODO: Support other granule sizes
>> + */
>> +#ifdef CONFIG_PAGE_SIZE_4KB
>> +#define RMM_GRANULE_TRACKING_SIZE SZ_1G
>> +#elif defined(CONFIG_PAGE_SIZE_16KB)
>> +#define RMM_GRANULE_TRACKING_SIZE SZ_32M
>> +#elif defined(CONFIG_PAGE_SIZE_64KB)
>> +#define RMM_GRANULE_TRACKING_SIZE SZ_512M
>> +#endif
>> +
>
> Probably this should be made a Kconfig option, like the VA_BITS we have
> today for each page size.
Yes that's probably a good option - note that for 4k page size there is
only the one option in the spec. So this is only relevant for 16K/64K.
Thanks for the other comment suggestions below (and in the other email)
- all good points.
Thanks,
Steve
>> static int rmi_configure(void)
>> {
>> struct rmm_config *config __free(free_page) = NULL;
>> @@ -95,6 +108,80 @@ static int rmi_configure(void)
>> return 0;
>> }
>> +static int rmi_verify_memory_tracking(phys_addr_t start,
>> phys_addr_t end)
>
> Could we add a comment what we are trying to do here ?
>
> /*
> * Make sure the area is tracked by RMM at FINE granularity.
> * We do not support changing the TRACKING yet. This will
> * be added in the future.
> */
>
>
>> +{
>> + start = ALIGN_DOWN(start, RMM_GRANULE_TRACKING_SIZE);
>> + end = ALIGN(end, RMM_GRANULE_TRACKING_SIZE);
>> +
>> + while (start < end) {
>> + unsigned long ret, category, state;
>> +
>> + ret = rmi_granule_tracking_get(start, &category, &state);
>> + if (ret != RMI_SUCCESS ||
>> + state != RMI_TRACKING_FINE ||
>> + category != RMI_MEM_CATEGORY_CONVENTIONAL) {
>> + /* TODO: Set granule tracking in this case */
>> + kvm_err("Granule tracking for region isn't fine/
>> conventional: %llx",
>> + start);
>> + return -ENODEV;
>> + }
>> + start += RMM_GRANULE_TRACKING_SIZE;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static unsigned long rmi_l0gpt_size(void)
>> +{
>> + return 1UL << (30 + FIELD_GET(RMI_FEATURE_REGISTER_1_L0GPTSZ,
>> + rmm_feat_reg1));
>> +}
>> +
>> +static int rmi_create_gpts(phys_addr_t start, phys_addr_t end)
>> +{
>> + unsigned long l0gpt_sz = rmi_l0gpt_size();
>> +
>> + start = ALIGN_DOWN(start, l0gpt_sz);
>> + end = ALIGN(end, l0gpt_sz);
>> +
>> + while (start < end) {
>> + int ret = rmi_gpt_l1_create(start);
>
> How about adding a comment here explaining why we look for RMI_ERROR_GPT ?
>
>
>>
> /*
> * Make sure the L1 GPT tables are created for the region.
> * RMI_ERROR_GPT indicates the L1 table exists.
> */
> +
>> + if (ret && ret != RMI_ERROR_GPT) {
>
>
>> + /*
>> + * FIXME: Handle SRO so that memory can be donated for
>> + * the tables.
>> + */
>> + kvm_err("GPT Level1 table missing for %llx\n", start);
>> + return -ENOMEM;
>> + }
>> + start += l0gpt_sz;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_init_metadata(void)
>> +{
>> + phys_addr_t start, end;
>> + const struct memblock_region *r;
>> +
>> + for_each_mem_region(r) {
>> + int ret;
>> +
>> + start = memblock_region_memory_base_pfn(r) << PAGE_SHIFT;
>> + end = memblock_region_memory_end_pfn(r) << PAGE_SHIFT;
>> + ret = rmi_verify_memory_tracking(start, end);
>> + if (ret)
>> + return ret;
>> + ret = rmi_create_gpts(start, end);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static int rmm_check_features(void)
>> {
>> if (kvm_lpa2_is_enabled() && !
>> rmi_has_feature(RMI_FEATURE_REGISTER_0_LPA2)) {
>> @@ -120,6 +207,8 @@ void kvm_init_rmi(void)
>> return;
>> if (rmi_configure())
>> return;
>> + if (rmi_init_metadata())
>> + return;
>> /* Future patch will enable static branch kvm_rmi_is_available */
>> }
>
^ permalink raw reply
* Re: [PATCH v13 13/48] kvm: arm64: Don't expose unsupported capabilities for realm guests
From: Suzuki K Poulose @ 2026-03-19 14:09 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260318155413.793430-14-steven.price@arm.com>
On 18/03/2026 15:53, Steven Price wrote:
> From: Suzuki K Poulose <suzuki.poulose@arm.com>
>
> RMM v1.0 provides no mechanism for the host to perform debug operations
> on the guest. So limit the extensions that are visible to an allowlist
> so that only those capabilities we can support are advertised.
>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> Changes since v10:
> * Add a kvm_realm_ext_allowed() function which limits which extensions
> are exposed to an allowlist. This removes the need for special casing
> various extensions.
> Changes since v7:
> * Remove the helper functions and inline the kvm_is_realm() check with
> a ternary operator.
> * Rewrite the commit message to explain this patch.
> ---
> arch/arm64/kvm/arm.c | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 9b17bdfaf0c2..ddbf080e4f55 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -357,6 +357,25 @@ static bool kvm_has_full_ptr_auth(void)
> (apa + api + apa3) == 1);
> }
>
> +static bool kvm_realm_ext_allowed(long ext)
> +{
> + switch (ext) {
> + case KVM_CAP_IRQCHIP:
> + case KVM_CAP_ARM_PSCI:
> + case KVM_CAP_ARM_PSCI_0_2:
> + case KVM_CAP_NR_VCPUS:
> + case KVM_CAP_MAX_VCPUS:
> + case KVM_CAP_MAX_VCPU_ID:
> + case KVM_CAP_MSI_DEVID:
> + case KVM_CAP_ARM_VM_IPA_SIZE:
> + case KVM_CAP_ARM_PTRAUTH_ADDRESS:
> + case KVM_CAP_ARM_PTRAUTH_GENERIC:
> + case KVM_CAP_ARM_RMI:
> + return true;
> + }
> + return false;
> +}
> +
> int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> {
> int r;
> @@ -364,6 +383,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> if (is_protected_kvm_enabled() && !kvm_pkvm_ext_allowed(kvm, ext))
> return 0;
>
> + if (kvm && kvm_is_realm(kvm) && !kvm_realm_ext_allowed(ext))
> + return 0;
> +
We need a similar check in in kvm_vm_ioctl_enable_cap() to prevent
enabling the filtered caps ? Otherwise looks good to me.
Suzuki
> switch (ext) {
> case KVM_CAP_IRQCHIP:
> r = vgic_present;
^ permalink raw reply
* Re: [PATCH v5 22/22] x86/virt/seamldr: Log TDX module update failures
From: Kiryl Shutsemau @ 2026-03-19 14:06 UTC (permalink / raw)
To: Chao Gao
Cc: linux-kernel, linux-coco, kvm, binbin.wu, dan.j.williams,
dave.hansen, ira.weiny, kai.huang, nik.borisov, paulmck, pbonzini,
reinette.chatre, rick.p.edgecombe, sagis, seanjc, tony.lindgren,
vannapurve, vishal.l.verma, yilun.xu, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin
In-Reply-To: <20260315135920.354657-23-chao.gao@intel.com>
On Sun, Mar 15, 2026 at 06:58:42AM -0700, Chao Gao wrote:
> diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
> index e6b7af410c54..4e1ad06506cc 100644
> --- a/arch/x86/virt/vmx/tdx/seamldr.c
> +++ b/arch/x86/virt/vmx/tdx/seamldr.c
> @@ -222,6 +222,11 @@ static void ack_state(void)
> set_target_state(update_data.state + 1);
> }
>
> +static void print_update_failure_message(void)
> +{
> + pr_err_once("update failed, SEAMCALLs will report failure until TDs killed\n");
Is it useful to indicate which step has failed?
>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply
* Re: [PATCH v5 21/22] x86/virt/tdx: Document TDX module update
From: Kiryl Shutsemau @ 2026-03-19 14:05 UTC (permalink / raw)
To: Chao Gao
Cc: linux-kernel, linux-doc, linux-coco, kvm, binbin.wu,
dan.j.williams, dave.hansen, ira.weiny, kai.huang, nik.borisov,
paulmck, pbonzini, reinette.chatre, rick.p.edgecombe, sagis,
seanjc, tony.lindgren, vannapurve, vishal.l.verma, yilun.xu,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86,
H. Peter Anvin, Jonathan Corbet, Shuah Khan
In-Reply-To: <20260315135920.354657-22-chao.gao@intel.com>
On Sun, Mar 15, 2026 at 06:58:41AM -0700, Chao Gao wrote:
> Document TDX module update as a subsection of "TDX Host Kernel Support" to
> provide background information and cover key points that developers and
> users may need to know, for example:
>
> - update is done in stop_machine() context
> - update instructions and results
> - update policy and tooling
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Reviewed-by: Kai Huang <kai.huang@intel.com>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply
* Re: [PATCH v5 20/22] coco/tdx-host: Document TDX module update compatibility criteria
From: Kiryl Shutsemau @ 2026-03-19 14:04 UTC (permalink / raw)
To: Chao Gao
Cc: x86, linux-coco, kvm, linux-kernel, binbin.wu, dan.j.williams,
dave.hansen, ira.weiny, kai.huang, nik.borisov, paulmck, pbonzini,
reinette.chatre, rick.p.edgecombe, sagis, seanjc, tony.lindgren,
vannapurve, vishal.l.verma, yilun.xu
In-Reply-To: <20260315135920.354657-21-chao.gao@intel.com>
On Sun, Mar 15, 2026 at 06:58:40AM -0700, Chao Gao wrote:
> The TDX module update protocol facilitates compatible runtime updates.
>
> Document the compatibility criteria and indicators of various update
> failures, including violations of the compatibility criteria.
>
> Note that runtime TDX module updates are an "update at your own risk"
> operation; userspace must enforce all of the above compatibility
> criteria.
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply
* Re: [PATCH v5 19/22] x86/virt/tdx: Enable TDX module runtime updates
From: Kiryl Shutsemau @ 2026-03-19 14:03 UTC (permalink / raw)
To: Chao Gao
Cc: linux-kernel, linux-coco, kvm, binbin.wu, dan.j.williams,
dave.hansen, ira.weiny, kai.huang, nik.borisov, paulmck, pbonzini,
reinette.chatre, rick.p.edgecombe, sagis, seanjc, tony.lindgren,
vannapurve, vishal.l.verma, yilun.xu, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin
In-Reply-To: <20260315135920.354657-20-chao.gao@intel.com>
On Sun, Mar 15, 2026 at 06:58:39AM -0700, Chao Gao wrote:
> All pieces of TDX module runtime updates are in place. Enable it if it
> is supported.
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Reviewed-by: Xu Yilun <yilun.xu@linux.intel.com>
> Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox