From: Steven Price <steven.price@arm.com>
To: Suzuki K Poulose <suzuki.poulose@arm.com>,
kvm@vger.kernel.org, kvmarm@lists.linux.dev
Cc: Catalin Marinas <catalin.marinas@arm.com>,
Marc Zyngier <maz@kernel.org>, Will Deacon <will@kernel.org>,
James Morse <james.morse@arm.com>,
Oliver Upton <oliver.upton@linux.dev>,
Zenghui Yu <yuzenghui@huawei.com>,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Joey Gouly <joey.gouly@arm.com>,
Alexandru Elisei <alexandru.elisei@arm.com>,
Christoffer Dall <christoffer.dall@arm.com>,
Fuad Tabba <tabba@google.com>,
linux-coco@lists.linux.dev,
Ganapatrao Kulkarni <gankulkarni@os.amperecomputing.com>,
Gavin Shan <gshan@redhat.com>,
Shanker Donthineni <sdonthineni@nvidia.com>,
Alper Gun <alpergun@google.com>,
"Aneesh Kumar K . V" <aneesh.kumar@kernel.org>,
Emi Kisanuki <fj0570is@fujitsu.com>,
Vishal Annapurve <vannapurve@google.com>,
WeiLin.Chang@arm.com, Lorenzo Pieralisi <lpieralisi@kernel.org>
Subject: Re: [PATCH 10/11] KVM: arm64: CCA: Support runtime faulting of memory
Date: Fri, 6 Nov 2026 11:45:40 +0000 [thread overview]
Message-ID: <31ef9077-0d88-401d-b6aa-78b9d9f496bc@arm.com> (raw)
In-Reply-To: <fa0ee5a1-4c49-4c1f-bfbb-c270dc4b6301@arm.com>
On 05/11/2026 17:30, Suzuki K Poulose wrote:
> On 02/11/2026 14:13, 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.
>
> minor nit: "populate" could be confusing here in the CCA context
> Could we just say:
>
> If the realm guest accesses memory which hasn't yet been
> mapped at stage2, KVM needs to fixup the stage2 mapping.
> (The only other option is to inject an Synchronous External Abort
> to back to the Guest if the fault is at an unprotected IPA)
Sure I'll avoid "populate" since it has another meaning in CCA.
>
>>
>> For memory in the lower (protected) region of IPA, verify that the
>> corresponding guest_memfd page is private before delegating to the RMM.
>> The RMM then maps the page into the realm and initializes its contents
>> to zero.
>>
>> For memory in the upper (shared) IPA region, map the backing page as
>> non-secure memory. Check that a guest_memfd page is shared when the
>> fault uses guest_memfd; ordinary userspace-backed memslots follow the
>> normal user memory fault path.
>
> Should we make it explicit that the ordinary userspace-backed memslots
> can only be mapped at the shared ipa ?
Sure
>>
>> If the fault IPA and guest_memfd attributes disagree then exit to
>> userspace with KVM_EXIT_MEMORY_FAULT so the VMM can perform the required
>> conversion.
>
> Or alternately, the host can inject an SEA on a fault at unprotected
> fault.
Indeed - I'll add that.
>>
>> Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> arch/arm64/include/asm/kvm_emulate.h | 13 ++
>> arch/arm64/include/asm/kvm_rmi.h | 7 +-
>> arch/arm64/kvm/mmu.c | 115 +++++++++++---
>> arch/arm64/kvm/rmi.c | 228 ++++++++++++++++++++++++++-
>> 4 files changed, 336 insertions(+), 27 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/
>> include/asm/kvm_emulate.h
>> index 32021bdf2610..8a71fddfdb85 100644
>> --- a/arch/arm64/include/asm/kvm_emulate.h
>> +++ b/arch/arm64/include/asm/kvm_emulate.h
>> @@ -847,4 +847,17 @@ static inline bool kvm_realm_is_created(struct
>> kvm *kvm)
>> return kvm_vm_is_realm(kvm) && kvm_realm_state(kvm) !=
>> REALM_STATE_NONE;
>> }
>> +static inline gpa_t kvm_gpa_from_fault(struct kvm *kvm, phys_addr_t
>> ipa)
>> +{
>> + if (!kvm_vm_is_realm(kvm))
>> + return ipa;
>> +
>> + return ipa & ~BIT(kvm->arch.realm.ia_bits - 1);
>> +}
>> +
>> +static inline gfn_t kvm_gfn_from_fault(struct kvm *kvm, phys_addr_t ipa)
>> +{
>> + return gpa_to_gfn(kvm_gpa_from_fault(kvm, ipa));
>> +}
>> +
>> #endif /* __ARM64_KVM_EMULATE_H__ */
>> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/
>> asm/kvm_rmi.h
>> index 72a9449f00d7..105660d4d56a 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 <linux/arm-smccc-rmi.h>
>> #include <linux/kvm.h>
>> @@ -92,9 +93,13 @@ void kvm_realm_unmap_range_filter(struct kvm *kvm,
>> void kvm_realm_unmap_range(struct kvm *kvm,
>> unsigned long ipa,
>> unsigned long size,
>> - bool unmap_private,
>> bool may_block);
>
> The above function is not need as discussed in the previous patch.
>> +int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,
>> + kvm_pfn_t pfn, unsigned long map_size,
>> + enum kvm_pgtable_prot prot,
>> + struct kvm_mmu_memory_cache *memcache);
>> +
>> static inline bool kvm_realm_is_private_address(struct realm *realm,
>> unsigned long addr)
>> {
>> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
>> index eb8c5f9e9791..20aedbe6ec07 100644
>> --- a/arch/arm64/kvm/mmu.c
>> +++ b/arch/arm64/kvm/mmu.c
>> @@ -434,7 +434,11 @@ static void stage2_flush_memslot(struct kvm *kvm,
>> phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
>> phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
>> - kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
>> + if (kvm_vm_is_realm(kvm))
>> + kvm_realm_unmap_range_filter(kvm, addr, end - addr, true,
>> + KVM_FILTER_SHARED);
>
> Actually, we should simply do nothing for Realm. Here, we are t rying
> unmap the memslot, while we are supposed to make sure the flush the
> dcache to poc for cacheable mappings. Given that RMM mandates FWB for
> stage2, this is NOP for a Realm, which also solves the sashiko report
> on memslot deletion for guest_memfd. Also, another candidate for
> vm_s2_ops ;-)
Probably better is to early out in stage2_flush_vm() such that this
function is never called.
>> + else
>> + kvm_stage2_flush_range(&kvm->arch.mmu, addr, end);
>> }
>> /**
>> @@ -1221,6 +1225,10 @@ void stage2_unmap_vm(struct kvm *kvm)
>> struct kvm_memory_slot *memslot;
>> int idx, bkt;
>> + /* For realms this is handled by the RMM so nothing to do here */
>> + if (kvm_vm_is_realm(kvm))
>> + return;
>> +
>> idx = srcu_read_lock(&kvm->srcu);
>> mmap_read_lock(current->mm);
>> write_lock(&kvm->mmu_lock);
>> @@ -1790,6 +1798,10 @@ static int gmem_abort(const struct
>> kvm_s2_fault_desc *s2fd,
>> void *memcache = NULL;
>> gfn_t canonical_gfn;
>> int ret;
>> + bool is_realm_private = false;
>> +
>> + if (KVM_BUG_ON(!kvm_slot_has_gmem(s2fd->memslot), kvm))
>> + return -EINVAL;
>> if (!perm_fault) {
>> memcache = get_mmu_memcache(s2fd->vcpu);
>> @@ -1804,7 +1816,7 @@ static int gmem_abort(const struct
>> kvm_s2_fault_desc *s2fd,
>> }
>> s2vi.vma_pagesize = PAGE_SIZE;
>> - s2vi.gfn = ALIGN_DOWN(s2fd->fault_ipa, s2vi.vma_pagesize) >>
>> PAGE_SHIFT;
>> + s2vi.gfn = gpa_to_gfn(kvm_gpa_from_fault(kvm, s2fd->fault_ipa));
>> canonical_gfn = get_canonical_gfn(s2fd, &s2vi);
>> write_fault = kvm_s2_fault_is_write(s2fd);
>> @@ -1816,13 +1828,28 @@ static int gmem_abort(const struct
>> kvm_s2_fault_desc *s2fd,
>> /* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */
>> smp_rmb();
>> + if (kvm_vm_is_realm(kvm)) {
>> + is_realm_private = kvm_realm_is_private_address(&kvm-
>> >arch.realm,
>> + s2fd->fault_ipa);
>> +
>> + if (is_realm_private != kvm_gmem_is_private_gfn(kvm,
>> canonical_gfn)) {
>> + kvm_prepare_memory_fault_exit(s2fd->vcpu,
>> + gfn_to_gpa(canonical_gfn),
>> + PAGE_SIZE,
>> + write_fault, exec_fault,
>> + is_realm_private);
>> + kfree(mapping);
>> + return -EFAULT;
>> + }
>> + }
>> +
>
> I am wondering there is a way to make it a bit more generic. The issue
> is, only Realm has private memory with guest_memfd for now. Don't know
> the pKVM plans for it.
>
>
>> ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, canonical_gfn,
>> &s2vi.pfn, NULL);
>> if (ret) {
>> /* If result is non-NULL this is a synthetic fault. */
>> if (!result)
>> kvm_prepare_memory_fault_exit(s2fd->vcpu,
>> gfn_to_gpa(canonical_gfn),
>> s2vi.vma_pagesize, write_fault,
>> exec_fault,
>> - false);
>> + is_realm_private);
>> kfree(mapping);
>> return ret;
>> }
>> @@ -1845,6 +1872,13 @@ static int gmem_abort(const struct
>> kvm_s2_fault_desc *s2fd,
>> goto out_unlock;
>> }
>> + if (kvm_vm_is_realm(kvm)) {
>> + prot &= ~KVM_PGTABLE_PROT_X;
>> + ret = realm_map_ipa(kvm, s2fd->fault_ipa, s2vi.pfn,
>> + PAGE_SIZE, prot, memcache);
>
> I wonder if we need to treat the perm_faults (in the shared IPA)
> differently and explicitly make a helper for that. We do handle
> them in realm_map_unprotected() now, but it is rather a little
> unclear.
>
>> + goto out_unlock;
>> + }
>> +
>> if (perm_fault) {
>> /*
>> * Drop the SW bits in favour of those stored in the
>> @@ -2046,7 +2080,8 @@ static int kvm_s2_fault_get_vma_info(const
>> struct kvm_s2_fault_desc *s2fd,
>> * mapping size to ensure we find the right PFN and lay down the
>> * mapping in the right place.
>> */
>> - s2vi->gfn = ALIGN_DOWN(s2fd->fault_ipa, s2vi->vma_pagesize) >>
>> PAGE_SHIFT;
>> + s2vi->gfn = kvm_gfn_from_fault(kvm, ALIGN_DOWN(s2fd->fault_ipa,
>> + s2vi->vma_pagesize));
>> s2vi->mte_allowed = kvm_vma_mte_allowed(vma);
>> @@ -2241,12 +2276,15 @@ static int kvm_s2_fault_map(const struct
>> kvm_s2_fault_desc *s2fd,
>> if (!perm_fault_granule && !s2vi->map_non_cacheable &&
>> kvm_has_mte(kvm))
>> sanitise_mte_tags(kvm, pfn, mapping_size);
>> - /*
>> - * Under the premise of getting a FSC_PERM fault, we just need to
>> relax
>> - * permissions only if mapping_size equals perm_fault_granule.
>> Otherwise,
>> - * kvm_pgtable_stage2_map() should be called to change block size.
>> - */
>> - if (mapping_size == perm_fault_granule) {
>> + if (kvm_vm_is_realm(kvm)) {
>> + ret = realm_map_ipa(kvm, s2fd->fault_ipa, pfn, mapping_size,
>> + prot, memcache);
>> + } else if (mapping_size == perm_fault_granule) {
>> + /*
>> + * Under the premise of getting a FSC_PERM fault, we just
>> need to relax
>> + * permissions only if mapping_size equals
>> perm_fault_granule. Otherwise,
>> + * kvm_pgtable_stage2_map() should be called to change block
>> size.
>> + */
>> /*
>> * Drop the SW bits in favour of those stored in the
>> * PTE, which will be preserved.
>> @@ -2339,18 +2377,43 @@ static int user_mem_abort(const struct
>> kvm_s2_fault_desc *s2fd,
>> return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache, result);
>> }
>> +static bool private_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa);
>> +static bool kvm_fault_is_gmem_abort(struct kvm *kvm,
>> + const struct kvm_s2_fault_desc *s2fd);
>> +
>
> minor nit: Both those could be dropped by moving the definitons up ?
Ack
>> static int kvm_vm_mem_abort(const struct kvm_s2_fault_desc *s2fd)
>> {
>> VM_WARN_ON_ONCE(kvm_s2_fault_is_perm(s2fd) &&
>> !esr_abt_is_write_fault(s2fd->esr) &&
>> !kvm_s2_fault_is_exec(s2fd));
>> - if (kvm_slot_has_gmem(s2fd->memslot))
>> + if (private_ipa_fault(s2fd->vcpu->kvm, s2fd->fault_ipa) &&
>> + !kvm_slot_has_gmem(s2fd->memslot))
>> + return -EINVAL;
>> +
>> + if (kvm_fault_is_gmem_abort(s2fd->vcpu->kvm, s2fd))
>> return gmem_abort(s2fd, NULL);
>> else
>> return user_mem_abort(s2fd, NULL);
>> }
>> +/* Decide if an abort should be handled by the gmem */
>> +static bool kvm_fault_is_gmem_abort(struct kvm *kvm,
>> + const struct kvm_s2_fault_desc *s2fd)
>> +{
>> + if (!kvm_slot_has_gmem(s2fd->memslot))
>> + return false;
>> + if (kvm_memslot_is_gmem_only(s2fd->memslot))
>> + return true;
>> + /*
>> + * For Realms, all private faults must be backed by GMEM.
>> + * TODO: Handle Trusted device private memory mappings.
>> + */
>> + if (private_ipa_fault(kvm, s2fd->fault_ipa))
>> + return true;
>> + return false;
>> +}
>> +
>> /* Resolve the access fault by making the page young again. */
>> static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t
>> fault_ipa)
>> {
>> @@ -2444,6 +2507,12 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
>> return 0;
>> }
>> +static bool private_ipa_fault(struct kvm *kvm, phys_addr_t fault_ipa)
>> +{
>> + return kvm_vm_is_realm(kvm) &&
>> + kvm_realm_is_private_address(&kvm->arch.realm, fault_ipa);
>> +}
>> +
>> /**
>> * kvm_handle_guest_abort - handles all 2nd stage aborts
>> * @vcpu: the VCPU pointer
>> @@ -2457,10 +2526,10 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu)
>> */
>> int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>> {
>> + struct kvm *kvm = vcpu->kvm;
>> struct kvm_s2_trans nested_trans, *nested = NULL;
>> unsigned long esr = kvm_vcpu_get_esr(vcpu);
>> struct kvm_s2_mmu *mmu = vcpu->arch.hw_mmu;
>> - struct kvm *kvm = vcpu->kvm;
>> phys_addr_t fault_ipa; /* The address we faulted on */
>> phys_addr_t ipa; /* Always the IPA in the L1 guest phys space */
>> struct kvm_memory_slot *memslot;
>> @@ -2553,8 +2622,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>> nested = &nested_trans;
>> }
>> - gfn = ipa >> PAGE_SHIFT;
>> + gfn = kvm_gfn_from_fault(kvm, ipa);
>> memslot = gfn_to_memslot(kvm, gfn);
>> +
>> hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
>> write_fault = esr_abt_is_write_fault(esr);
>> if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
>> @@ -2597,7 +2667,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
>> * of the page size.
>> */
>> ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(vcpu));
>> - ret = io_mem_abort(vcpu, ipa);
>> + ret = io_mem_abort(vcpu, kvm_gpa_from_fault(kvm, ipa));
>> goto out_unlock;
>> }
>> @@ -2856,6 +2926,7 @@ int kvm_arch_prepare_memory_region(struct kvm
>> *kvm,
>> enum kvm_mr_change change)
>> {
>> hva_t hva, reg_end;
>> + gfn_t ipa_limit;
>> int ret = 0;
>> if (kvm_vm_is_protected(kvm) && new &&
>> @@ -2891,17 +2962,21 @@ int kvm_arch_prepare_memory_region(struct kvm
>> *kvm,
>> return 0;
>> /*
>> - * Prevent userspace from creating a memory region outside of the
>> IPA
>> - * space addressable by the KVM guest IPA space.
>> + * Realm shared IPAs alias the corresponding private IPAs, so
>> memslots
>> + * must reside entirely in the canonical lower half of the IPA
>> space.
>> */
>> - if ((new->base_gfn + new->npages) > (kvm_phys_size(&kvm-
>> >arch.mmu) >> PAGE_SHIFT))
>> + ipa_limit = kvm_phys_size(&kvm->arch.mmu) >> PAGE_SHIFT;
>> + if (kvm_vm_is_realm(kvm))
>> + ipa_limit >>= 1;
>> + if (new->base_gfn + new->npages > ipa_limit)
>> return -EFAULT;
>
> Please could we move this change to the patch where we deal with
> memslots for Realms ?>
Will do
>> /*
>> - * Only support guest_memfd backed memslots with mappable memory,
>> since
>> - * there aren't any CoCo VMs that support only private memory on
>> arm64.
>> + * Only support guest_memfd backed memslots with mappable memory,
>> + * unless the guest is a CCA realm guest.
>> */
>> - if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new))
>> + if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new) &&
>> + !kvm_vm_is_realm(kvm))
>> return -EINVAL;
>
> This looks like a left over from the original support without in-place
> conversion support. This hunk should be dropped ?
Good point - yes we should be requiring the VMM specifies the MMAP flag.
>> /* guest_memfd is incompatible with MTE. */
>> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
>> index 1066f767f85d..f89460d8adcb 100644
>> --- a/arch/arm64/kvm/rmi.c
>> +++ b/arch/arm64/kvm/rmi.c
>> @@ -531,15 +531,231 @@ void kvm_realm_unmap_range_filter(struct kvm
>> *kvm, unsigned long start,
>> }
>> void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start,
>> - unsigned long size, bool unmap_private,
>> - bool may_block)
>> + unsigned long size, bool may_block)
>> {
>> - enum kvm_gfn_range_filter attr_filter = KVM_FILTER_SHARED;
>> + kvm_realm_unmap_range_filter(kvm, start, size, may_block,
>> + KVM_FILTER_SHARED | KVM_FILTER_PRIVATE);
>> +}
>
>
>
>> +
>> +static unsigned long addr_range_desc(unsigned long phys, unsigned
>> long size,
>> + unsigned long state)
>> +{
>> + unsigned long count = 1;
>> + unsigned long out;
>> + int rmi_size;
>> +
>> + for (rmi_size = KVM_PGTABLE_LAST_LEVEL; rmi_size >= 0; rmi_size--) {
>> + unsigned long entry_size = rmi_range_entry_size(rmi_size);
>> +
>> + if (size == entry_size)
>> + break;
>> + if (size > entry_size) {
>> + rmi_size = -1;
>> + break;
>> + }
>> + }
>> +
>> + if (rmi_size < 0) {
>> + /*
>> + * Only support mapping at the page level granularity when
>> + * it's an unusual length. This should get us back onto a larger
>> + * block size for the subsequent mappings.
>
> Is the second part really valid ?
Well the idea here is that after a number of PAGE_SIZEd mappings we
should be sufficiently aligned that we can move back to the larger block
size.
I've reworked this function locally and hopefully made things clearer.
>> + */
>> + rmi_size = 0;
>> + count = MIN(size >> PAGE_SHIFT, PTRS_PER_PTE - 1);
>> + }
>> +
>> + WARN_ON(phys & ~PAGE_MASK);
>> +
>> + out = FIELD_PREP(RMI_ADDR_RANGE_BLOCK_SIZE_MASK, rmi_size) |
>> + FIELD_PREP(RMI_ADDR_RANGE_COUNT_MASK, count) |
>> + FIELD_PREP(RMI_ADDR_RANGE_STATE_MASK, state);
>> + out |= phys & PAGE_MASK;
>> +
>> + return out;
>> +}
>> +
>> +static int realm_map_protected(struct kvm *kvm,
>> + unsigned long ipa,
>> + kvm_pfn_t pfn,
>> + unsigned long map_size,
>> + struct kvm_mmu_memory_cache *memcache)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> + phys_addr_t phys = __pfn_to_phys(pfn);
>> + phys_addr_t base_phys = phys;
>> + phys_addr_t rd = virt_to_phys(realm->rd);
>> + phys_addr_t delegated_phys;
>> + unsigned long base_ipa = ipa;
>> + unsigned long ipa_top;
>> + long ret = 0;
>> +
>> + if (WARN_ON(!IS_ALIGNED(map_size, PAGE_SIZE) ||
>> + !IS_ALIGNED(ipa, map_size)))
>> + return -EINVAL;
>> +
>> + if (rmi_delegate_range(phys, map_size, &delegated_phys)) {
>> + if (delegated_phys == phys) {
>> + /*
>> + * It's likely we raced with another VCPU on the same
>> + * fault. Assume the other VCPU has handled the fault
>> + * and return to the guest.
>> + */
>> + return 0;
>> + }
>> + /* Partial delegation - map as much as we can */
>> + map_size = delegated_phys - phys;
>> + }
>> +
>> + ipa_top = ipa + map_size;
>> +
>> + while (ipa < ipa_top) {
>> + unsigned long flags = RMI_ADDR_TYPE_SINGLE;
>> + unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa,
>> + RMI_OP_MEM_DELEGATED);
>> + unsigned long out_top;
>> +
>> + ret = rmi_rtt_data_map(rd, ipa, ipa_top, flags, range_desc,
>> + &out_top);
>> + if (ret < 0)
>> + goto err_undelegate;
>> +
>> + if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {
>> + /* Create missing RTTs and retry */
>> + int level = RMI_RESULT_DATA_LEVEL(ret);
>> +
>> + if (WARN_ON(level >= KVM_PGTABLE_LAST_LEVEL))
>> + goto err_undelegate;
>> + ret = realm_create_rtt_levels(realm, ipa, level,
>> + level + 1,
>> + memcache);
>> + if (ret)
>> + goto err_undelegate;
>> - if (unmap_private)
>> - attr_filter |= KVM_FILTER_PRIVATE;
>> + continue;
>> + }
>> +
>> + if (WARN_ON(ret))
>> + goto err_undelegate;
>> +
>> + phys += out_top - ipa;
>> + ipa = out_top;
>> + }
>> +
>> + return 0;
>> +
>> +err_undelegate:
>> + realm_unmap_private_range(kvm, base_ipa, ipa, true);
>> + /* A range which cannot be undelegated remains owned by the RMM. */
>> + WARN_ON(rmi_undelegate_range(base_phys, map_size));
>> + return ret < 0 ? ret : -ENXIO;
>> +}
>> +
>> +static int realm_map_non_secure(struct kvm *kvm,
>
> realm_map_unprotected() to pair with realm_map_protected() ?
Yes that's probably better - "secure" is always a problematic term ;)
>> + unsigned long ipa,
>> + kvm_pfn_t pfn,
>> + unsigned long size,
>> + enum kvm_pgtable_prot prot,
>> + struct kvm_mmu_memory_cache *memcache)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> + unsigned long attr, flags = 0;
>> + phys_addr_t rd = virt_to_phys(realm->rd);
>> + phys_addr_t phys = __pfn_to_phys(pfn);
>> + unsigned long ipa_top = ipa + size;
>> + long ret;
>> +
>> + if (WARN_ON(!IS_ALIGNED(size, PAGE_SIZE)))
>> + return -EINVAL;
>> +
>> + switch (prot & (KVM_PGTABLE_PROT_DEVICE |
>> KVM_PGTABLE_PROT_NORMAL_NC)) {
>> + case KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC:
>> + return -EINVAL;
>> + case KVM_PGTABLE_PROT_DEVICE:
>> + attr = MT_S2_FWB_DEVICE_nGnRE;
>> + break;
>> + case KVM_PGTABLE_PROT_NORMAL_NC:
>> + attr = MT_S2_FWB_NORMAL_NC;
>> + break;
>> + default:
>> + attr = MT_S2_FWB_NORMAL;
>> + }
>> +
>> + flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_MEMATTR_MASK, attr);
>> +
>> + if (prot & KVM_PGTABLE_PROT_R)
>> + flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK,
>> RMI_S2AP_DIRECT_READ);
>> + if (prot & KVM_PGTABLE_PROT_W)
>> + flags |= FIELD_PREP(RMI_RTT_UNPROT_MAP_FLAGS_S2AP_MASK,
>> RMI_S2AP_DIRECT_WRITE);
>> +
>> + flags |= RMI_ADDR_TYPE_SINGLE;
>
> May be add a TODO: comment, explaining we plan to add multi-list mapping
> later ?
Ack.
>> +
>> + while (ipa < ipa_top) {
>> + unsigned long range_desc = addr_range_desc(phys, ipa_top - ipa,
>> + RMI_OP_MEM_UNDELEGATED);
>> + unsigned long out_top;
>> +
>> + ret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags, range_desc,
>> + &out_top);
>
> Do we get an RMI_ERROR_INPUT, if the range_desc doesn't cover the
> ipa_top, if we truncated the range_desc in addr_range_desc() due
> to alignment ?
It doesn't appear to be listed as a failure condition in the spec from
what I can see. I have to admit the spec isn't entirely clear on this
though.
I'll update the code to cap the top value in this case.
Thanks,
Steve
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {
>> + /* Create missing RTTs and retry */
>> + int level = RMI_RESULT_DATA_LEVEL(ret);
>> + int req_level = find_map_level(realm, ipa, ipa_top);
>> +
>> + /*
>> + * There already exists a mapping at the level. May be
>> + * we are relaxing a permission for the given range ?
>> + */
>> + if (level >= req_level) {
>> + realm_unmap_shared_range(kvm, ipa, ipa_top, true);
>> + continue;
>> + }
>> +
>> + ret = realm_create_rtt_levels(realm, ipa, level,
>> + req_level,
>> + memcache);
>> + if (ret)
>> + return ret;
>> +
>> + ret = rmi_rtt_unprot_map(rd, ipa, ipa_top, flags,
>> + range_desc, &out_top);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + if (ret && RMI_RESULT_STATUS(ret) != RMI_ERROR_RTT)
>> + return ret;
>> +
>> + phys += out_top - ipa;
>> + ipa = out_top;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int realm_map_ipa(struct kvm *kvm, phys_addr_t ipa,
>> + kvm_pfn_t pfn, unsigned long map_size,
>> + enum kvm_pgtable_prot prot,
>> + struct kvm_mmu_memory_cache *memcache)
>> +{
>> + struct realm *realm = &kvm->arch.realm;
>> +
>> + if (kvm_realm_state(kvm) != REALM_STATE_ACTIVE)
>> + return -EBUSY;
>> +
>> + ipa = ALIGN_DOWN(ipa, map_size);
>> + if (!kvm_realm_is_private_address(realm, ipa)) {
>> + return realm_map_non_secure(kvm, ipa, pfn, map_size, prot,
>> + memcache);
>> + }
>> - kvm_realm_unmap_range_filter(kvm, start, size, may_block,
>> attr_filter);
>> + /* It's impossible to map protected pages read-only. */
>> + if (WARN_ON(!(prot & KVM_PGTABLE_PROT_W)))
>> + return -EFAULT;
>> + return realm_map_protected(kvm, ipa, pfn, map_size, memcache);
>> }
>> void kvm_destroy_realm(struct kvm *kvm)
>
next prev parent reply other threads:[~2026-11-06 11:45 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 14:12 [PATCH 00/11] KVM: arm64: CCA: Prepare Realm memory management Steven Price
2026-11-02 14:12 ` [PATCH 01/11] KVM: arm64: CCA: Reserve the RMM mode Steven Price
2026-11-02 14:12 ` [PATCH 02/11] KVM: arm64: CCA: Introduce Realms Steven Price
2026-11-03 11:34 ` Suzuki K Poulose
2026-11-05 15:12 ` Steven Price
2026-11-02 14:12 ` [PATCH 03/11] KVM: arm64: CCA: Check for RMI support at KVM init Steven Price
2026-11-04 10:23 ` Kohei Enju
2026-11-04 10:27 ` Kohei Enju
2026-11-04 10:39 ` Suzuki K Poulose
2026-11-04 10:47 ` Kohei Enju
2026-11-02 14:12 ` [PATCH 04/11] KVM: arm64: CCA: Check for LPA2 support and SHA-256 Steven Price
2026-11-03 11:47 ` Suzuki K Poulose
2026-11-05 15:12 ` Steven Price
2026-11-02 14:12 ` [PATCH 05/11] KVM: arm64: Handle attributes in stage2_unmap_range Steven Price
2026-11-02 14:12 ` [PATCH 06/11] KVM: arm64: CCA: Add basic infrastructure for creating a realm Steven Price
2026-11-02 23:49 ` Suzuki K Poulose
2026-11-05 15:48 ` Steven Price
2026-11-02 14:12 ` [PATCH 07/11] KVM: arm64: CCA: Tear down RTTs Steven Price
2026-11-03 15:20 ` Suzuki K Poulose
2026-11-05 16:22 ` Steven Price
2026-11-02 14:12 ` [PATCH 08/11] KVM: arm64: CCA: Add Realm RTT mapping and invalidation helpers Steven Price
2026-11-04 18:48 ` Suzuki K Poulose
2026-11-04 18:59 ` Suzuki K Poulose
2026-11-05 16:49 ` Steven Price
2026-11-02 14:13 ` [PATCH 09/11] KVM: arm64: CCA: Prevent memslot modifications after Realm creation Steven Price
2026-11-02 14:13 ` [PATCH 10/11] KVM: arm64: CCA: Support runtime faulting of memory Steven Price
2026-11-05 17:30 ` Suzuki K Poulose
2026-11-06 11:45 ` Steven Price [this message]
2026-11-02 14:13 ` [PATCH 11/11] KVM: arm64: CCA: Add Realm stage-2 MMU operations Steven Price
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=31ef9077-0d88-401d-b6aa-78b9d9f496bc@arm.com \
--to=steven.price@arm.com \
--cc=WeiLin.Chang@arm.com \
--cc=alexandru.elisei@arm.com \
--cc=alpergun@google.com \
--cc=aneesh.kumar@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=christoffer.dall@arm.com \
--cc=fj0570is@fujitsu.com \
--cc=gankulkarni@os.amperecomputing.com \
--cc=gshan@redhat.com \
--cc=james.morse@arm.com \
--cc=joey.gouly@arm.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=maz@kernel.org \
--cc=oliver.upton@linux.dev \
--cc=sdonthineni@nvidia.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=vannapurve@google.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®